insert.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { entityKind, is } from "../../entity.js";
  2. import { QueryPromise } from "../../query-promise.js";
  3. import { Param, SQL, sql } from "../../sql/sql.js";
  4. import { SQLiteTable } from "../table.js";
  5. import { Columns, Table } from "../../table.js";
  6. import { haveSameKeys, mapUpdateSet, orderSelectedFields } from "../../utils.js";
  7. import { extractUsedTable } from "../utils.js";
  8. import { QueryBuilder } from "./query-builder.js";
  9. class SQLiteInsertBuilder {
  10. constructor(table, session, dialect, withList) {
  11. this.table = table;
  12. this.session = session;
  13. this.dialect = dialect;
  14. this.withList = withList;
  15. }
  16. static [entityKind] = "SQLiteInsertBuilder";
  17. values(values) {
  18. values = Array.isArray(values) ? values : [values];
  19. if (values.length === 0) {
  20. throw new Error("values() must be called with at least one value");
  21. }
  22. const mappedValues = values.map((entry) => {
  23. const result = {};
  24. const cols = this.table[Table.Symbol.Columns];
  25. for (const colKey of Object.keys(entry)) {
  26. const colValue = entry[colKey];
  27. result[colKey] = is(colValue, SQL) ? colValue : new Param(colValue, cols[colKey]);
  28. }
  29. return result;
  30. });
  31. return new SQLiteInsertBase(this.table, mappedValues, this.session, this.dialect, this.withList);
  32. }
  33. select(selectQuery) {
  34. const select = typeof selectQuery === "function" ? selectQuery(new QueryBuilder()) : selectQuery;
  35. if (!is(select, SQL) && !haveSameKeys(this.table[Columns], select._.selectedFields)) {
  36. throw new Error(
  37. "Insert select error: selected fields are not the same or are in a different order compared to the table definition"
  38. );
  39. }
  40. return new SQLiteInsertBase(this.table, select, this.session, this.dialect, this.withList, true);
  41. }
  42. }
  43. class SQLiteInsertBase extends QueryPromise {
  44. constructor(table, values, session, dialect, withList, select) {
  45. super();
  46. this.session = session;
  47. this.dialect = dialect;
  48. this.config = { table, values, withList, select };
  49. }
  50. static [entityKind] = "SQLiteInsert";
  51. /** @internal */
  52. config;
  53. returning(fields = this.config.table[SQLiteTable.Symbol.Columns]) {
  54. this.config.returning = orderSelectedFields(fields);
  55. return this;
  56. }
  57. /**
  58. * Adds an `on conflict do nothing` clause to the query.
  59. *
  60. * Calling this method simply avoids inserting a row as its alternative action.
  61. *
  62. * See docs: {@link https://orm.drizzle.team/docs/insert#on-conflict-do-nothing}
  63. *
  64. * @param config The `target` and `where` clauses.
  65. *
  66. * @example
  67. * ```ts
  68. * // Insert one row and cancel the insert if there's a conflict
  69. * await db.insert(cars)
  70. * .values({ id: 1, brand: 'BMW' })
  71. * .onConflictDoNothing();
  72. *
  73. * // Explicitly specify conflict target
  74. * await db.insert(cars)
  75. * .values({ id: 1, brand: 'BMW' })
  76. * .onConflictDoNothing({ target: cars.id });
  77. * ```
  78. */
  79. onConflictDoNothing(config = {}) {
  80. if (!this.config.onConflict) this.config.onConflict = [];
  81. if (config.target === void 0) {
  82. this.config.onConflict.push(sql` on conflict do nothing`);
  83. } else {
  84. const targetSql = Array.isArray(config.target) ? sql`${config.target}` : sql`${[config.target]}`;
  85. const whereSql = config.where ? sql` where ${config.where}` : sql``;
  86. this.config.onConflict.push(sql` on conflict ${targetSql} do nothing${whereSql}`);
  87. }
  88. return this;
  89. }
  90. /**
  91. * Adds an `on conflict do update` clause to the query.
  92. *
  93. * Calling this method will update the existing row that conflicts with the row proposed for insertion as its alternative action.
  94. *
  95. * See docs: {@link https://orm.drizzle.team/docs/insert#upserts-and-conflicts}
  96. *
  97. * @param config The `target`, `set` and `where` clauses.
  98. *
  99. * @example
  100. * ```ts
  101. * // Update the row if there's a conflict
  102. * await db.insert(cars)
  103. * .values({ id: 1, brand: 'BMW' })
  104. * .onConflictDoUpdate({
  105. * target: cars.id,
  106. * set: { brand: 'Porsche' }
  107. * });
  108. *
  109. * // Upsert with 'where' clause
  110. * await db.insert(cars)
  111. * .values({ id: 1, brand: 'BMW' })
  112. * .onConflictDoUpdate({
  113. * target: cars.id,
  114. * set: { brand: 'newBMW' },
  115. * where: sql`${cars.createdAt} > '2023-01-01'::date`,
  116. * });
  117. * ```
  118. */
  119. onConflictDoUpdate(config) {
  120. if (config.where && (config.targetWhere || config.setWhere)) {
  121. throw new Error(
  122. 'You cannot use both "where" and "targetWhere"/"setWhere" at the same time - "where" is deprecated, use "targetWhere" or "setWhere" instead.'
  123. );
  124. }
  125. if (!this.config.onConflict) this.config.onConflict = [];
  126. const whereSql = config.where ? sql` where ${config.where}` : void 0;
  127. const targetWhereSql = config.targetWhere ? sql` where ${config.targetWhere}` : void 0;
  128. const setWhereSql = config.setWhere ? sql` where ${config.setWhere}` : void 0;
  129. const targetSql = Array.isArray(config.target) ? sql`${config.target}` : sql`${[config.target]}`;
  130. const setSql = this.dialect.buildUpdateSet(this.config.table, mapUpdateSet(this.config.table, config.set));
  131. this.config.onConflict.push(
  132. sql` on conflict ${targetSql}${targetWhereSql} do update set ${setSql}${whereSql}${setWhereSql}`
  133. );
  134. return this;
  135. }
  136. /** @internal */
  137. getSQL() {
  138. return this.dialect.buildInsertQuery(this.config);
  139. }
  140. toSQL() {
  141. const { typings: _typings, ...rest } = this.dialect.sqlToQuery(this.getSQL());
  142. return rest;
  143. }
  144. /** @internal */
  145. _prepare(isOneTimeQuery = true) {
  146. return this.session[isOneTimeQuery ? "prepareOneTimeQuery" : "prepareQuery"](
  147. this.dialect.sqlToQuery(this.getSQL()),
  148. this.config.returning,
  149. this.config.returning ? "all" : "run",
  150. true,
  151. void 0,
  152. {
  153. type: "insert",
  154. tables: extractUsedTable(this.config.table)
  155. }
  156. );
  157. }
  158. prepare() {
  159. return this._prepare(false);
  160. }
  161. run = (placeholderValues) => {
  162. return this._prepare().run(placeholderValues);
  163. };
  164. all = (placeholderValues) => {
  165. return this._prepare().all(placeholderValues);
  166. };
  167. get = (placeholderValues) => {
  168. return this._prepare().get(placeholderValues);
  169. };
  170. values = (placeholderValues) => {
  171. return this._prepare().values(placeholderValues);
  172. };
  173. async execute() {
  174. return this.config.returning ? this.all() : this.run();
  175. }
  176. $dynamic() {
  177. return this;
  178. }
  179. }
  180. export {
  181. SQLiteInsertBase,
  182. SQLiteInsertBuilder
  183. };
  184. //# sourceMappingURL=insert.js.map