select.js 27 KB

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