AK-21/Graphite-Industrial-Intelligence
0
1import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from "../alias.js";2import { CasingCache } from "../casing.js";3import { Column } from "../column.js";4import { entityKind, is } from "../entity.js";5import { DrizzleError } from "../errors.js";6import { GelColumn, GelDecimal, GelJson, GelUUID } from "./columns/index.js";7import { GelTable } from "./table.js";8import {9 getOperators,10 getOrderByOperators,11 Many,12 normalizeRelation,13 One14} from "../relations.js";15import { and, eq, View } from "../sql/index.js";16import {17 Param,18 SQL,19 sql20} from "../sql/sql.js";21import { Subquery } from "../subquery.js";22import { getTableName, getTableUniqueName, Table } from "../table.js";23import { orderSelectedFields } from "../utils.js";24import { ViewBaseConfig } from "../view-common.js";25import { GelTimestamp } from "./columns/timestamp.js";26import { GelViewBase } from "./view-base.js";27class GelDialect {28 static [entityKind] = "GelDialect";29 /** @internal */30 casing;31 constructor(config) {32 this.casing = new CasingCache(config?.casing);33 }34 // TODO can not migrate gel with drizzle35 // async migrate(migrations: MigrationMeta[], session: GelSession, config: string | MigrationConfig): Promise<void> {36 // const migrationsTable = typeof config === 'string'37 // ? '__drizzle_migrations'38 // : config.migrationsTable ?? '__drizzle_migrations';39 // const migrationsSchema = typeof config === 'string' ? 'drizzle' : config.migrationsSchema ?? 'drizzle';40 // const migrationTableCreate = sql`41 // CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsSchema)}.${sql.identifier(migrationsTable)} (42 // id SERIAL PRIMARY KEY,43 // hash text NOT NULL,44 // created_at bigint45 // )46 // `;47 // await session.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.identifier(migrationsSchema)}`);48 // await session.execute(migrationTableCreate);49 // const dbMigrations = await session.all<{ id: number; hash: string; created_at: string }>(50 // sql`select id, hash, created_at from ${sql.identifier(migrationsSchema)}.${51 // sql.identifier(migrationsTable)52 // } order by created_at desc limit 1`,53 // );54 // const lastDbMigration = dbMigrations[0];55 // await session.transaction(async (tx) => {56 // for await (const migration of migrations) {57 // if (58 // !lastDbMigration59 // || Number(lastDbMigration.created_at) < migration.folderMillis60 // ) {61 // for (const stmt of migration.sql) {62 // await tx.execute(sql.raw(stmt));63 // }64 // await tx.execute(65 // sql`insert into ${sql.identifier(migrationsSchema)}.${66 // sql.identifier(migrationsTable)67 // } ("hash", "created_at") values(${migration.hash}, ${migration.folderMillis})`,68 // );69 // }70 // }71 // });72 // }73 escapeName(name) {74 return `"${name}"`;75 }76 escapeParam(num) {77 return `$${num + 1}`;78 }79 escapeString(str) {80 return `'${str.replace(/'/g, "''")}'`;81 }82 buildWithCTE(queries) {83 if (!queries?.length) return void 0;84 const withSqlChunks = [sql`with `];85 for (const [i, w] of queries.entries()) {86 withSqlChunks.push(sql`${sql.identifier(w._.alias)} as (${w._.sql})`);87 if (i < queries.length - 1) {88 withSqlChunks.push(sql`, `);89 }90 }91 withSqlChunks.push(sql` `);92 return sql.join(withSqlChunks);93 }94 buildDeleteQuery({ table, where, returning, withList }) {95 const withSql = this.buildWithCTE(withList);96 const returningSql = returning ? sql` returning ${this.buildSelection(returning, { isSingleTable: true })}` : void 0;97 const whereSql = where ? sql` where ${where}` : void 0;98 return sql`${withSql}delete from ${table}${whereSql}${returningSql}`;99 }100 buildUpdateSet(table, set) {101 const tableColumns = table[Table.Symbol.Columns];102 const columnNames = Object.keys(tableColumns).filter(103 (colName) => set[colName] !== void 0 || tableColumns[colName]?.onUpdateFn !== void 0104 );105 const setSize = columnNames.length;106 return sql.join(columnNames.flatMap((colName, i) => {107 const col = tableColumns[colName];108 const onUpdateFnResult = col.onUpdateFn?.();109 const value = set[colName] ?? (is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col));110 const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;111 if (i < setSize - 1) {112 return [res, sql.raw(", ")];113 }114 return [res];115 }));116 }117 buildUpdateQuery({ table, set, where, returning, withList, from, joins }) {118 const withSql = this.buildWithCTE(withList);119 const tableName = table[GelTable.Symbol.Name];120 const tableSchema = table[GelTable.Symbol.Schema];121 const origTableName = table[GelTable.Symbol.OriginalName];122 const alias = tableName === origTableName ? void 0 : tableName;123 const tableSql = sql`${tableSchema ? sql`${sql.identifier(tableSchema)}.` : void 0}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`}`;124 const setSql = this.buildUpdateSet(table, set);125 const fromSql = from && sql.join([sql.raw(" from "), this.buildFromTable(from)]);126 const joinsSql = this.buildJoins(joins);127 const returningSql = returning ? sql` returning ${this.buildSelection(returning, { isSingleTable: !from })}` : void 0;128 const whereSql = where ? sql` where ${where}` : void 0;129 return sql`${withSql}update ${tableSql} set ${setSql}${fromSql}${joinsSql}${whereSql}${returningSql}`;130 }131 /**132 * Builds selection SQL with provided fields/expressions133 *134 * Examples:135 *136 * `select <selection> from`137 *138 * `insert ... returning <selection>`139 *140 * If `isSingleTable` is true, then columns won't be prefixed with table name141 * ^ Temporarily disabled behaviour, see comments within method for a reasoning142 */143 buildSelection(fields, { isSingleTable = false } = {}) {144 const columnsLen = fields.length;145 const chunks = fields.flatMap(({ field }, i) => {146 const chunk = [];147 if (is(field, SQL.Aliased) && field.isSelectionField) {148 chunk.push(sql.identifier(field.fieldAlias));149 } else if (is(field, SQL.Aliased) || is(field, SQL)) {150 const query = is(field, SQL.Aliased) ? field.sql : field;151 chunk.push(query);152 if (is(field, SQL.Aliased)) {153 chunk.push(sql` as ${sql.identifier(field.fieldAlias)}`);154 }155 } else if (is(field, Column)) {156 chunk.push(field);157 } else if (is(field, Subquery)) {158 const entries = Object.entries(field._.selectedFields);159 if (entries.length === 1) {160 const entry = entries[0][1];161 const fieldDecoder = is(entry, SQL) ? entry.decoder : is(entry, Column) ? { mapFromDriverValue: (v) => entry.mapFromDriverValue(v) } : entry.sql.decoder;162 if (fieldDecoder) {163 field._.sql.decoder = fieldDecoder;164 }165 }166 chunk.push(field);167 }168 if (i < columnsLen - 1) {169 chunk.push(sql`, `);170 }171 return chunk;172 });173 return sql.join(chunks);174 }175 buildJoins(joins) {176 if (!joins || joins.length === 0) {177 return void 0;178 }179 const joinsArray = [];180 for (const [index, joinMeta] of joins.entries()) {181 if (index === 0) {182 joinsArray.push(sql` `);183 }184 const table = joinMeta.table;185 const lateralSql = joinMeta.lateral ? sql` lateral` : void 0;186 const onSql = joinMeta.on ? sql` on ${joinMeta.on}` : void 0;187 if (is(table, GelTable)) {188 const tableName = table[GelTable.Symbol.Name];189 const tableSchema = table[GelTable.Symbol.Schema];190 const origTableName = table[GelTable.Symbol.OriginalName];191 const alias = tableName === origTableName ? void 0 : joinMeta.alias;192 joinsArray.push(193 sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${tableSchema ? sql`${sql.identifier(tableSchema)}.` : void 0}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`194 );195 } else if (is(table, View)) {196 const viewName = table[ViewBaseConfig].name;197 const viewSchema = table[ViewBaseConfig].schema;198 const origViewName = table[ViewBaseConfig].originalName;199 const alias = viewName === origViewName ? void 0 : joinMeta.alias;200 joinsArray.push(201 sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${viewSchema ? sql`${sql.identifier(viewSchema)}.` : void 0}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`202 );203 } else {204 joinsArray.push(205 sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table}${onSql}`206 );207 }208 if (index < joins.length - 1) {209 joinsArray.push(sql` `);210 }211 }212 return sql.join(joinsArray);213 }214 buildFromTable(table) {215 if (is(table, Table) && table[Table.Symbol.OriginalName] !== table[Table.Symbol.Name]) {216 let fullName = sql`${sql.identifier(table[Table.Symbol.OriginalName])}`;217 if (table[Table.Symbol.Schema]) {218 fullName = sql`${sql.identifier(table[Table.Symbol.Schema])}.${fullName}`;219 }220 return sql`${fullName} ${sql.identifier(table[Table.Symbol.Name])}`;221 }222 return table;223 }224 buildSelectQuery({225 withList,226 fields,227 fieldsFlat,228 where,229 having,230 table,231 joins,232 orderBy,233 groupBy,234 limit,235 offset,236 lockingClause,237 distinct,238 setOperators239 }) {240 const fieldsList = fieldsFlat ?? orderSelectedFields(fields);241 for (const f of fieldsList) {242 if (is(f.field, Column) && getTableName(f.field.table) !== (is(table, Subquery) ? table._.alias : is(table, GelViewBase) ? table[ViewBaseConfig].name : is(table, SQL) ? void 0 : getTableName(table)) && !((table2) => joins?.some(243 ({ alias }) => alias === (table2[Table.Symbol.IsAlias] ? getTableName(table2) : table2[Table.Symbol.BaseName])244 ))(f.field.table)) {245 const tableName = getTableName(f.field.table);246 throw new Error(247 `Your "${f.path.join("->")}" field references a column "${tableName}"."${f.field.name}", but the table "${tableName}" is not part of the query! Did you forget to join it?`248 );249 }250 }251 const isSingleTable = !joins || joins.length === 0;252 const withSql = this.buildWithCTE(withList);253 let distinctSql;254 if (distinct) {255 distinctSql = distinct === true ? sql` distinct` : sql` distinct on (${sql.join(distinct.on, sql`, `)})`;256 }257 const selection = this.buildSelection(fieldsList, { isSingleTable });258 const tableSql = this.buildFromTable(table);259 const joinsSql = this.buildJoins(joins);260 const whereSql = where ? sql` where ${where}` : void 0;261 const havingSql = having ? sql` having ${having}` : void 0;262 let orderBySql;263 if (orderBy && orderBy.length > 0) {264 orderBySql = sql` order by ${sql.join(orderBy, sql`, `)}`;265 }266 let groupBySql;267 if (groupBy && groupBy.length > 0) {268 groupBySql = sql` group by ${sql.join(groupBy, sql`, `)}`;269 }270 const limitSql = typeof limit === "object" || typeof limit === "number" && limit >= 0 ? sql` limit ${limit}` : void 0;271 const offsetSql = offset ? sql` offset ${offset}` : void 0;272 const lockingClauseSql = sql.empty();273 if (lockingClause) {274 const clauseSql = sql` for ${sql.raw(lockingClause.strength)}`;275 if (lockingClause.config.of) {276 clauseSql.append(277 sql` of ${sql.join(278 Array.isArray(lockingClause.config.of) ? lockingClause.config.of : [lockingClause.config.of],279 sql`, `280 )}`281 );282 }283 if (lockingClause.config.noWait) {284 clauseSql.append(sql` nowait`);285 } else if (lockingClause.config.skipLocked) {286 clauseSql.append(sql` skip locked`);287 }288 lockingClauseSql.append(clauseSql);289 }290 const finalQuery = sql`${withSql}select${distinctSql} ${selection} from ${tableSql}${joinsSql}${whereSql}${groupBySql}${havingSql}${orderBySql}${limitSql}${offsetSql}${lockingClauseSql}`;291 if (setOperators.length > 0) {292 return this.buildSetOperations(finalQuery, setOperators);293 }294 return finalQuery;295 }296 buildSetOperations(leftSelect, setOperators) {297 const [setOperator, ...rest] = setOperators;298 if (!setOperator) {299 throw new Error("Cannot pass undefined values to any set operator");300 }301 if (rest.length === 0) {302 return this.buildSetOperationQuery({ leftSelect, setOperator });303 }304 return this.buildSetOperations(305 this.buildSetOperationQuery({ leftSelect, setOperator }),306 rest307 );308 }309 buildSetOperationQuery({310 leftSelect,311 setOperator: { type, isAll, rightSelect, limit, orderBy, offset }312 }) {313 const leftChunk = sql`(${leftSelect.getSQL()}) `;314 const rightChunk = sql`(${rightSelect.getSQL()})`;315 let orderBySql;316 if (orderBy && orderBy.length > 0) {317 const orderByValues = [];318 for (const singleOrderBy of orderBy) {319 if (is(singleOrderBy, GelColumn)) {320 orderByValues.push(sql.identifier(singleOrderBy.name));321 } else if (is(singleOrderBy, SQL)) {322 for (let i = 0; i < singleOrderBy.queryChunks.length; i++) {323 const chunk = singleOrderBy.queryChunks[i];324 if (is(chunk, GelColumn)) {325 singleOrderBy.queryChunks[i] = sql.identifier(chunk.name);326 }327 }328 orderByValues.push(sql`${singleOrderBy}`);329 } else {330 orderByValues.push(sql`${singleOrderBy}`);331 }332 }333 orderBySql = sql` order by ${sql.join(orderByValues, sql`, `)} `;334 }335 const limitSql = typeof limit === "object" || typeof limit === "number" && limit >= 0 ? sql` limit ${limit}` : void 0;336 const operatorChunk = sql.raw(`${type} ${isAll ? "all " : ""}`);337 const offsetSql = offset ? sql` offset ${offset}` : void 0;338 return sql`${leftChunk}${operatorChunk}${rightChunk}${orderBySql}${limitSql}${offsetSql}`;339 }340 buildInsertQuery({ table, values: valuesOrSelect, onConflict, returning, withList, select, overridingSystemValue_ }) {341 const valuesSqlList = [];342 const columns = table[Table.Symbol.Columns];343 const colEntries = Object.entries(columns).filter(([_, col]) => !col.shouldDisableInsert());344 const insertOrder = colEntries.map(345 ([, column]) => sql.identifier(this.casing.getColumnCasing(column))346 );347 if (select) {348 const select2 = valuesOrSelect;349 if (is(select2, SQL)) {350 valuesSqlList.push(select2);351 } else {352 valuesSqlList.push(select2.getSQL());353 }354 } else {355 const values = valuesOrSelect;356 valuesSqlList.push(sql.raw("values "));357 for (const [valueIndex, value] of values.entries()) {358 const valueList = [];359 for (const [fieldName, col] of colEntries) {360 const colValue = value[fieldName];361 if (colValue === void 0 || is(colValue, Param) && colValue.value === void 0) {362 if (col.defaultFn !== void 0) {363 const defaultFnResult = col.defaultFn();364 const defaultValue = is(defaultFnResult, SQL) ? defaultFnResult : sql.param(defaultFnResult, col);365 valueList.push(defaultValue);366 } else if (!col.default && col.onUpdateFn !== void 0) {367 const onUpdateFnResult = col.onUpdateFn();368 const newValue = is(onUpdateFnResult, SQL) ? onUpdateFnResult : sql.param(onUpdateFnResult, col);369 valueList.push(newValue);370 } else {371 valueList.push(sql`default`);372 }373 } else {374 valueList.push(colValue);375 }376 }377 valuesSqlList.push(valueList);378 if (valueIndex < values.length - 1) {379 valuesSqlList.push(sql`, `);380 }381 }382 }383 const withSql = this.buildWithCTE(withList);384 const valuesSql = sql.join(valuesSqlList);385 const returningSql = returning ? sql` returning ${this.buildSelection(returning, { isSingleTable: true })}` : void 0;386 const onConflictSql = onConflict ? sql` on conflict ${onConflict}` : void 0;387 const overridingSql = overridingSystemValue_ === true ? sql`overriding system value ` : void 0;388 return sql`${withSql}insert into ${table} ${insertOrder} ${overridingSql}${valuesSql}${onConflictSql}${returningSql}`;389 }390 buildRefreshMaterializedViewQuery({ view, concurrently, withNoData }) {391 const concurrentlySql = concurrently ? sql` concurrently` : void 0;392 const withNoDataSql = withNoData ? sql` with no data` : void 0;393 return sql`refresh materialized view${concurrentlySql} ${view}${withNoDataSql}`;394 }395 prepareTyping(encoder) {396 if (is(encoder, GelJson)) {397 return "json";398 } else if (is(encoder, GelDecimal)) {399 return "decimal";400 } else if (is(encoder, GelTimestamp)) {401 return "timestamp";402 } else if (is(encoder, GelUUID)) {403 return "uuid";404 } else {405 return "none";406 }407 }408 sqlToQuery(sql2, invokeSource) {409 return sql2.toQuery({410 casing: this.casing,411 escapeName: this.escapeName,412 escapeParam: this.escapeParam,413 escapeString: this.escapeString,414 prepareTyping: this.prepareTyping,415 invokeSource416 });417 }418 // buildRelationalQueryWithPK({419 // fullSchema,420 // schema,421 // tableNamesMap,422 // table,423 // tableConfig,424 // queryConfig: config,425 // tableAlias,426 // isRoot = false,427 // joinOn,428 // }: {429 // fullSchema: Record<string, unknown>;430 // schema: TablesRelationalConfig;431 // tableNamesMap: Record<string, string>;432 // table: GelTable;433 // tableConfig: TableRelationalConfig;434 // queryConfig: true | DBQueryConfig<'many', true>;435 // tableAlias: string;436 // isRoot?: boolean;437 // joinOn?: SQL;438 // }): BuildRelationalQueryResult<GelTable, GelColumn> {439 // // For { "<relation>": true }, return a table with selection of all columns440 // if (config === true) {441 // const selectionEntries = Object.entries(tableConfig.columns);442 // const selection: BuildRelationalQueryResult<GelTable, GelColumn>['selection'] = selectionEntries.map((443 // [key, value],444 // ) => ({445 // dbKey: value.name,446 // tsKey: key,447 // field: value as GelColumn,448 // relationTableTsKey: undefined,449 // isJson: false,450 // selection: [],451 // }));452 // return {453 // tableTsKey: tableConfig.tsName,454 // sql: table,455 // selection,456 // };457 // }458 // // let selection: BuildRelationalQueryResult<GelTable, GelColumn>['selection'] = [];459 // // let selectionForBuild = selection;460 // const aliasedColumns = Object.fromEntries(461 // Object.entries(tableConfig.columns).map(([key, value]) => [key, aliasedTableColumn(value, tableAlias)]),462 // );463 // const aliasedRelations = Object.fromEntries(464 // Object.entries(tableConfig.relations).map(([key, value]) => [key, aliasedRelation(value, tableAlias)]),465 // );466 // const aliasedFields = Object.assign({}, aliasedColumns, aliasedRelations);467 // let where, hasUserDefinedWhere;468 // if (config.where) {469 // const whereSql = typeof config.where === 'function' ? config.where(aliasedFields, operators) : config.where;470 // where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);471 // hasUserDefinedWhere = !!where;472 // }473 // where = and(joinOn, where);474 // // const fieldsSelection: { tsKey: string; value: GelColumn | SQL.Aliased; isExtra?: boolean }[] = [];475 // let joins: Join[] = [];476 // let selectedColumns: string[] = [];477 // // Figure out which columns to select478 // if (config.columns) {479 // let isIncludeMode = false;480 // for (const [field, value] of Object.entries(config.columns)) {481 // if (value === undefined) {482 // continue;483 // }484 // if (field in tableConfig.columns) {485 // if (!isIncludeMode && value === true) {486 // isIncludeMode = true;487 // }488 // selectedColumns.push(field);489 // }490 // }491 // if (selectedColumns.length > 0) {492 // selectedColumns = isIncludeMode493 // ? selectedColumns.filter((c) => config.columns?.[c] === true)494 // : Object.keys(tableConfig.columns).filter((key) => !selectedColumns.includes(key));495 // }496 // } else {497 // // Select all columns if selection is not specified498 // selectedColumns = Object.keys(tableConfig.columns);499 // }500 // // for (const field of selectedColumns) {501 // // const column = tableConfig.columns[field]! as GelColumn;502 // // fieldsSelection.push({ tsKey: field, value: column });503 // // }504 // let initiallySelectedRelations: {505 // tsKey: string;506 // queryConfig: true | DBQueryConfig<'many', false>;507 // relation: Relation;508 // }[] = [];509 // // let selectedRelations: BuildRelationalQueryResult<GelTable, GelColumn>['selection'] = [];510 // // Figure out which relations to select511 // if (config.with) {512 // initiallySelectedRelations = Object.entries(config.with)513 // .filter((entry): entry is [typeof entry[0], NonNullable<typeof entry[1]>] => !!entry[1])514 // .map(([tsKey, queryConfig]) => ({ tsKey, queryConfig, relation: tableConfig.relations[tsKey]! }));515 // }516 // const manyRelations = initiallySelectedRelations.filter((r) =>517 // is(r.relation, Many)518 // && (schema[tableNamesMap[r.relation.referencedTable[Table.Symbol.Name]]!]?.primaryKey.length ?? 0) > 0519 // );520 // // If this is the last Many relation (or there are no Many relations), we are on the innermost subquery level521 // const isInnermostQuery = manyRelations.length < 2;522 // const selectedExtras: {523 // tsKey: string;524 // value: SQL.Aliased;525 // }[] = [];526 // // Figure out which extras to select527 // if (isInnermostQuery && config.extras) {528 // const extras = typeof config.extras === 'function'529 // ? config.extras(aliasedFields, { sql })530 // : config.extras;531 // for (const [tsKey, value] of Object.entries(extras)) {532 // selectedExtras.push({533 // tsKey,534 // value: mapColumnsInAliasedSQLToAlias(value, tableAlias),535 // });536 // }537 // }538 // // Transform `fieldsSelection` into `selection`539 // // `fieldsSelection` shouldn't be used after this point540 // // for (const { tsKey, value, isExtra } of fieldsSelection) {541 // // selection.push({542 // // dbKey: is(value, SQL.Aliased) ? value.fieldAlias : tableConfig.columns[tsKey]!.name,543 // // tsKey,544 // // field: is(value, Column) ? aliasedTableColumn(value, tableAlias) : value,545 // // relationTableTsKey: undefined,546 // // isJson: false,547 // // isExtra,548 // // selection: [],549 // // });550 // // }551 // let orderByOrig = typeof config.orderBy === 'function'552 // ? config.orderBy(aliasedFields, orderByOperators)553 // : config.orderBy ?? [];554 // if (!Array.isArray(orderByOrig)) {555 // orderByOrig = [orderByOrig];556 // }557 // const orderBy = orderByOrig.map((orderByValue) => {558 // if (is(orderByValue, Column)) {559 // return aliasedTableColumn(orderByValue, tableAlias) as GelColumn;560 // }561 // return mapColumnsInSQLToAlias(orderByValue, tableAlias);562 // });563 // const limit = isInnermostQuery ? config.limit : undefined;564 // const offset = isInnermostQuery ? config.offset : undefined;565 // // For non-root queries without additional config except columns, return a table with selection566 // if (567 // !isRoot568 // && initiallySelectedRelations.length === 0569 // && selectedExtras.length === 0570 // && !where571 // && orderBy.length === 0572 // && limit === undefined573 // && offset === undefined574 // ) {575 // return {576 // tableTsKey: tableConfig.tsName,577 // sql: table,578 // selection: selectedColumns.map((key) => ({579 // dbKey: tableConfig.columns[key]!.name,580 // tsKey: key,581 // field: tableConfig.columns[key] as GelColumn,582 // relationTableTsKey: undefined,583 // isJson: false,584 // selection: [],585 // })),586 // };587 // }588 // const selectedRelationsWithoutPK:589 // // Process all relations without primary keys, because they need to be joined differently and will all be on the same query level590 // for (591 // const {592 // tsKey: selectedRelationTsKey,593 // queryConfig: selectedRelationConfigValue,594 // relation,595 // } of initiallySelectedRelations596 // ) {597 // const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);598 // const relationTableName = relation.referencedTable[Table.Symbol.Name];599 // const relationTableTsName = tableNamesMap[relationTableName]!;600 // const relationTable = schema[relationTableTsName]!;601 // if (relationTable.primaryKey.length > 0) {602 // continue;603 // }604 // const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;605 // const joinOn = and(606 // ...normalizedRelation.fields.map((field, i) =>607 // eq(608 // aliasedTableColumn(normalizedRelation.references[i]!, relationTableAlias),609 // aliasedTableColumn(field, tableAlias),610 // )611 // ),612 // );613 // const builtRelation = this.buildRelationalQueryWithoutPK({614 // fullSchema,615 // schema,616 // tableNamesMap,617 // table: fullSchema[relationTableTsName] as GelTable,618 // tableConfig: schema[relationTableTsName]!,619 // queryConfig: selectedRelationConfigValue,620 // tableAlias: relationTableAlias,621 // joinOn,622 // nestedQueryRelation: relation,623 // });624 // const field = sql`${sql.identifier(relationTableAlias)}.${sql.identifier('data')}`.as(selectedRelationTsKey);625 // joins.push({626 // on: sql`true`,627 // table: new Subquery(builtRelation.sql as SQL, {}, relationTableAlias),628 // alias: relationTableAlias,629 // joinType: 'left',630 // lateral: true,631 // });632 // selectedRelations.push({633 // dbKey: selectedRelationTsKey,634 // tsKey: selectedRelationTsKey,635 // field,636 // relationTableTsKey: relationTableTsName,637 // isJson: true,638 // selection: builtRelation.selection,639 // });640 // }641 // const oneRelations = initiallySelectedRelations.filter((r): r is typeof r & { relation: One } =>642 // is(r.relation, One)643 // );644 // // Process all One relations with PKs, because they can all be joined on the same level645 // for (646 // const {647 // tsKey: selectedRelationTsKey,648 // queryConfig: selectedRelationConfigValue,649 // relation,650 // } of oneRelations651 // ) {652 // const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);653 // const relationTableName = relation.referencedTable[Table.Symbol.Name];654 // const relationTableTsName = tableNamesMap[relationTableName]!;655 // const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;656 // const relationTable = schema[relationTableTsName]!;657 // if (relationTable.primaryKey.length === 0) {658 // continue;659 // }660 // const joinOn = and(661 // ...normalizedRelation.fields.map((field, i) =>662 // eq(663 // aliasedTableColumn(normalizedRelation.references[i]!, relationTableAlias),664 // aliasedTableColumn(field, tableAlias),665 // )666 // ),667 // );668 // const builtRelation = this.buildRelationalQueryWithPK({669 // fullSchema,670 // schema,671 // tableNamesMap,672 // table: fullSchema[relationTableTsName] as GelTable,673 // tableConfig: schema[relationTableTsName]!,674 // queryConfig: selectedRelationConfigValue,675 // tableAlias: relationTableAlias,676 // joinOn,677 // });678 // const field = sql`case when ${sql.identifier(relationTableAlias)} is null then null else json_build_array(${679 // sql.join(680 // builtRelation.selection.map(({ field }) =>681 // is(field, SQL.Aliased)682 // ? sql`${sql.identifier(relationTableAlias)}.${sql.identifier(field.fieldAlias)}`683 // : is(field, Column)684 // ? aliasedTableColumn(field, relationTableAlias)685 // : field686 // ),687 // sql`, `,688 // )689 // }) end`.as(selectedRelationTsKey);690 // const isLateralJoin = is(builtRelation.sql, SQL);691 // joins.push({692 // on: isLateralJoin ? sql`true` : joinOn,693 // table: is(builtRelation.sql, SQL)694 // ? new Subquery(builtRelation.sql, {}, relationTableAlias)695 // : aliasedTable(builtRelation.sql, relationTableAlias),696 // alias: relationTableAlias,697 // joinType: 'left',698 // lateral: is(builtRelation.sql, SQL),699 // });700 // selectedRelations.push({701 // dbKey: selectedRelationTsKey,702 // tsKey: selectedRelationTsKey,703 // field,704 // relationTableTsKey: relationTableTsName,705 // isJson: true,706 // selection: builtRelation.selection,707 // });708 // }709 // let distinct: GelSelectConfig['distinct'];710 // let tableFrom: GelTable | Subquery = table;711 // // Process first Many relation - each one requires a nested subquery712 // const manyRelation = manyRelations[0];713 // if (manyRelation) {714 // const {715 // tsKey: selectedRelationTsKey,716 // queryConfig: selectedRelationQueryConfig,717 // relation,718 // } = manyRelation;719 // distinct = {720 // on: tableConfig.primaryKey.map((c) => aliasedTableColumn(c as GelColumn, tableAlias)),721 // };722 // const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);723 // const relationTableName = relation.referencedTable[Table.Symbol.Name];724 // const relationTableTsName = tableNamesMap[relationTableName]!;725 // const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;726 // const joinOn = and(727 // ...normalizedRelation.fields.map((field, i) =>728 // eq(729 // aliasedTableColumn(normalizedRelation.references[i]!, relationTableAlias),730 // aliasedTableColumn(field, tableAlias),731 // )732 // ),733 // );734 // const builtRelationJoin = this.buildRelationalQueryWithPK({735 // fullSchema,736 // schema,737 // tableNamesMap,738 // table: fullSchema[relationTableTsName] as GelTable,739 // tableConfig: schema[relationTableTsName]!,740 // queryConfig: selectedRelationQueryConfig,741 // tableAlias: relationTableAlias,742 // joinOn,743 // });744 // const builtRelationSelectionField = sql`case when ${745 // sql.identifier(relationTableAlias)746 // } is null then '[]' else json_agg(json_build_array(${747 // sql.join(748 // builtRelationJoin.selection.map(({ field }) =>749 // is(field, SQL.Aliased)750 // ? sql`${sql.identifier(relationTableAlias)}.${sql.identifier(field.fieldAlias)}`751 // : is(field, Column)752 // ? aliasedTableColumn(field, relationTableAlias)753 // : field754 // ),755 // sql`, `,756 // )757 // })) over (partition by ${sql.join(distinct.on, sql`, `)}) end`.as(selectedRelationTsKey);758 // const isLateralJoin = is(builtRelationJoin.sql, SQL);759 // joins.push({760 // on: isLateralJoin ? sql`true` : joinOn,761 // table: isLateralJoin762 // ? new Subquery(builtRelationJoin.sql as SQL, {}, relationTableAlias)763 // : aliasedTable(builtRelationJoin.sql as GelTable, relationTableAlias),764 // alias: relationTableAlias,765 // joinType: 'left',766 // lateral: isLateralJoin,767 // });768 // // Build the "from" subquery with the remaining Many relations769 // const builtTableFrom = this.buildRelationalQueryWithPK({770 // fullSchema,771 // schema,772 // tableNamesMap,773 // table,774 // tableConfig,775 // queryConfig: {776 // ...config,777 // where: undefined,778 // orderBy: undefined,779 // limit: undefined,780 // offset: undefined,781 // with: manyRelations.slice(1).reduce<NonNullable<typeof config['with']>>(782 // (result, { tsKey, queryConfig: configValue }) => {783 // result[tsKey] = configValue;784 // return result;785 // },786 // {},787 // ),788 // },789 // tableAlias,790 // });791 // selectedRelations.push({792 // dbKey: selectedRelationTsKey,793 // tsKey: selectedRelationTsKey,794 // field: builtRelationSelectionField,795 // relationTableTsKey: relationTableTsName,796 // isJson: true,797 // selection: builtRelationJoin.selection,798 // });799 // // selection = builtTableFrom.selection.map((item) =>800 // // is(item.field, SQL.Aliased)801 // // ? { ...item, field: sql`${sql.identifier(tableAlias)}.${sql.identifier(item.field.fieldAlias)}` }802 // // : item803 // // );804 // // selectionForBuild = [{805 // // dbKey: '*',806 // // tsKey: '*',807 // // field: sql`${sql.identifier(tableAlias)}.*`,808 // // selection: [],809 // // isJson: false,810 // // relationTableTsKey: undefined,811 // // }];812 // // const newSelectionItem: (typeof selection)[number] = {813 // // dbKey: selectedRelationTsKey,814 // // tsKey: selectedRelationTsKey,815 // // field,816 // // relationTableTsKey: relationTableTsName,817 // // isJson: true,818 // // selection: builtRelationJoin.selection,819 // // };820 // // selection.push(newSelectionItem);821 // // selectionForBuild.push(newSelectionItem);822 // tableFrom = is(builtTableFrom.sql, GelTable)823 // ? builtTableFrom.sql824 // : new Subquery(builtTableFrom.sql, {}, tableAlias);825 // }826 // if (selectedColumns.length === 0 && selectedRelations.length === 0 && selectedExtras.length === 0) {827 // throw new DrizzleError(`No fields selected for table "${tableConfig.tsName}" ("${tableAlias}")`);828 // }829 // let selection: BuildRelationalQueryResult<GelTable, GelColumn>['selection'];830 // function prepareSelectedColumns() {831 // return selectedColumns.map((key) => ({832 // dbKey: tableConfig.columns[key]!.name,833 // tsKey: key,834 // field: tableConfig.columns[key] as GelColumn,835 // relationTableTsKey: undefined,836 // isJson: false,837 // selection: [],838 // }));839 // }840 // function prepareSelectedExtras() {841 // return selectedExtras.map((item) => ({842 // dbKey: item.value.fieldAlias,843 // tsKey: item.tsKey,844 // field: item.value,845 // relationTableTsKey: undefined,846 // isJson: false,847 // selection: [],848 // }));849 // }850 // if (isRoot) {851 // selection = [852 // ...prepareSelectedColumns(),853 // ...prepareSelectedExtras(),854 // ];855 // }856 // if (hasUserDefinedWhere || orderBy.length > 0) {857 // tableFrom = new Subquery(858 // this.buildSelectQuery({859 // table: is(tableFrom, GelTable) ? aliasedTable(tableFrom, tableAlias) : tableFrom,860 // fields: {},861 // fieldsFlat: selectionForBuild.map(({ field }) => ({862 // path: [],863 // field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field,864 // })),865 // joins,866 // distinct,867 // }),868 // {},869 // tableAlias,870 // );871 // selectionForBuild = selection.map((item) =>872 // is(item.field, SQL.Aliased)873 // ? { ...item, field: sql`${sql.identifier(tableAlias)}.${sql.identifier(item.field.fieldAlias)}` }874 // : item875 // );876 // joins = [];877 // distinct = undefined;878 // }879 // const result = this.buildSelectQuery({880 // table: is(tableFrom, GelTable) ? aliasedTable(tableFrom, tableAlias) : tableFrom,881 // fields: {},882 // fieldsFlat: selectionForBuild.map(({ field }) => ({883 // path: [],884 // field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field,885 // })),886 // where,887 // limit,888 // offset,889 // joins,890 // orderBy,891 // distinct,892 // });893 // return {894 // tableTsKey: tableConfig.tsName,895 // sql: result,896 // selection,897 // };898 // }899 buildRelationalQueryWithoutPK({900 fullSchema,901 schema,902 tableNamesMap,903 table,904 tableConfig,905 queryConfig: config,906 tableAlias,907 nestedQueryRelation,908 joinOn909 }) {910 let selection = [];911 let limit, offset, orderBy = [], where;912 const joins = [];913 if (config === true) {914 const selectionEntries = Object.entries(tableConfig.columns);915 selection = selectionEntries.map(([key, value]) => ({916 dbKey: value.name,917 tsKey: key,918 field: aliasedTableColumn(value, tableAlias),919 relationTableTsKey: void 0,920 isJson: false,921 selection: []922 }));923 } else {924 const aliasedColumns = Object.fromEntries(925 Object.entries(tableConfig.columns).map(([key, value]) => [key, aliasedTableColumn(value, tableAlias)])926 );927 if (config.where) {928 const whereSql = typeof config.where === "function" ? config.where(aliasedColumns, getOperators()) : config.where;929 where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);930 }931 const fieldsSelection = [];932 let selectedColumns = [];933 if (config.columns) {934 let isIncludeMode = false;935 for (const [field, value] of Object.entries(config.columns)) {936 if (value === void 0) {937 continue;938 }939 if (field in tableConfig.columns) {940 if (!isIncludeMode && value === true) {941 isIncludeMode = true;942 }943 selectedColumns.push(field);944 }945 }946 if (selectedColumns.length > 0) {947 selectedColumns = isIncludeMode ? selectedColumns.filter((c) => config.columns?.[c] === true) : Object.keys(tableConfig.columns).filter((key) => !selectedColumns.includes(key));948 }949 } else {950 selectedColumns = Object.keys(tableConfig.columns);951 }952 for (const field of selectedColumns) {953 const column = tableConfig.columns[field];954 fieldsSelection.push({ tsKey: field, value: column });955 }956 let selectedRelations = [];957 if (config.with) {958 selectedRelations = Object.entries(config.with).filter((entry) => !!entry[1]).map(([tsKey, queryConfig]) => ({ tsKey, queryConfig, relation: tableConfig.relations[tsKey] }));959 }960 let extras;961 if (config.extras) {962 extras = typeof config.extras === "function" ? config.extras(aliasedColumns, { sql }) : config.extras;963 for (const [tsKey, value] of Object.entries(extras)) {964 fieldsSelection.push({965 tsKey,966 value: mapColumnsInAliasedSQLToAlias(value, tableAlias)967 });968 }969 }970 for (const { tsKey, value } of fieldsSelection) {971 selection.push({972 dbKey: is(value, SQL.Aliased) ? value.fieldAlias : tableConfig.columns[tsKey].name,973 tsKey,974 field: is(value, Column) ? aliasedTableColumn(value, tableAlias) : value,975 relationTableTsKey: void 0,976 isJson: false,977 selection: []978 });979 }980 let orderByOrig = typeof config.orderBy === "function" ? config.orderBy(aliasedColumns, getOrderByOperators()) : config.orderBy ?? [];981 if (!Array.isArray(orderByOrig)) {982 orderByOrig = [orderByOrig];983 }984 orderBy = orderByOrig.map((orderByValue) => {985 if (is(orderByValue, Column)) {986 return aliasedTableColumn(orderByValue, tableAlias);987 }988 return mapColumnsInSQLToAlias(orderByValue, tableAlias);989 });990 limit = config.limit;991 offset = config.offset;992 for (const {993 tsKey: selectedRelationTsKey,994 queryConfig: selectedRelationConfigValue,995 relation996 } of selectedRelations) {997 const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);998 const relationTableName = getTableUniqueName(relation.referencedTable);999 const relationTableTsName = tableNamesMap[relationTableName];1000 const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;1001 const joinOn2 = and(1002 ...normalizedRelation.fields.map(1003 (field2, i) => eq(1004 aliasedTableColumn(normalizedRelation.references[i], relationTableAlias),1005 aliasedTableColumn(field2, tableAlias)1006 )1007 )1008 );1009 const builtRelation = this.buildRelationalQueryWithoutPK({1010 fullSchema,1011 schema,1012 tableNamesMap,1013 table: fullSchema[relationTableTsName],1014 tableConfig: schema[relationTableTsName],1015 queryConfig: is(relation, One) ? selectedRelationConfigValue === true ? { limit: 1 } : { ...selectedRelationConfigValue, limit: 1 } : selectedRelationConfigValue,1016 tableAlias: relationTableAlias,1017 joinOn: joinOn2,1018 nestedQueryRelation: relation1019 });1020 const field = sql`${sql.identifier(relationTableAlias)}.${sql.identifier("data")}`.as(selectedRelationTsKey);1021 joins.push({1022 on: sql`true`,1023 table: new Subquery(builtRelation.sql, {}, relationTableAlias),1024 alias: relationTableAlias,1025 joinType: "left",1026 lateral: true1027 });1028 selection.push({1029 dbKey: selectedRelationTsKey,1030 tsKey: selectedRelationTsKey,1031 field,1032 relationTableTsKey: relationTableTsName,1033 isJson: true,1034 selection: builtRelation.selection1035 });1036 }1037 }1038 if (selection.length === 0) {1039 throw new DrizzleError({ message: `No fields selected for table "${tableConfig.tsName}" ("${tableAlias}")` });1040 }1041 let result;1042 where = and(joinOn, where);1043 if (nestedQueryRelation) {1044 let field = sql`json_build_array(${sql.join(1045 selection.map(1046 ({ field: field2, tsKey, isJson }) => isJson ? sql`${sql.identifier(`${tableAlias}_${tsKey}`)}.${sql.identifier("data")}` : is(field2, SQL.Aliased) ? field2.sql : field21047 ),1048 sql`, `1049 )})`;1050 if (is(nestedQueryRelation, Many)) {1051 field = sql`coalesce(json_agg(${field}${orderBy.length > 0 ? sql` order by ${sql.join(orderBy, sql`, `)}` : void 0}), '[]'::json)`;1052 }1053 const nestedSelection = [{1054 dbKey: "data",1055 tsKey: "data",1056 field: field.as("data"),1057 isJson: true,1058 relationTableTsKey: tableConfig.tsName,1059 selection1060 }];1061 const needsSubquery = limit !== void 0 || offset !== void 0 || orderBy.length > 0;1062 if (needsSubquery) {1063 result = this.buildSelectQuery({1064 table: aliasedTable(table, tableAlias),1065 fields: {},1066 fieldsFlat: [{1067 path: [],1068 field: sql.raw("*")1069 }],1070 where,1071 limit,1072 offset,1073 orderBy,1074 setOperators: []1075 });1076 where = void 0;1077 limit = void 0;1078 offset = void 0;1079 orderBy = [];1080 } else {1081 result = aliasedTable(table, tableAlias);1082 }1083 result = this.buildSelectQuery({1084 table: is(result, GelTable) ? result : new Subquery(result, {}, tableAlias),1085 fields: {},1086 fieldsFlat: nestedSelection.map(({ field: field2 }) => ({1087 path: [],1088 field: is(field2, Column) ? aliasedTableColumn(field2, tableAlias) : field21089 })),1090 joins,1091 where,1092 limit,1093 offset,1094 orderBy,1095 setOperators: []1096 });1097 } else {1098 result = this.buildSelectQuery({1099 table: aliasedTable(table, tableAlias),1100 fields: {},1101 fieldsFlat: selection.map(({ field }) => ({1102 path: [],1103 field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field1104 })),1105 joins,1106 where,1107 limit,1108 offset,1109 orderBy,1110 setOperators: []1111 });1112 }1113 return {1114 tableTsKey: tableConfig.tsName,1115 sql: result,1116 selection1117 };1118 }1119}1120export {1121 GelDialect1122};1123//# sourceMappingURL=dialect.js.map