db.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { entityKind } from "../entity.js";
  2. import { SelectionProxyHandler } from "../selection-proxy.js";
  3. import { sql } from "../sql/sql.js";
  4. import { WithSubquery } from "../subquery.js";
  5. import { MySqlCountBuilder } from "./query-builders/count.js";
  6. import {
  7. MySqlDeleteBase,
  8. MySqlInsertBuilder,
  9. MySqlSelectBuilder,
  10. MySqlUpdateBuilder,
  11. QueryBuilder
  12. } from "./query-builders/index.js";
  13. import { RelationalQueryBuilder } from "./query-builders/query.js";
  14. class MySqlDatabase {
  15. constructor(dialect, session, schema, mode) {
  16. this.dialect = dialect;
  17. this.session = session;
  18. this.mode = mode;
  19. this._ = schema ? {
  20. schema: schema.schema,
  21. fullSchema: schema.fullSchema,
  22. tableNamesMap: schema.tableNamesMap
  23. } : {
  24. schema: void 0,
  25. fullSchema: {},
  26. tableNamesMap: {}
  27. };
  28. this.query = {};
  29. if (this._.schema) {
  30. for (const [tableName, columns] of Object.entries(this._.schema)) {
  31. this.query[tableName] = new RelationalQueryBuilder(
  32. schema.fullSchema,
  33. this._.schema,
  34. this._.tableNamesMap,
  35. schema.fullSchema[tableName],
  36. columns,
  37. dialect,
  38. session,
  39. this.mode
  40. );
  41. }
  42. }
  43. this.$cache = { invalidate: async (_params) => {
  44. } };
  45. }
  46. static [entityKind] = "MySqlDatabase";
  47. query;
  48. /**
  49. * Creates a subquery that defines a temporary named result set as a CTE.
  50. *
  51. * It is useful for breaking down complex queries into simpler parts and for reusing the result set in subsequent parts of the query.
  52. *
  53. * See docs: {@link https://orm.drizzle.team/docs/select#with-clause}
  54. *
  55. * @param alias The alias for the subquery.
  56. *
  57. * Failure to provide an alias will result in a DrizzleTypeError, preventing the subquery from being referenced in other queries.
  58. *
  59. * @example
  60. *
  61. * ```ts
  62. * // Create a subquery with alias 'sq' and use it in the select query
  63. * const sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
  64. *
  65. * const result = await db.with(sq).select().from(sq);
  66. * ```
  67. *
  68. * To select arbitrary SQL values as fields in a CTE and reference them in other CTEs or in the main query, you need to add aliases to them:
  69. *
  70. * ```ts
  71. * // Select an arbitrary SQL value as a field in a CTE and reference it in the main query
  72. * const sq = db.$with('sq').as(db.select({
  73. * name: sql<string>`upper(${users.name})`.as('name'),
  74. * })
  75. * .from(users));
  76. *
  77. * const result = await db.with(sq).select({ name: sq.name }).from(sq);
  78. * ```
  79. */
  80. $with = (alias, selection) => {
  81. const self = this;
  82. const as = (qb) => {
  83. if (typeof qb === "function") {
  84. qb = qb(new QueryBuilder(self.dialect));
  85. }
  86. return new Proxy(
  87. new WithSubquery(
  88. qb.getSQL(),
  89. selection ?? ("getSelectedFields" in qb ? qb.getSelectedFields() ?? {} : {}),
  90. alias,
  91. true
  92. ),
  93. new SelectionProxyHandler({ alias, sqlAliasedBehavior: "alias", sqlBehavior: "error" })
  94. );
  95. };
  96. return { as };
  97. };
  98. $count(source, filters) {
  99. return new MySqlCountBuilder({ source, filters, session: this.session });
  100. }
  101. $cache;
  102. /**
  103. * Incorporates a previously defined CTE (using `$with`) into the main query.
  104. *
  105. * This method allows the main query to reference a temporary named result set.
  106. *
  107. * See docs: {@link https://orm.drizzle.team/docs/select#with-clause}
  108. *
  109. * @param queries The CTEs to incorporate into the main query.
  110. *
  111. * @example
  112. *
  113. * ```ts
  114. * // Define a subquery 'sq' as a CTE using $with
  115. * const sq = db.$with('sq').as(db.select().from(users).where(eq(users.id, 42)));
  116. *
  117. * // Incorporate the CTE 'sq' into the main query and select from it
  118. * const result = await db.with(sq).select().from(sq);
  119. * ```
  120. */
  121. with(...queries) {
  122. const self = this;
  123. function select(fields) {
  124. return new MySqlSelectBuilder({
  125. fields: fields ?? void 0,
  126. session: self.session,
  127. dialect: self.dialect,
  128. withList: queries
  129. });
  130. }
  131. function selectDistinct(fields) {
  132. return new MySqlSelectBuilder({
  133. fields: fields ?? void 0,
  134. session: self.session,
  135. dialect: self.dialect,
  136. withList: queries,
  137. distinct: true
  138. });
  139. }
  140. function update(table) {
  141. return new MySqlUpdateBuilder(table, self.session, self.dialect, queries);
  142. }
  143. function delete_(table) {
  144. return new MySqlDeleteBase(table, self.session, self.dialect, queries);
  145. }
  146. return { select, selectDistinct, update, delete: delete_ };
  147. }
  148. select(fields) {
  149. return new MySqlSelectBuilder({ fields: fields ?? void 0, session: this.session, dialect: this.dialect });
  150. }
  151. selectDistinct(fields) {
  152. return new MySqlSelectBuilder({
  153. fields: fields ?? void 0,
  154. session: this.session,
  155. dialect: this.dialect,
  156. distinct: true
  157. });
  158. }
  159. /**
  160. * Creates an update query.
  161. *
  162. * Calling this method without `.where()` clause will update all rows in a table. The `.where()` clause specifies which rows should be updated.
  163. *
  164. * Use `.set()` method to specify which values to update.
  165. *
  166. * See docs: {@link https://orm.drizzle.team/docs/update}
  167. *
  168. * @param table The table to update.
  169. *
  170. * @example
  171. *
  172. * ```ts
  173. * // Update all rows in the 'cars' table
  174. * await db.update(cars).set({ color: 'red' });
  175. *
  176. * // Update rows with filters and conditions
  177. * await db.update(cars).set({ color: 'red' }).where(eq(cars.brand, 'BMW'));
  178. * ```
  179. */
  180. update(table) {
  181. return new MySqlUpdateBuilder(table, this.session, this.dialect);
  182. }
  183. /**
  184. * Creates an insert query.
  185. *
  186. * Calling this method will create new rows in a table. Use `.values()` method to specify which values to insert.
  187. *
  188. * See docs: {@link https://orm.drizzle.team/docs/insert}
  189. *
  190. * @param table The table to insert into.
  191. *
  192. * @example
  193. *
  194. * ```ts
  195. * // Insert one row
  196. * await db.insert(cars).values({ brand: 'BMW' });
  197. *
  198. * // Insert multiple rows
  199. * await db.insert(cars).values([{ brand: 'BMW' }, { brand: 'Porsche' }]);
  200. * ```
  201. */
  202. insert(table) {
  203. return new MySqlInsertBuilder(table, this.session, this.dialect);
  204. }
  205. /**
  206. * Creates a delete query.
  207. *
  208. * Calling this method without `.where()` clause will delete all rows in a table. The `.where()` clause specifies which rows should be deleted.
  209. *
  210. * See docs: {@link https://orm.drizzle.team/docs/delete}
  211. *
  212. * @param table The table to delete from.
  213. *
  214. * @example
  215. *
  216. * ```ts
  217. * // Delete all rows in the 'cars' table
  218. * await db.delete(cars);
  219. *
  220. * // Delete rows with filters and conditions
  221. * await db.delete(cars).where(eq(cars.color, 'green'));
  222. * ```
  223. */
  224. delete(table) {
  225. return new MySqlDeleteBase(table, this.session, this.dialect);
  226. }
  227. execute(query) {
  228. return this.session.execute(typeof query === "string" ? sql.raw(query) : query.getSQL());
  229. }
  230. transaction(transaction, config) {
  231. return this.session.transaction(transaction, config);
  232. }
  233. }
  234. const withReplicas = (primary, replicas, getReplica = () => replicas[Math.floor(Math.random() * replicas.length)]) => {
  235. const select = (...args) => getReplica(replicas).select(...args);
  236. const selectDistinct = (...args) => getReplica(replicas).selectDistinct(...args);
  237. const $count = (...args) => getReplica(replicas).$count(...args);
  238. const $with = (...args) => getReplica(replicas).with(...args);
  239. const update = (...args) => primary.update(...args);
  240. const insert = (...args) => primary.insert(...args);
  241. const $delete = (...args) => primary.delete(...args);
  242. const execute = (...args) => primary.execute(...args);
  243. const transaction = (...args) => primary.transaction(...args);
  244. return {
  245. ...primary,
  246. update,
  247. insert,
  248. delete: $delete,
  249. execute,
  250. transaction,
  251. $primary: primary,
  252. $replicas: replicas,
  253. select,
  254. selectDistinct,
  255. $count,
  256. with: $with,
  257. get query() {
  258. return getReplica(replicas).query;
  259. }
  260. };
  261. };
  262. export {
  263. MySqlDatabase,
  264. withReplicas
  265. };
  266. //# sourceMappingURL=db.js.map