dialect.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from "../alias.js";
  2. import { CasingCache } from "../casing.js";
  3. import { Column } from "../column.js";
  4. import { entityKind, is } from "../entity.js";
  5. import { DrizzleError } from "../errors.js";
  6. import {
  7. getOperators,
  8. getOrderByOperators,
  9. Many,
  10. normalizeRelation,
  11. One
  12. } from "../relations.js";
  13. import { and, eq } from "../sql/expressions/index.js";
  14. import { Param, SQL, sql, View } from "../sql/sql.js";
  15. import { Subquery } from "../subquery.js";
  16. import { getTableName, getTableUniqueName, Table } from "../table.js";
  17. import { orderSelectedFields } from "../utils.js";
  18. import { ViewBaseConfig } from "../view-common.js";
  19. import { MySqlColumn } from "./columns/common.js";
  20. import { MySqlTable } from "./table.js";
  21. import { MySqlViewBase } from "./view-base.js";
  22. class MySqlDialect {
  23. static [entityKind] = "MySqlDialect";
  24. /** @internal */
  25. casing;
  26. constructor(config) {
  27. this.casing = new CasingCache(config?.casing);
  28. }
  29. async migrate(migrations, session, config) {
  30. const migrationsTable = config.migrationsTable ?? "__drizzle_migrations";
  31. const migrationTableCreate = sql`
  32. create table if not exists ${sql.identifier(migrationsTable)} (
  33. id serial primary key,
  34. hash text not null,
  35. created_at bigint
  36. )
  37. `;
  38. await session.execute(migrationTableCreate);
  39. const dbMigrations = await session.all(
  40. sql`select id, hash, created_at from ${sql.identifier(migrationsTable)} order by created_at desc limit 1`
  41. );
  42. const lastDbMigration = dbMigrations[0];
  43. await session.transaction(async (tx) => {
  44. for (const migration of migrations) {
  45. if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
  46. for (const stmt of migration.sql) {
  47. await tx.execute(sql.raw(stmt));
  48. }
  49. await tx.execute(
  50. sql`insert into ${sql.identifier(
  51. migrationsTable
  52. )} (\`hash\`, \`created_at\`) values(${migration.hash}, ${migration.folderMillis})`
  53. );
  54. }
  55. }
  56. });
  57. }
  58. escapeName(name) {
  59. return `\`${name.replace(/`/g, "``")}\``;
  60. }
  61. escapeParam(_num) {
  62. return `?`;
  63. }
  64. escapeString(str) {
  65. return `'${str.replace(/'/g, "''")}'`;
  66. }
  67. buildWithCTE(queries) {
  68. if (!queries?.length) return void 0;
  69. const withSqlChunks = [sql`with `];
  70. for (const [i, w] of queries.entries()) {
  71. withSqlChunks.push(sql`${sql.identifier(w._.alias)} as (${w._.sql})`);
  72. if (i < queries.length - 1) {
  73. withSqlChunks.push(sql`, `);
  74. }
  75. }
  76. withSqlChunks.push(sql` `);
  77. return sql.join(withSqlChunks);
  78. }
  79. buildDeleteQuery({
  80. table,
  81. where,
  82. returning,
  83. withList,
  84. limit,
  85. orderBy
  86. }) {
  87. const withSql = this.buildWithCTE(withList);
  88. const returningSql = returning ? sql` returning ${this.buildSelection(returning, { isSingleTable: true })}` : void 0;
  89. const whereSql = where ? sql` where ${where}` : void 0;
  90. const orderBySql = this.buildOrderBy(orderBy);
  91. const limitSql = this.buildLimit(limit);
  92. return sql`${withSql}delete from ${table}${whereSql}${orderBySql}${limitSql}${returningSql}`;
  93. }
  94. buildUpdateSet(table, set) {
  95. const tableColumns = table[Table.Symbol.Columns];
  96. const columnNames = Object.keys(tableColumns).filter(
  97. (colName) => set[colName] !== void 0 || tableColumns[colName]?.onUpdateFn !== void 0
  98. );
  99. const setSize = columnNames.length;
  100. return sql.join(
  101. columnNames.flatMap((colName, i) => {
  102. const col = tableColumns[colName];
  103. const onUpdateFnResult = col.onUpdateFn?.();
  104. const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));
  105. const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
  106. if (i < setSize - 1) {
  107. return [res, sql.raw(", ")];
  108. }
  109. return [res];
  110. })
  111. );
  112. }
  113. buildUpdateQuery({
  114. table,
  115. set,
  116. where,
  117. returning,
  118. withList,
  119. limit,
  120. orderBy
  121. }) {
  122. const withSql = this.buildWithCTE(withList);
  123. const setSql = this.buildUpdateSet(table, set);
  124. const returningSql = returning ? sql` returning ${this.buildSelection(returning, { isSingleTable: true })}` : void 0;
  125. const whereSql = where ? sql` where ${where}` : void 0;
  126. const orderBySql = this.buildOrderBy(orderBy);
  127. const limitSql = this.buildLimit(limit);
  128. return sql`${withSql}update ${table} set ${setSql}${whereSql}${orderBySql}${limitSql}${returningSql}`;
  129. }
  130. /**
  131. * Builds selection SQL with provided fields/expressions
  132. *
  133. * Examples:
  134. *
  135. * `select <selection> from`
  136. *
  137. * `insert ... returning <selection>`
  138. *
  139. * If `isSingleTable` is true, then columns won't be prefixed with table name
  140. */
  141. buildSelection(fields, { isSingleTable = false } = {}) {
  142. const columnsLen = fields.length;
  143. const chunks = fields.flatMap(({ field }, i) => {
  144. const chunk = [];
  145. if (is(field, SQL.Aliased) && field.isSelectionField) {
  146. chunk.push(sql.identifier(field.fieldAlias));
  147. } else if (is(field, SQL.Aliased) || is(field, SQL)) {
  148. const query = is(field, SQL.Aliased) ? field.sql : field;
  149. if (isSingleTable) {
  150. chunk.push(
  151. new SQL(
  152. query.queryChunks.map((c) => {
  153. if (is(c, MySqlColumn)) {
  154. return sql.identifier(this.casing.getColumnCasing(c));
  155. }
  156. return c;
  157. })
  158. )
  159. );
  160. } else {
  161. chunk.push(query);
  162. }
  163. if (is(field, SQL.Aliased)) {
  164. chunk.push(sql` as ${sql.identifier(field.fieldAlias)}`);
  165. }
  166. } else if (is(field, Column)) {
  167. if (isSingleTable) {
  168. chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
  169. } else {
  170. chunk.push(field);
  171. }
  172. } else if (is(field, Subquery)) {
  173. const entries = Object.entries(field._.selectedFields);
  174. if (entries.length === 1) {
  175. const entry = entries[0][1];
  176. const fieldDecoder = is(entry, SQL) ? entry.decoder : is(entry, Column) ? { mapFromDriverValue: (v) => entry.mapFromDriverValue(v) } : entry.sql.decoder;
  177. if (fieldDecoder) {
  178. field._.sql.decoder = fieldDecoder;
  179. }
  180. }
  181. chunk.push(field);
  182. }
  183. if (i < columnsLen - 1) {
  184. chunk.push(sql`, `);
  185. }
  186. return chunk;
  187. });
  188. return sql.join(chunks);
  189. }
  190. buildLimit(limit) {
  191. return typeof limit === "object" || typeof limit === "number" && limit >= 0 ? sql` limit ${limit}` : void 0;
  192. }
  193. buildOrderBy(orderBy) {
  194. return orderBy && orderBy.length > 0 ? sql` order by ${sql.join(orderBy, sql`, `)}` : void 0;
  195. }
  196. buildIndex({
  197. indexes,
  198. indexFor
  199. }) {
  200. return indexes && indexes.length > 0 ? sql` ${sql.raw(indexFor)} INDEX (${sql.raw(indexes.join(`, `))})` : void 0;
  201. }
  202. buildSelectQuery({
  203. withList,
  204. fields,
  205. fieldsFlat,
  206. where,
  207. having,
  208. table,
  209. joins,
  210. orderBy,
  211. groupBy,
  212. limit,
  213. offset,
  214. lockingClause,
  215. distinct,
  216. setOperators,
  217. useIndex,
  218. forceIndex,
  219. ignoreIndex
  220. }) {
  221. const fieldsList = fieldsFlat ?? orderSelectedFields(fields);
  222. for (const f of fieldsList) {
  223. if (is(f.field, Column) && getTableName(f.field.table) !== (is(table, Subquery) ? table._.alias : is(table, MySqlViewBase) ? table[ViewBaseConfig].name : is(table, SQL) ? void 0 : getTableName(table)) && !((table2) => joins?.some(
  224. ({ alias }) => alias === (table2[Table.Symbol.IsAlias] ? getTableName(table2) : table2[Table.Symbol.BaseName])
  225. ))(f.field.table)) {
  226. const tableName = getTableName(f.field.table);
  227. throw new Error(
  228. `Your "${f.path.join(
  229. "->"
  230. )}" field references a column "${tableName}"."${f.field.name}", but the table "${tableName}" is not part of the query! Did you forget to join it?`
  231. );
  232. }
  233. }
  234. const isSingleTable = !joins || joins.length === 0;
  235. const withSql = this.buildWithCTE(withList);
  236. const distinctSql = distinct ? sql` distinct` : void 0;
  237. const selection = this.buildSelection(fieldsList, { isSingleTable });
  238. const tableSql = (() => {
  239. if (is(table, Table) && table[Table.Symbol.IsAlias]) {
  240. return sql`${sql`${sql.identifier(table[Table.Symbol.Schema] ?? "")}.`.if(table[Table.Symbol.Schema])}${sql.identifier(
  241. table[Table.Symbol.OriginalName]
  242. )} ${sql.identifier(table[Table.Symbol.Name])}`;
  243. }
  244. return table;
  245. })();
  246. const joinsArray = [];
  247. if (joins) {
  248. for (const [index, joinMeta] of joins.entries()) {
  249. if (index === 0) {
  250. joinsArray.push(sql` `);
  251. }
  252. const table2 = joinMeta.table;
  253. const lateralSql = joinMeta.lateral ? sql` lateral` : void 0;
  254. const onSql = joinMeta.on ? sql` on ${joinMeta.on}` : void 0;
  255. if (is(table2, MySqlTable)) {
  256. const tableName = table2[MySqlTable.Symbol.Name];
  257. const tableSchema = table2[MySqlTable.Symbol.Schema];
  258. const origTableName = table2[MySqlTable.Symbol.OriginalName];
  259. const alias = tableName === origTableName ? void 0 : joinMeta.alias;
  260. const useIndexSql2 = this.buildIndex({
  261. indexes: joinMeta.useIndex,
  262. indexFor: "USE"
  263. });
  264. const forceIndexSql2 = this.buildIndex({
  265. indexes: joinMeta.forceIndex,
  266. indexFor: "FORCE"
  267. });
  268. const ignoreIndexSql2 = this.buildIndex({
  269. indexes: joinMeta.ignoreIndex,
  270. indexFor: "IGNORE"
  271. });
  272. joinsArray.push(
  273. sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${tableSchema ? sql`${sql.identifier(tableSchema)}.` : void 0}${sql.identifier(origTableName)}${useIndexSql2}${forceIndexSql2}${ignoreIndexSql2}${alias && sql` ${sql.identifier(alias)}`}${onSql}`
  274. );
  275. } else if (is(table2, View)) {
  276. const viewName = table2[ViewBaseConfig].name;
  277. const viewSchema = table2[ViewBaseConfig].schema;
  278. const origViewName = table2[ViewBaseConfig].originalName;
  279. const alias = viewName === origViewName ? void 0 : joinMeta.alias;
  280. joinsArray.push(
  281. sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${viewSchema ? sql`${sql.identifier(viewSchema)}.` : void 0}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`
  282. );
  283. } else {
  284. joinsArray.push(
  285. sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table2}${onSql}`
  286. );
  287. }
  288. if (index < joins.length - 1) {
  289. joinsArray.push(sql` `);
  290. }
  291. }
  292. }
  293. const joinsSql = sql.join(joinsArray);
  294. const whereSql = where ? sql` where ${where}` : void 0;
  295. const havingSql = having ? sql` having ${having}` : void 0;
  296. const orderBySql = this.buildOrderBy(orderBy);
  297. const groupBySql = groupBy && groupBy.length > 0 ? sql` group by ${sql.join(groupBy, sql`, `)}` : void 0;
  298. const limitSql = this.buildLimit(limit);
  299. const offsetSql = offset ? sql` offset ${offset}` : void 0;
  300. const useIndexSql = this.buildIndex({ indexes: useIndex, indexFor: "USE" });
  301. const forceIndexSql = this.buildIndex({
  302. indexes: forceIndex,
  303. indexFor: "FORCE"
  304. });
  305. const ignoreIndexSql = this.buildIndex({
  306. indexes: ignoreIndex,
  307. indexFor: "IGNORE"
  308. });
  309. let lockingClausesSql;
  310. if (lockingClause) {
  311. const { config, strength } = lockingClause;
  312. lockingClausesSql = sql` for ${sql.raw(strength)}`;
  313. if (config.noWait) {
  314. lockingClausesSql.append(sql` nowait`);
  315. } else if (config.skipLocked) {
  316. lockingClausesSql.append(sql` skip locked`);
  317. }
  318. }
  319. const finalQuery = sql`${withSql}select${distinctSql} ${selection} from ${tableSql}${useIndexSql}${forceIndexSql}${ignoreIndexSql}${joinsSql}${whereSql}${groupBySql}${havingSql}${orderBySql}${limitSql}${offsetSql}${lockingClausesSql}`;
  320. if (setOperators.length > 0) {
  321. return this.buildSetOperations(finalQuery, setOperators);
  322. }
  323. return finalQuery;
  324. }
  325. buildSetOperations(leftSelect, setOperators) {
  326. const [setOperator, ...rest] = setOperators;
  327. if (!setOperator) {
  328. throw new Error("Cannot pass undefined values to any set operator");
  329. }
  330. if (rest.length === 0) {
  331. return this.buildSetOperationQuery({ leftSelect, setOperator });
  332. }
  333. return this.buildSetOperations(
  334. this.buildSetOperationQuery({ leftSelect, setOperator }),
  335. rest
  336. );
  337. }
  338. buildSetOperationQuery({
  339. leftSelect,
  340. setOperator: { type, isAll, rightSelect, limit, orderBy, offset }
  341. }) {
  342. const leftChunk = sql`(${leftSelect.getSQL()}) `;
  343. const rightChunk = sql`(${rightSelect.getSQL()})`;
  344. let orderBySql;
  345. if (orderBy && orderBy.length > 0) {
  346. const orderByValues = [];
  347. for (const orderByUnit of orderBy) {
  348. if (is(orderByUnit, MySqlColumn)) {
  349. orderByValues.push(
  350. sql.identifier(this.casing.getColumnCasing(orderByUnit))
  351. );
  352. } else if (is(orderByUnit, SQL)) {
  353. for (let i = 0; i < orderByUnit.queryChunks.length; i++) {
  354. const chunk = orderByUnit.queryChunks[i];
  355. if (is(chunk, MySqlColumn)) {
  356. orderByUnit.queryChunks[i] = sql.identifier(
  357. this.casing.getColumnCasing(chunk)
  358. );
  359. }
  360. }
  361. orderByValues.push(sql`${orderByUnit}`);
  362. } else {
  363. orderByValues.push(sql`${orderByUnit}`);
  364. }
  365. }
  366. orderBySql = sql` order by ${sql.join(orderByValues, sql`, `)} `;
  367. }
  368. const limitSql = typeof limit === "object" || typeof limit === "number" && limit >= 0 ? sql` limit ${limit}` : void 0;
  369. const operatorChunk = sql.raw(`${type} ${isAll ? "all " : ""}`);
  370. const offsetSql = offset ? sql` offset ${offset}` : void 0;
  371. return sql`${leftChunk}${operatorChunk}${rightChunk}${orderBySql}${limitSql}${offsetSql}`;
  372. }
  373. buildInsertQuery({
  374. table,
  375. values: valuesOrSelect,
  376. ignore,
  377. onConflict,
  378. select
  379. }) {
  380. const valuesSqlList = [];
  381. const columns = table[Table.Symbol.Columns];
  382. const colEntries = Object.entries(columns).filter(
  383. ([_, col]) => !col.shouldDisableInsert()
  384. );
  385. const insertOrder = colEntries.map(([, column]) => sql.identifier(this.casing.getColumnCasing(column)));
  386. const generatedIdsResponse = [];
  387. if (select) {
  388. const select2 = valuesOrSelect;
  389. if (is(select2, SQL)) {
  390. valuesSqlList.push(select2);
  391. } else {
  392. valuesSqlList.push(select2.getSQL());
  393. }
  394. } else {
  395. const values = valuesOrSelect;
  396. valuesSqlList.push(sql.raw("values "));
  397. for (const [valueIndex, value] of values.entries()) {
  398. const generatedIds = {};
  399. const valueList = [];
  400. for (const [fieldName, col] of colEntries) {
  401. const colValue = value[fieldName];
  402. if (colValue === void 0 || is(colValue, Param) && colValue.value === void 0) {
  403. if (col.defaultFn !== void 0) {
  404. const defaultFnResult = col.defaultFn();
  405. generatedIds[fieldName] = defaultFnResult;
  406. const defaultValue = is(defaultFnResult, SQL) ? defaultFnResult : sql.param(defaultFnResult, col);
  407. valueList.push(defaultValue);
  408. } else if (!col.default && col.onUpdateFn !== void 0) {
  409. const onUpdateFnResult = col.onUpdateFn();
  410. const newValue = is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col);
  411. valueList.push(newValue);
  412. } else {
  413. valueList.push(sql`default`);
  414. }
  415. } else {
  416. if (col.defaultFn && is(colValue, Param)) {
  417. generatedIds[fieldName] = colValue.value;
  418. }
  419. valueList.push(colValue);
  420. }
  421. }
  422. generatedIdsResponse.push(generatedIds);
  423. valuesSqlList.push(valueList);
  424. if (valueIndex < values.length - 1) {
  425. valuesSqlList.push(sql`, `);
  426. }
  427. }
  428. }
  429. const valuesSql = sql.join(valuesSqlList);
  430. const ignoreSql = ignore ? sql` ignore` : void 0;
  431. const onConflictSql = onConflict ? sql` on duplicate key ${onConflict}` : void 0;
  432. return {
  433. sql: sql`insert${ignoreSql} into ${table} ${insertOrder} ${valuesSql}${onConflictSql}`,
  434. generatedIds: generatedIdsResponse
  435. };
  436. }
  437. sqlToQuery(sql2, invokeSource) {
  438. return sql2.toQuery({
  439. casing: this.casing,
  440. escapeName: this.escapeName,
  441. escapeParam: this.escapeParam,
  442. escapeString: this.escapeString,
  443. invokeSource
  444. });
  445. }
  446. buildRelationalQuery({
  447. fullSchema,
  448. schema,
  449. tableNamesMap,
  450. table,
  451. tableConfig,
  452. queryConfig: config,
  453. tableAlias,
  454. nestedQueryRelation,
  455. joinOn
  456. }) {
  457. let selection = [];
  458. let limit, offset, orderBy, where;
  459. const joins = [];
  460. if (config === true) {
  461. const selectionEntries = Object.entries(tableConfig.columns);
  462. selection = selectionEntries.map(([key, value]) => ({
  463. dbKey: value.name,
  464. tsKey: key,
  465. field: aliasedTableColumn(value, tableAlias),
  466. relationTableTsKey: void 0,
  467. isJson: false,
  468. selection: []
  469. }));
  470. } else {
  471. const aliasedColumns = Object.fromEntries(
  472. Object.entries(tableConfig.columns).map(([key, value]) => [
  473. key,
  474. aliasedTableColumn(value, tableAlias)
  475. ])
  476. );
  477. if (config.where) {
  478. const whereSql = typeof config.where === "function" ? config.where(aliasedColumns, getOperators()) : config.where;
  479. where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);
  480. }
  481. const fieldsSelection = [];
  482. let selectedColumns = [];
  483. if (config.columns) {
  484. let isIncludeMode = false;
  485. for (const [field, value] of Object.entries(config.columns)) {
  486. if (value === void 0) {
  487. continue;
  488. }
  489. if (field in tableConfig.columns) {
  490. if (!isIncludeMode && value === true) {
  491. isIncludeMode = true;
  492. }
  493. selectedColumns.push(field);
  494. }
  495. }
  496. if (selectedColumns.length > 0) {
  497. selectedColumns = isIncludeMode ? selectedColumns.filter((c) => config.columns?.[c] === true) : Object.keys(tableConfig.columns).filter(
  498. (key) => !selectedColumns.includes(key)
  499. );
  500. }
  501. } else {
  502. selectedColumns = Object.keys(tableConfig.columns);
  503. }
  504. for (const field of selectedColumns) {
  505. const column = tableConfig.columns[field];
  506. fieldsSelection.push({ tsKey: field, value: column });
  507. }
  508. let selectedRelations = [];
  509. if (config.with) {
  510. selectedRelations = Object.entries(config.with).filter(
  511. (entry) => !!entry[1]
  512. ).map(([tsKey, queryConfig]) => ({
  513. tsKey,
  514. queryConfig,
  515. relation: tableConfig.relations[tsKey]
  516. }));
  517. }
  518. let extras;
  519. if (config.extras) {
  520. extras = typeof config.extras === "function" ? config.extras(aliasedColumns, { sql }) : config.extras;
  521. for (const [tsKey, value] of Object.entries(extras)) {
  522. fieldsSelection.push({
  523. tsKey,
  524. value: mapColumnsInAliasedSQLToAlias(value, tableAlias)
  525. });
  526. }
  527. }
  528. for (const { tsKey, value } of fieldsSelection) {
  529. selection.push({
  530. dbKey: is(value, SQL.Aliased) ? value.fieldAlias : tableConfig.columns[tsKey].name,
  531. tsKey,
  532. field: is(value, Column) ? aliasedTableColumn(value, tableAlias) : value,
  533. relationTableTsKey: void 0,
  534. isJson: false,
  535. selection: []
  536. });
  537. }
  538. let orderByOrig = typeof config.orderBy === "function" ? config.orderBy(aliasedColumns, getOrderByOperators()) : config.orderBy ?? [];
  539. if (!Array.isArray(orderByOrig)) {
  540. orderByOrig = [orderByOrig];
  541. }
  542. orderBy = orderByOrig.map((orderByValue) => {
  543. if (is(orderByValue, Column)) {
  544. return aliasedTableColumn(orderByValue, tableAlias);
  545. }
  546. return mapColumnsInSQLToAlias(orderByValue, tableAlias);
  547. });
  548. limit = config.limit;
  549. offset = config.offset;
  550. for (const {
  551. tsKey: selectedRelationTsKey,
  552. queryConfig: selectedRelationConfigValue,
  553. relation
  554. } of selectedRelations) {
  555. const normalizedRelation = normalizeRelation(
  556. schema,
  557. tableNamesMap,
  558. relation
  559. );
  560. const relationTableName = getTableUniqueName(relation.referencedTable);
  561. const relationTableTsName = tableNamesMap[relationTableName];
  562. const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
  563. const joinOn2 = and(
  564. ...normalizedRelation.fields.map(
  565. (field2, i) => eq(
  566. aliasedTableColumn(
  567. normalizedRelation.references[i],
  568. relationTableAlias
  569. ),
  570. aliasedTableColumn(field2, tableAlias)
  571. )
  572. )
  573. );
  574. const builtRelation = this.buildRelationalQuery({
  575. fullSchema,
  576. schema,
  577. tableNamesMap,
  578. table: fullSchema[relationTableTsName],
  579. tableConfig: schema[relationTableTsName],
  580. queryConfig: is(relation, One) ? selectedRelationConfigValue === true ? { limit: 1 } : { ...selectedRelationConfigValue, limit: 1 } : selectedRelationConfigValue,
  581. tableAlias: relationTableAlias,
  582. joinOn: joinOn2,
  583. nestedQueryRelation: relation
  584. });
  585. const field = sql`${sql.identifier(relationTableAlias)}.${sql.identifier("data")}`.as(
  586. selectedRelationTsKey
  587. );
  588. joins.push({
  589. on: sql`true`,
  590. table: new Subquery(builtRelation.sql, {}, relationTableAlias),
  591. alias: relationTableAlias,
  592. joinType: "left",
  593. lateral: true
  594. });
  595. selection.push({
  596. dbKey: selectedRelationTsKey,
  597. tsKey: selectedRelationTsKey,
  598. field,
  599. relationTableTsKey: relationTableTsName,
  600. isJson: true,
  601. selection: builtRelation.selection
  602. });
  603. }
  604. }
  605. if (selection.length === 0) {
  606. throw new DrizzleError({
  607. message: `No fields selected for table "${tableConfig.tsName}" ("${tableAlias}")`
  608. });
  609. }
  610. let result;
  611. where = and(joinOn, where);
  612. if (nestedQueryRelation) {
  613. let field = sql`json_array(${sql.join(
  614. selection.map(
  615. ({ field: field2, tsKey, isJson }) => isJson ? sql`${sql.identifier(`${tableAlias}_${tsKey}`)}.${sql.identifier("data")}` : is(field2, SQL.Aliased) ? field2.sql : field2
  616. ),
  617. sql`, `
  618. )})`;
  619. if (is(nestedQueryRelation, Many)) {
  620. field = sql`coalesce(json_arrayagg(${field}), json_array())`;
  621. }
  622. const nestedSelection = [
  623. {
  624. dbKey: "data",
  625. tsKey: "data",
  626. field: field.as("data"),
  627. isJson: true,
  628. relationTableTsKey: tableConfig.tsName,
  629. selection
  630. }
  631. ];
  632. const needsSubquery = limit !== void 0 || offset !== void 0 || (orderBy?.length ?? 0) > 0;
  633. if (needsSubquery) {
  634. result = this.buildSelectQuery({
  635. table: aliasedTable(table, tableAlias),
  636. fields: {},
  637. fieldsFlat: [
  638. {
  639. path: [],
  640. field: sql.raw("*")
  641. },
  642. ...((orderBy?.length ?? 0) > 0 ? [
  643. {
  644. path: [],
  645. field: sql`row_number() over (order by ${sql.join(orderBy, sql`, `)})`
  646. }
  647. ] : [])
  648. ],
  649. where,
  650. limit,
  651. offset,
  652. setOperators: []
  653. });
  654. where = void 0;
  655. limit = void 0;
  656. offset = void 0;
  657. orderBy = void 0;
  658. } else {
  659. result = aliasedTable(table, tableAlias);
  660. }
  661. result = this.buildSelectQuery({
  662. table: is(result, MySqlTable) ? result : new Subquery(result, {}, tableAlias),
  663. fields: {},
  664. fieldsFlat: nestedSelection.map(({ field: field2 }) => ({
  665. path: [],
  666. field: is(field2, Column) ? aliasedTableColumn(field2, tableAlias) : field2
  667. })),
  668. joins,
  669. where,
  670. limit,
  671. offset,
  672. orderBy,
  673. setOperators: []
  674. });
  675. } else {
  676. result = this.buildSelectQuery({
  677. table: aliasedTable(table, tableAlias),
  678. fields: {},
  679. fieldsFlat: selection.map(({ field }) => ({
  680. path: [],
  681. field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field
  682. })),
  683. joins,
  684. where,
  685. limit,
  686. offset,
  687. orderBy,
  688. setOperators: []
  689. });
  690. }
  691. return {
  692. tableTsKey: tableConfig.tsName,
  693. sql: result,
  694. selection
  695. };
  696. }
  697. buildRelationalQueryWithoutLateralSubqueries({
  698. fullSchema,
  699. schema,
  700. tableNamesMap,
  701. table,
  702. tableConfig,
  703. queryConfig: config,
  704. tableAlias,
  705. nestedQueryRelation,
  706. joinOn
  707. }) {
  708. let selection = [];
  709. let limit, offset, orderBy = [], where;
  710. if (config === true) {
  711. const selectionEntries = Object.entries(tableConfig.columns);
  712. selection = selectionEntries.map(([key, value]) => ({
  713. dbKey: value.name,
  714. tsKey: key,
  715. field: aliasedTableColumn(value, tableAlias),
  716. relationTableTsKey: void 0,
  717. isJson: false,
  718. selection: []
  719. }));
  720. } else {
  721. const aliasedColumns = Object.fromEntries(
  722. Object.entries(tableConfig.columns).map(([key, value]) => [
  723. key,
  724. aliasedTableColumn(value, tableAlias)
  725. ])
  726. );
  727. if (config.where) {
  728. const whereSql = typeof config.where === "function" ? config.where(aliasedColumns, getOperators()) : config.where;
  729. where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);
  730. }
  731. const fieldsSelection = [];
  732. let selectedColumns = [];
  733. if (config.columns) {
  734. let isIncludeMode = false;
  735. for (const [field, value] of Object.entries(config.columns)) {
  736. if (value === void 0) {
  737. continue;
  738. }
  739. if (field in tableConfig.columns) {
  740. if (!isIncludeMode && value === true) {
  741. isIncludeMode = true;
  742. }
  743. selectedColumns.push(field);
  744. }
  745. }
  746. if (selectedColumns.length > 0) {
  747. selectedColumns = isIncludeMode ? selectedColumns.filter((c) => config.columns?.[c] === true) : Object.keys(tableConfig.columns).filter(
  748. (key) => !selectedColumns.includes(key)
  749. );
  750. }
  751. } else {
  752. selectedColumns = Object.keys(tableConfig.columns);
  753. }
  754. for (const field of selectedColumns) {
  755. const column = tableConfig.columns[field];
  756. fieldsSelection.push({ tsKey: field, value: column });
  757. }
  758. let selectedRelations = [];
  759. if (config.with) {
  760. selectedRelations = Object.entries(config.with).filter(
  761. (entry) => !!entry[1]
  762. ).map(([tsKey, queryConfig]) => ({
  763. tsKey,
  764. queryConfig,
  765. relation: tableConfig.relations[tsKey]
  766. }));
  767. }
  768. let extras;
  769. if (config.extras) {
  770. extras = typeof config.extras === "function" ? config.extras(aliasedColumns, { sql }) : config.extras;
  771. for (const [tsKey, value] of Object.entries(extras)) {
  772. fieldsSelection.push({
  773. tsKey,
  774. value: mapColumnsInAliasedSQLToAlias(value, tableAlias)
  775. });
  776. }
  777. }
  778. for (const { tsKey, value } of fieldsSelection) {
  779. selection.push({
  780. dbKey: is(value, SQL.Aliased) ? value.fieldAlias : tableConfig.columns[tsKey].name,
  781. tsKey,
  782. field: is(value, Column) ? aliasedTableColumn(value, tableAlias) : value,
  783. relationTableTsKey: void 0,
  784. isJson: false,
  785. selection: []
  786. });
  787. }
  788. let orderByOrig = typeof config.orderBy === "function" ? config.orderBy(aliasedColumns, getOrderByOperators()) : config.orderBy ?? [];
  789. if (!Array.isArray(orderByOrig)) {
  790. orderByOrig = [orderByOrig];
  791. }
  792. orderBy = orderByOrig.map((orderByValue) => {
  793. if (is(orderByValue, Column)) {
  794. return aliasedTableColumn(orderByValue, tableAlias);
  795. }
  796. return mapColumnsInSQLToAlias(orderByValue, tableAlias);
  797. });
  798. limit = config.limit;
  799. offset = config.offset;
  800. for (const {
  801. tsKey: selectedRelationTsKey,
  802. queryConfig: selectedRelationConfigValue,
  803. relation
  804. } of selectedRelations) {
  805. const normalizedRelation = normalizeRelation(
  806. schema,
  807. tableNamesMap,
  808. relation
  809. );
  810. const relationTableName = getTableUniqueName(relation.referencedTable);
  811. const relationTableTsName = tableNamesMap[relationTableName];
  812. const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
  813. const joinOn2 = and(
  814. ...normalizedRelation.fields.map(
  815. (field2, i) => eq(
  816. aliasedTableColumn(
  817. normalizedRelation.references[i],
  818. relationTableAlias
  819. ),
  820. aliasedTableColumn(field2, tableAlias)
  821. )
  822. )
  823. );
  824. const builtRelation = this.buildRelationalQueryWithoutLateralSubqueries(
  825. {
  826. fullSchema,
  827. schema,
  828. tableNamesMap,
  829. table: fullSchema[relationTableTsName],
  830. tableConfig: schema[relationTableTsName],
  831. queryConfig: is(relation, One) ? selectedRelationConfigValue === true ? { limit: 1 } : { ...selectedRelationConfigValue, limit: 1 } : selectedRelationConfigValue,
  832. tableAlias: relationTableAlias,
  833. joinOn: joinOn2,
  834. nestedQueryRelation: relation
  835. }
  836. );
  837. let fieldSql = sql`(${builtRelation.sql})`;
  838. if (is(relation, Many)) {
  839. fieldSql = sql`coalesce(${fieldSql}, json_array())`;
  840. }
  841. const field = fieldSql.as(selectedRelationTsKey);
  842. selection.push({
  843. dbKey: selectedRelationTsKey,
  844. tsKey: selectedRelationTsKey,
  845. field,
  846. relationTableTsKey: relationTableTsName,
  847. isJson: true,
  848. selection: builtRelation.selection
  849. });
  850. }
  851. }
  852. if (selection.length === 0) {
  853. throw new DrizzleError({
  854. message: `No fields selected for table "${tableConfig.tsName}" ("${tableAlias}"). You need to have at least one item in "columns", "with" or "extras". If you need to select all columns, omit the "columns" key or set it to undefined.`
  855. });
  856. }
  857. let result;
  858. where = and(joinOn, where);
  859. if (nestedQueryRelation) {
  860. let field = sql`json_array(${sql.join(
  861. selection.map(
  862. ({ field: field2 }) => is(field2, MySqlColumn) ? sql.identifier(this.casing.getColumnCasing(field2)) : is(field2, SQL.Aliased) ? field2.sql : field2
  863. ),
  864. sql`, `
  865. )})`;
  866. if (is(nestedQueryRelation, Many)) {
  867. field = sql`json_arrayagg(${field})`;
  868. }
  869. const nestedSelection = [
  870. {
  871. dbKey: "data",
  872. tsKey: "data",
  873. field,
  874. isJson: true,
  875. relationTableTsKey: tableConfig.tsName,
  876. selection
  877. }
  878. ];
  879. const needsSubquery = limit !== void 0 || offset !== void 0 || orderBy.length > 0;
  880. if (needsSubquery) {
  881. result = this.buildSelectQuery({
  882. table: aliasedTable(table, tableAlias),
  883. fields: {},
  884. fieldsFlat: [
  885. {
  886. path: [],
  887. field: sql.raw("*")
  888. },
  889. ...(orderBy.length > 0 ? [
  890. {
  891. path: [],
  892. field: sql`row_number() over (order by ${sql.join(orderBy, sql`, `)})`
  893. }
  894. ] : [])
  895. ],
  896. where,
  897. limit,
  898. offset,
  899. setOperators: []
  900. });
  901. where = void 0;
  902. limit = void 0;
  903. offset = void 0;
  904. orderBy = void 0;
  905. } else {
  906. result = aliasedTable(table, tableAlias);
  907. }
  908. result = this.buildSelectQuery({
  909. table: is(result, MySqlTable) ? result : new Subquery(result, {}, tableAlias),
  910. fields: {},
  911. fieldsFlat: nestedSelection.map(({ field: field2 }) => ({
  912. path: [],
  913. field: is(field2, Column) ? aliasedTableColumn(field2, tableAlias) : field2
  914. })),
  915. where,
  916. limit,
  917. offset,
  918. orderBy,
  919. setOperators: []
  920. });
  921. } else {
  922. result = this.buildSelectQuery({
  923. table: aliasedTable(table, tableAlias),
  924. fields: {},
  925. fieldsFlat: selection.map(({ field }) => ({
  926. path: [],
  927. field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field
  928. })),
  929. where,
  930. limit,
  931. offset,
  932. orderBy,
  933. setOperators: []
  934. });
  935. }
  936. return {
  937. tableTsKey: tableConfig.tsName,
  938. sql: result,
  939. selection
  940. };
  941. }
  942. }
  943. export {
  944. MySqlDialect
  945. };
  946. //# sourceMappingURL=dialect.js.map