select.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. import { entityKind, is } from "../../entity.js";
  2. import { MySqlTable } from "../table.js";
  3. import { TypedQueryBuilder } from "../../query-builders/query-builder.js";
  4. import { QueryPromise } from "../../query-promise.js";
  5. import { SelectionProxyHandler } from "../../selection-proxy.js";
  6. import { SQL, View } from "../../sql/sql.js";
  7. import { Subquery } from "../../subquery.js";
  8. import { Table } from "../../table.js";
  9. import { applyMixins, getTableColumns, getTableLikeName, haveSameKeys, orderSelectedFields } from "../../utils.js";
  10. import { ViewBaseConfig } from "../../view-common.js";
  11. import { convertIndexToString, extractUsedTable, toArray } from "../utils.js";
  12. import { MySqlViewBase } from "../view-base.js";
  13. class MySqlSelectBuilder {
  14. static [entityKind] = "MySqlSelectBuilder";
  15. fields;
  16. session;
  17. dialect;
  18. withList = [];
  19. distinct;
  20. constructor(config) {
  21. this.fields = config.fields;
  22. this.session = config.session;
  23. this.dialect = config.dialect;
  24. if (config.withList) {
  25. this.withList = config.withList;
  26. }
  27. this.distinct = config.distinct;
  28. }
  29. from(source, onIndex) {
  30. const isPartialSelect = !!this.fields;
  31. let fields;
  32. if (this.fields) {
  33. fields = this.fields;
  34. } else if (is(source, Subquery)) {
  35. fields = Object.fromEntries(
  36. Object.keys(source._.selectedFields).map((key) => [key, source[key]])
  37. );
  38. } else if (is(source, MySqlViewBase)) {
  39. fields = source[ViewBaseConfig].selectedFields;
  40. } else if (is(source, SQL)) {
  41. fields = {};
  42. } else {
  43. fields = getTableColumns(source);
  44. }
  45. let useIndex = [];
  46. let forceIndex = [];
  47. let ignoreIndex = [];
  48. if (is(source, MySqlTable) && onIndex && typeof onIndex !== "string") {
  49. if (onIndex.useIndex) {
  50. useIndex = convertIndexToString(toArray(onIndex.useIndex));
  51. }
  52. if (onIndex.forceIndex) {
  53. forceIndex = convertIndexToString(toArray(onIndex.forceIndex));
  54. }
  55. if (onIndex.ignoreIndex) {
  56. ignoreIndex = convertIndexToString(toArray(onIndex.ignoreIndex));
  57. }
  58. }
  59. return new MySqlSelectBase(
  60. {
  61. table: source,
  62. fields,
  63. isPartialSelect,
  64. session: this.session,
  65. dialect: this.dialect,
  66. withList: this.withList,
  67. distinct: this.distinct,
  68. useIndex,
  69. forceIndex,
  70. ignoreIndex
  71. }
  72. );
  73. }
  74. }
  75. class MySqlSelectQueryBuilderBase extends TypedQueryBuilder {
  76. static [entityKind] = "MySqlSelectQueryBuilder";
  77. _;
  78. config;
  79. joinsNotNullableMap;
  80. tableName;
  81. isPartialSelect;
  82. /** @internal */
  83. session;
  84. dialect;
  85. cacheConfig = void 0;
  86. usedTables = /* @__PURE__ */ new Set();
  87. constructor({ table, fields, isPartialSelect, session, dialect, withList, distinct, useIndex, forceIndex, ignoreIndex }) {
  88. super();
  89. this.config = {
  90. withList,
  91. table,
  92. fields: { ...fields },
  93. distinct,
  94. setOperators: [],
  95. useIndex,
  96. forceIndex,
  97. ignoreIndex
  98. };
  99. this.isPartialSelect = isPartialSelect;
  100. this.session = session;
  101. this.dialect = dialect;
  102. this._ = {
  103. selectedFields: fields,
  104. config: this.config
  105. };
  106. this.tableName = getTableLikeName(table);
  107. this.joinsNotNullableMap = typeof this.tableName === "string" ? { [this.tableName]: true } : {};
  108. for (const item of extractUsedTable(table)) this.usedTables.add(item);
  109. }
  110. /** @internal */
  111. getUsedTables() {
  112. return [...this.usedTables];
  113. }
  114. createJoin(joinType, lateral) {
  115. return (table, a, b) => {
  116. const isCrossJoin = joinType === "cross";
  117. let on = isCrossJoin ? void 0 : a;
  118. const onIndex = isCrossJoin ? a : b;
  119. const baseTableName = this.tableName;
  120. const tableName = getTableLikeName(table);
  121. for (const item of extractUsedTable(table)) this.usedTables.add(item);
  122. if (typeof tableName === "string" && this.config.joins?.some((join) => join.alias === tableName)) {
  123. throw new Error(`Alias "${tableName}" is already used in this query`);
  124. }
  125. if (!this.isPartialSelect) {
  126. if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") {
  127. this.config.fields = {
  128. [baseTableName]: this.config.fields
  129. };
  130. }
  131. if (typeof tableName === "string" && !is(table, SQL)) {
  132. const selection = is(table, Subquery) ? table._.selectedFields : is(table, View) ? table[ViewBaseConfig].selectedFields : table[Table.Symbol.Columns];
  133. this.config.fields[tableName] = selection;
  134. }
  135. }
  136. if (typeof on === "function") {
  137. on = on(
  138. new Proxy(
  139. this.config.fields,
  140. new SelectionProxyHandler({ sqlAliasedBehavior: "sql", sqlBehavior: "sql" })
  141. )
  142. );
  143. }
  144. if (!this.config.joins) {
  145. this.config.joins = [];
  146. }
  147. let useIndex = [];
  148. let forceIndex = [];
  149. let ignoreIndex = [];
  150. if (is(table, MySqlTable) && onIndex && typeof onIndex !== "string") {
  151. if (onIndex.useIndex) {
  152. useIndex = convertIndexToString(toArray(onIndex.useIndex));
  153. }
  154. if (onIndex.forceIndex) {
  155. forceIndex = convertIndexToString(toArray(onIndex.forceIndex));
  156. }
  157. if (onIndex.ignoreIndex) {
  158. ignoreIndex = convertIndexToString(toArray(onIndex.ignoreIndex));
  159. }
  160. }
  161. this.config.joins.push({ on, table, joinType, alias: tableName, useIndex, forceIndex, ignoreIndex, lateral });
  162. if (typeof tableName === "string") {
  163. switch (joinType) {
  164. case "left": {
  165. this.joinsNotNullableMap[tableName] = false;
  166. break;
  167. }
  168. case "right": {
  169. this.joinsNotNullableMap = Object.fromEntries(
  170. Object.entries(this.joinsNotNullableMap).map(([key]) => [key, false])
  171. );
  172. this.joinsNotNullableMap[tableName] = true;
  173. break;
  174. }
  175. case "cross":
  176. case "inner": {
  177. this.joinsNotNullableMap[tableName] = true;
  178. break;
  179. }
  180. }
  181. }
  182. return this;
  183. };
  184. }
  185. /**
  186. * Executes a `left join` operation by adding another table to the current query.
  187. *
  188. * Calling this method associates each row of the table with the corresponding row from the joined table, if a match is found. If no matching row exists, it sets all columns of the joined table to null.
  189. *
  190. * See docs: {@link https://orm.drizzle.team/docs/joins#left-join}
  191. *
  192. * @param table the table to join.
  193. * @param on the `on` clause.
  194. * @param onIndex index hint.
  195. *
  196. * @example
  197. *
  198. * ```ts
  199. * // Select all users and their pets
  200. * const usersWithPets: { user: User; pets: Pet | null; }[] = await db.select()
  201. * .from(users)
  202. * .leftJoin(pets, eq(users.id, pets.ownerId))
  203. *
  204. * // Select userId and petId
  205. * const usersIdsAndPetIds: { userId: number; petId: number | null; }[] = await db.select({
  206. * userId: users.id,
  207. * petId: pets.id,
  208. * })
  209. * .from(users)
  210. * .leftJoin(pets, eq(users.id, pets.ownerId))
  211. *
  212. * // Select userId and petId with use index hint
  213. * const usersIdsAndPetIds: { userId: number; petId: number | null; }[] = await db.select({
  214. * userId: users.id,
  215. * petId: pets.id,
  216. * })
  217. * .from(users)
  218. * .leftJoin(pets, eq(users.id, pets.ownerId), {
  219. * useIndex: ['pets_owner_id_index']
  220. * })
  221. * ```
  222. */
  223. leftJoin = this.createJoin("left", false);
  224. /**
  225. * Executes a `left join lateral` operation by adding subquery to the current query.
  226. *
  227. * A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
  228. *
  229. * Calling this method associates each row of the table with the corresponding row from the joined table, if a match is found. If no matching row exists, it sets all columns of the joined table to null.
  230. *
  231. * See docs: {@link https://orm.drizzle.team/docs/joins#left-join-lateral}
  232. *
  233. * @param table the subquery to join.
  234. * @param on the `on` clause.
  235. */
  236. leftJoinLateral = this.createJoin("left", true);
  237. /**
  238. * Executes a `right join` operation by adding another table to the current query.
  239. *
  240. * Calling this method associates each row of the joined table with the corresponding row from the main table, if a match is found. If no matching row exists, it sets all columns of the main table to null.
  241. *
  242. * See docs: {@link https://orm.drizzle.team/docs/joins#right-join}
  243. *
  244. * @param table the table to join.
  245. * @param on the `on` clause.
  246. * @param onIndex index hint.
  247. *
  248. * @example
  249. *
  250. * ```ts
  251. * // Select all users and their pets
  252. * const usersWithPets: { user: User | null; pets: Pet; }[] = await db.select()
  253. * .from(users)
  254. * .rightJoin(pets, eq(users.id, pets.ownerId))
  255. *
  256. * // Select userId and petId
  257. * const usersIdsAndPetIds: { userId: number | null; petId: number; }[] = await db.select({
  258. * userId: users.id,
  259. * petId: pets.id,
  260. * })
  261. * .from(users)
  262. * .rightJoin(pets, eq(users.id, pets.ownerId))
  263. *
  264. * // Select userId and petId with use index hint
  265. * const usersIdsAndPetIds: { userId: number; petId: number | null; }[] = await db.select({
  266. * userId: users.id,
  267. * petId: pets.id,
  268. * })
  269. * .from(users)
  270. * .leftJoin(pets, eq(users.id, pets.ownerId), {
  271. * useIndex: ['pets_owner_id_index']
  272. * })
  273. * ```
  274. */
  275. rightJoin = this.createJoin("right", false);
  276. /**
  277. * Executes an `inner join` operation, creating a new table by combining rows from two tables that have matching values.
  278. *
  279. * Calling this method retrieves rows that have corresponding entries in both joined tables. Rows without matching entries in either table are excluded, resulting in a table that includes only matching pairs.
  280. *
  281. * See docs: {@link https://orm.drizzle.team/docs/joins#inner-join}
  282. *
  283. * @param table the table to join.
  284. * @param on the `on` clause.
  285. * @param onIndex index hint.
  286. *
  287. * @example
  288. *
  289. * ```ts
  290. * // Select all users and their pets
  291. * const usersWithPets: { user: User; pets: Pet; }[] = await db.select()
  292. * .from(users)
  293. * .innerJoin(pets, eq(users.id, pets.ownerId))
  294. *
  295. * // Select userId and petId
  296. * const usersIdsAndPetIds: { userId: number; petId: number; }[] = await db.select({
  297. * userId: users.id,
  298. * petId: pets.id,
  299. * })
  300. * .from(users)
  301. * .innerJoin(pets, eq(users.id, pets.ownerId))
  302. *
  303. * // Select userId and petId with use index hint
  304. * const usersIdsAndPetIds: { userId: number; petId: number | null; }[] = await db.select({
  305. * userId: users.id,
  306. * petId: pets.id,
  307. * })
  308. * .from(users)
  309. * .leftJoin(pets, eq(users.id, pets.ownerId), {
  310. * useIndex: ['pets_owner_id_index']
  311. * })
  312. * ```
  313. */
  314. innerJoin = this.createJoin("inner", false);
  315. /**
  316. * Executes an `inner join lateral` operation, creating a new table by combining rows from two queries that have matching values.
  317. *
  318. * A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
  319. *
  320. * Calling this method retrieves rows that have corresponding entries in both joined tables. Rows without matching entries in either table are excluded, resulting in a table that includes only matching pairs.
  321. *
  322. * See docs: {@link https://orm.drizzle.team/docs/joins#inner-join-lateral}
  323. *
  324. * @param table the subquery to join.
  325. * @param on the `on` clause.
  326. */
  327. innerJoinLateral = this.createJoin("inner", true);
  328. /**
  329. * Executes a `cross join` operation by combining rows from two tables into a new table.
  330. *
  331. * Calling this method retrieves all rows from both main and joined tables, merging all rows from each table.
  332. *
  333. * See docs: {@link https://orm.drizzle.team/docs/joins#cross-join}
  334. *
  335. * @param table the table to join.
  336. * @param onIndex index hint.
  337. *
  338. * @example
  339. *
  340. * ```ts
  341. * // Select all users, each user with every pet
  342. * const usersWithPets: { user: User; pets: Pet; }[] = await db.select()
  343. * .from(users)
  344. * .crossJoin(pets)
  345. *
  346. * // Select userId and petId
  347. * const usersIdsAndPetIds: { userId: number; petId: number; }[] = await db.select({
  348. * userId: users.id,
  349. * petId: pets.id,
  350. * })
  351. * .from(users)
  352. * .crossJoin(pets)
  353. *
  354. * // Select userId and petId with use index hint
  355. * const usersIdsAndPetIds: { userId: number; petId: number; }[] = await db.select({
  356. * userId: users.id,
  357. * petId: pets.id,
  358. * })
  359. * .from(users)
  360. * .crossJoin(pets, {
  361. * useIndex: ['pets_owner_id_index']
  362. * })
  363. * ```
  364. */
  365. crossJoin = this.createJoin("cross", false);
  366. /**
  367. * Executes a `cross join lateral` operation by combining rows from two queries into a new table.
  368. *
  369. * A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
  370. *
  371. * Calling this method retrieves all rows from both main and joined queries, merging all rows from each query.
  372. *
  373. * See docs: {@link https://orm.drizzle.team/docs/joins#cross-join-lateral}
  374. *
  375. * @param table the query to join.
  376. */
  377. crossJoinLateral = this.createJoin("cross", true);
  378. createSetOperator(type, isAll) {
  379. return (rightSelection) => {
  380. const rightSelect = typeof rightSelection === "function" ? rightSelection(getMySqlSetOperators()) : rightSelection;
  381. if (!haveSameKeys(this.getSelectedFields(), rightSelect.getSelectedFields())) {
  382. throw new Error(
  383. "Set operator error (union / intersect / except): selected fields are not the same or are in a different order"
  384. );
  385. }
  386. this.config.setOperators.push({ type, isAll, rightSelect });
  387. return this;
  388. };
  389. }
  390. /**
  391. * Adds `union` set operator to the query.
  392. *
  393. * Calling this method will combine the result sets of the `select` statements and remove any duplicate rows that appear across them.
  394. *
  395. * See docs: {@link https://orm.drizzle.team/docs/set-operations#union}
  396. *
  397. * @example
  398. *
  399. * ```ts
  400. * // Select all unique names from customers and users tables
  401. * await db.select({ name: users.name })
  402. * .from(users)
  403. * .union(
  404. * db.select({ name: customers.name }).from(customers)
  405. * );
  406. * // or
  407. * import { union } from 'drizzle-orm/mysql-core'
  408. *
  409. * await union(
  410. * db.select({ name: users.name }).from(users),
  411. * db.select({ name: customers.name }).from(customers)
  412. * );
  413. * ```
  414. */
  415. union = this.createSetOperator("union", false);
  416. /**
  417. * Adds `union all` set operator to the query.
  418. *
  419. * Calling this method will combine the result-set of the `select` statements and keep all duplicate rows that appear across them.
  420. *
  421. * See docs: {@link https://orm.drizzle.team/docs/set-operations#union-all}
  422. *
  423. * @example
  424. *
  425. * ```ts
  426. * // Select all transaction ids from both online and in-store sales
  427. * await db.select({ transaction: onlineSales.transactionId })
  428. * .from(onlineSales)
  429. * .unionAll(
  430. * db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales)
  431. * );
  432. * // or
  433. * import { unionAll } from 'drizzle-orm/mysql-core'
  434. *
  435. * await unionAll(
  436. * db.select({ transaction: onlineSales.transactionId }).from(onlineSales),
  437. * db.select({ transaction: inStoreSales.transactionId }).from(inStoreSales)
  438. * );
  439. * ```
  440. */
  441. unionAll = this.createSetOperator("union", true);
  442. /**
  443. * Adds `intersect` set operator to the query.
  444. *
  445. * Calling this method will retain only the rows that are present in both result sets and eliminate duplicates.
  446. *
  447. * See docs: {@link https://orm.drizzle.team/docs/set-operations#intersect}
  448. *
  449. * @example
  450. *
  451. * ```ts
  452. * // Select course names that are offered in both departments A and B
  453. * await db.select({ courseName: depA.courseName })
  454. * .from(depA)
  455. * .intersect(
  456. * db.select({ courseName: depB.courseName }).from(depB)
  457. * );
  458. * // or
  459. * import { intersect } from 'drizzle-orm/mysql-core'
  460. *
  461. * await intersect(
  462. * db.select({ courseName: depA.courseName }).from(depA),
  463. * db.select({ courseName: depB.courseName }).from(depB)
  464. * );
  465. * ```
  466. */
  467. intersect = this.createSetOperator("intersect", false);
  468. /**
  469. * Adds `intersect all` set operator to the query.
  470. *
  471. * Calling this method will retain only the rows that are present in both result sets including all duplicates.
  472. *
  473. * See docs: {@link https://orm.drizzle.team/docs/set-operations#intersect-all}
  474. *
  475. * @example
  476. *
  477. * ```ts
  478. * // Select all products and quantities that are ordered by both regular and VIP customers
  479. * await db.select({
  480. * productId: regularCustomerOrders.productId,
  481. * quantityOrdered: regularCustomerOrders.quantityOrdered
  482. * })
  483. * .from(regularCustomerOrders)
  484. * .intersectAll(
  485. * db.select({
  486. * productId: vipCustomerOrders.productId,
  487. * quantityOrdered: vipCustomerOrders.quantityOrdered
  488. * })
  489. * .from(vipCustomerOrders)
  490. * );
  491. * // or
  492. * import { intersectAll } from 'drizzle-orm/mysql-core'
  493. *
  494. * await intersectAll(
  495. * db.select({
  496. * productId: regularCustomerOrders.productId,
  497. * quantityOrdered: regularCustomerOrders.quantityOrdered
  498. * })
  499. * .from(regularCustomerOrders),
  500. * db.select({
  501. * productId: vipCustomerOrders.productId,
  502. * quantityOrdered: vipCustomerOrders.quantityOrdered
  503. * })
  504. * .from(vipCustomerOrders)
  505. * );
  506. * ```
  507. */
  508. intersectAll = this.createSetOperator("intersect", true);
  509. /**
  510. * Adds `except` set operator to the query.
  511. *
  512. * Calling this method will retrieve all unique rows from the left query, except for the rows that are present in the result set of the right query.
  513. *
  514. * See docs: {@link https://orm.drizzle.team/docs/set-operations#except}
  515. *
  516. * @example
  517. *
  518. * ```ts
  519. * // Select all courses offered in department A but not in department B
  520. * await db.select({ courseName: depA.courseName })
  521. * .from(depA)
  522. * .except(
  523. * db.select({ courseName: depB.courseName }).from(depB)
  524. * );
  525. * // or
  526. * import { except } from 'drizzle-orm/mysql-core'
  527. *
  528. * await except(
  529. * db.select({ courseName: depA.courseName }).from(depA),
  530. * db.select({ courseName: depB.courseName }).from(depB)
  531. * );
  532. * ```
  533. */
  534. except = this.createSetOperator("except", false);
  535. /**
  536. * Adds `except all` set operator to the query.
  537. *
  538. * Calling this method will retrieve all rows from the left query, except for the rows that are present in the result set of the right query.
  539. *
  540. * See docs: {@link https://orm.drizzle.team/docs/set-operations#except-all}
  541. *
  542. * @example
  543. *
  544. * ```ts
  545. * // Select all products that are ordered by regular customers but not by VIP customers
  546. * await db.select({
  547. * productId: regularCustomerOrders.productId,
  548. * quantityOrdered: regularCustomerOrders.quantityOrdered,
  549. * })
  550. * .from(regularCustomerOrders)
  551. * .exceptAll(
  552. * db.select({
  553. * productId: vipCustomerOrders.productId,
  554. * quantityOrdered: vipCustomerOrders.quantityOrdered,
  555. * })
  556. * .from(vipCustomerOrders)
  557. * );
  558. * // or
  559. * import { exceptAll } from 'drizzle-orm/mysql-core'
  560. *
  561. * await exceptAll(
  562. * db.select({
  563. * productId: regularCustomerOrders.productId,
  564. * quantityOrdered: regularCustomerOrders.quantityOrdered
  565. * })
  566. * .from(regularCustomerOrders),
  567. * db.select({
  568. * productId: vipCustomerOrders.productId,
  569. * quantityOrdered: vipCustomerOrders.quantityOrdered
  570. * })
  571. * .from(vipCustomerOrders)
  572. * );
  573. * ```
  574. */
  575. exceptAll = this.createSetOperator("except", true);
  576. /** @internal */
  577. addSetOperators(setOperators) {
  578. this.config.setOperators.push(...setOperators);
  579. return this;
  580. }
  581. /**
  582. * Adds a `where` clause to the query.
  583. *
  584. * Calling this method will select only those rows that fulfill a specified condition.
  585. *
  586. * See docs: {@link https://orm.drizzle.team/docs/select#filtering}
  587. *
  588. * @param where the `where` clause.
  589. *
  590. * @example
  591. * You can use conditional operators and `sql function` to filter the rows to be selected.
  592. *
  593. * ```ts
  594. * // Select all cars with green color
  595. * await db.select().from(cars).where(eq(cars.color, 'green'));
  596. * // or
  597. * await db.select().from(cars).where(sql`${cars.color} = 'green'`)
  598. * ```
  599. *
  600. * You can logically combine conditional operators with `and()` and `or()` operators:
  601. *
  602. * ```ts
  603. * // Select all BMW cars with a green color
  604. * await db.select().from(cars).where(and(eq(cars.color, 'green'), eq(cars.brand, 'BMW')));
  605. *
  606. * // Select all cars with the green or blue color
  607. * await db.select().from(cars).where(or(eq(cars.color, 'green'), eq(cars.color, 'blue')));
  608. * ```
  609. */
  610. where(where) {
  611. if (typeof where === "function") {
  612. where = where(
  613. new Proxy(
  614. this.config.fields,
  615. new SelectionProxyHandler({ sqlAliasedBehavior: "sql", sqlBehavior: "sql" })
  616. )
  617. );
  618. }
  619. this.config.where = where;
  620. return this;
  621. }
  622. /**
  623. * Adds a `having` clause to the query.
  624. *
  625. * Calling this method will select only those rows that fulfill a specified condition. It is typically used with aggregate functions to filter the aggregated data based on a specified condition.
  626. *
  627. * See docs: {@link https://orm.drizzle.team/docs/select#aggregations}
  628. *
  629. * @param having the `having` clause.
  630. *
  631. * @example
  632. *
  633. * ```ts
  634. * // Select all brands with more than one car
  635. * await db.select({
  636. * brand: cars.brand,
  637. * count: sql<number>`cast(count(${cars.id}) as int)`,
  638. * })
  639. * .from(cars)
  640. * .groupBy(cars.brand)
  641. * .having(({ count }) => gt(count, 1));
  642. * ```
  643. */
  644. having(having) {
  645. if (typeof having === "function") {
  646. having = having(
  647. new Proxy(
  648. this.config.fields,
  649. new SelectionProxyHandler({ sqlAliasedBehavior: "sql", sqlBehavior: "sql" })
  650. )
  651. );
  652. }
  653. this.config.having = having;
  654. return this;
  655. }
  656. groupBy(...columns) {
  657. if (typeof columns[0] === "function") {
  658. const groupBy = columns[0](
  659. new Proxy(
  660. this.config.fields,
  661. new SelectionProxyHandler({ sqlAliasedBehavior: "alias", sqlBehavior: "sql" })
  662. )
  663. );
  664. this.config.groupBy = Array.isArray(groupBy) ? groupBy : [groupBy];
  665. } else {
  666. this.config.groupBy = columns;
  667. }
  668. return this;
  669. }
  670. orderBy(...columns) {
  671. if (typeof columns[0] === "function") {
  672. const orderBy = columns[0](
  673. new Proxy(
  674. this.config.fields,
  675. new SelectionProxyHandler({ sqlAliasedBehavior: "alias", sqlBehavior: "sql" })
  676. )
  677. );
  678. const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
  679. if (this.config.setOperators.length > 0) {
  680. this.config.setOperators.at(-1).orderBy = orderByArray;
  681. } else {
  682. this.config.orderBy = orderByArray;
  683. }
  684. } else {
  685. const orderByArray = columns;
  686. if (this.config.setOperators.length > 0) {
  687. this.config.setOperators.at(-1).orderBy = orderByArray;
  688. } else {
  689. this.config.orderBy = orderByArray;
  690. }
  691. }
  692. return this;
  693. }
  694. /**
  695. * Adds a `limit` clause to the query.
  696. *
  697. * Calling this method will set the maximum number of rows that will be returned by this query.
  698. *
  699. * See docs: {@link https://orm.drizzle.team/docs/select#limit--offset}
  700. *
  701. * @param limit the `limit` clause.
  702. *
  703. * @example
  704. *
  705. * ```ts
  706. * // Get the first 10 people from this query.
  707. * await db.select().from(people).limit(10);
  708. * ```
  709. */
  710. limit(limit) {
  711. if (this.config.setOperators.length > 0) {
  712. this.config.setOperators.at(-1).limit = limit;
  713. } else {
  714. this.config.limit = limit;
  715. }
  716. return this;
  717. }
  718. /**
  719. * Adds an `offset` clause to the query.
  720. *
  721. * Calling this method will skip a number of rows when returning results from this query.
  722. *
  723. * See docs: {@link https://orm.drizzle.team/docs/select#limit--offset}
  724. *
  725. * @param offset the `offset` clause.
  726. *
  727. * @example
  728. *
  729. * ```ts
  730. * // Get the 10th-20th people from this query.
  731. * await db.select().from(people).offset(10).limit(10);
  732. * ```
  733. */
  734. offset(offset) {
  735. if (this.config.setOperators.length > 0) {
  736. this.config.setOperators.at(-1).offset = offset;
  737. } else {
  738. this.config.offset = offset;
  739. }
  740. return this;
  741. }
  742. /**
  743. * Adds a `for` clause to the query.
  744. *
  745. * Calling this method will specify a lock strength for this query that controls how strictly it acquires exclusive access to the rows being queried.
  746. *
  747. * See docs: {@link https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html}
  748. *
  749. * @param strength the lock strength.
  750. * @param config the lock configuration.
  751. */
  752. for(strength, config = {}) {
  753. this.config.lockingClause = { strength, config };
  754. return this;
  755. }
  756. /** @internal */
  757. getSQL() {
  758. return this.dialect.buildSelectQuery(this.config);
  759. }
  760. toSQL() {
  761. const { typings: _typings, ...rest } = this.dialect.sqlToQuery(this.getSQL());
  762. return rest;
  763. }
  764. as(alias) {
  765. const usedTables = [];
  766. usedTables.push(...extractUsedTable(this.config.table));
  767. if (this.config.joins) {
  768. for (const it of this.config.joins) usedTables.push(...extractUsedTable(it.table));
  769. }
  770. return new Proxy(
  771. new Subquery(this.getSQL(), this.config.fields, alias, false, [...new Set(usedTables)]),
  772. new SelectionProxyHandler({ alias, sqlAliasedBehavior: "alias", sqlBehavior: "error" })
  773. );
  774. }
  775. /** @internal */
  776. getSelectedFields() {
  777. return new Proxy(
  778. this.config.fields,
  779. new SelectionProxyHandler({ alias: this.tableName, sqlAliasedBehavior: "alias", sqlBehavior: "error" })
  780. );
  781. }
  782. $dynamic() {
  783. return this;
  784. }
  785. $withCache(config) {
  786. this.cacheConfig = config === void 0 ? { config: {}, enable: true, autoInvalidate: true } : config === false ? { enable: false } : { enable: true, autoInvalidate: true, ...config };
  787. return this;
  788. }
  789. }
  790. class MySqlSelectBase extends MySqlSelectQueryBuilderBase {
  791. static [entityKind] = "MySqlSelect";
  792. prepare() {
  793. if (!this.session) {
  794. throw new Error("Cannot execute a query on a query builder. Please use a database instance instead.");
  795. }
  796. const fieldsList = orderSelectedFields(this.config.fields);
  797. const query = this.session.prepareQuery(this.dialect.sqlToQuery(this.getSQL()), fieldsList, void 0, void 0, void 0, {
  798. type: "select",
  799. tables: [...this.usedTables]
  800. }, this.cacheConfig);
  801. query.joinsNotNullableMap = this.joinsNotNullableMap;
  802. return query;
  803. }
  804. execute = (placeholderValues) => {
  805. return this.prepare().execute(placeholderValues);
  806. };
  807. createIterator = () => {
  808. const self = this;
  809. return async function* (placeholderValues) {
  810. yield* self.prepare().iterator(placeholderValues);
  811. };
  812. };
  813. iterator = this.createIterator();
  814. }
  815. applyMixins(MySqlSelectBase, [QueryPromise]);
  816. function createSetOperator(type, isAll) {
  817. return (leftSelect, rightSelect, ...restSelects) => {
  818. const setOperators = [rightSelect, ...restSelects].map((select) => ({
  819. type,
  820. isAll,
  821. rightSelect: select
  822. }));
  823. for (const setOperator of setOperators) {
  824. if (!haveSameKeys(leftSelect.getSelectedFields(), setOperator.rightSelect.getSelectedFields())) {
  825. throw new Error(
  826. "Set operator error (union / intersect / except): selected fields are not the same or are in a different order"
  827. );
  828. }
  829. }
  830. return leftSelect.addSetOperators(setOperators);
  831. };
  832. }
  833. const getMySqlSetOperators = () => ({
  834. union,
  835. unionAll,
  836. intersect,
  837. intersectAll,
  838. except,
  839. exceptAll
  840. });
  841. const union = createSetOperator("union", false);
  842. const unionAll = createSetOperator("union", true);
  843. const intersect = createSetOperator("intersect", false);
  844. const intersectAll = createSetOperator("intersect", true);
  845. const except = createSetOperator("except", false);
  846. const exceptAll = createSetOperator("except", true);
  847. export {
  848. MySqlSelectBase,
  849. MySqlSelectBuilder,
  850. MySqlSelectQueryBuilderBase,
  851. except,
  852. exceptAll,
  853. intersect,
  854. intersectAll,
  855. union,
  856. unionAll
  857. };
  858. //# sourceMappingURL=select.js.map