drizzle-databend 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/columns.d.ts +25 -37
- package/dist/databend-core/alias.d.ts +1 -0
- package/dist/databend-core/columns/all.d.ts +35 -0
- package/dist/databend-core/columns/array.d.ts +16 -0
- package/dist/databend-core/columns/bigint.d.ts +24 -0
- package/dist/databend-core/columns/binary.d.ts +13 -0
- package/dist/databend-core/columns/bitmap.d.ts +13 -0
- package/dist/databend-core/columns/boolean.d.ts +13 -0
- package/dist/databend-core/columns/common.d.ts +45 -0
- package/dist/databend-core/columns/custom.d.ts +33 -0
- package/dist/databend-core/columns/date.d.ts +16 -0
- package/dist/databend-core/columns/decimal.d.ts +19 -0
- package/dist/databend-core/columns/double.d.ts +14 -0
- package/dist/databend-core/columns/float.d.ts +14 -0
- package/dist/databend-core/columns/index.d.ts +21 -0
- package/dist/databend-core/columns/integer.d.ts +13 -0
- package/dist/databend-core/columns/map.d.ts +17 -0
- package/dist/databend-core/columns/smallint.d.ts +13 -0
- package/dist/databend-core/columns/text.d.ts +13 -0
- package/dist/databend-core/columns/timestamp.d.ts +16 -0
- package/dist/databend-core/columns/tinyint.d.ts +13 -0
- package/dist/databend-core/columns/tuple.d.ts +16 -0
- package/dist/databend-core/columns/varchar.d.ts +17 -0
- package/dist/databend-core/columns/variant.d.ts +15 -0
- package/dist/databend-core/db.d.ts +31 -0
- package/dist/databend-core/dialect.d.ts +29 -0
- package/dist/databend-core/index.d.ts +18 -0
- package/dist/databend-core/indexes.d.ts +24 -0
- package/dist/databend-core/primary-keys.d.ts +20 -0
- package/dist/databend-core/query-builders/count.d.ts +18 -0
- package/dist/databend-core/query-builders/delete.d.ts +18 -0
- package/dist/databend-core/query-builders/index.d.ts +5 -0
- package/dist/databend-core/query-builders/insert.d.ts +27 -0
- package/dist/databend-core/query-builders/query-builder.d.ts +19 -0
- package/dist/databend-core/query-builders/query.d.ts +37 -0
- package/dist/databend-core/query-builders/raw.d.ts +17 -0
- package/dist/databend-core/query-builders/select.d.ts +66 -0
- package/dist/databend-core/query-builders/update.d.ts +27 -0
- package/dist/databend-core/schema.d.ts +12 -0
- package/dist/databend-core/session.d.ts +28 -0
- package/dist/databend-core/subquery.d.ts +1 -0
- package/dist/databend-core/table.d.ts +12 -0
- package/dist/databend-core/utils.d.ts +7 -0
- package/dist/databend-core/view-base.d.ts +5 -0
- package/dist/databend-core/view-common.d.ts +1 -0
- package/dist/databend-core/view.d.ts +30 -0
- package/dist/dialect.d.ts +1 -10
- package/dist/driver.d.ts +4 -4
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +2611 -205
- package/dist/session.d.ts +22 -19
- package/dist/sql/result-mapper.d.ts +2 -3
- package/dist/sql/selection.d.ts +2 -1
- package/package.json +4 -4
- package/src/columns.ts +8 -7
- package/src/databend-core/alias.ts +5 -0
- package/src/databend-core/columns/all.ts +38 -0
- package/src/databend-core/columns/array.ts +46 -0
- package/src/databend-core/columns/bigint.ts +52 -0
- package/src/databend-core/columns/binary.ts +27 -0
- package/src/databend-core/columns/bitmap.ts +27 -0
- package/src/databend-core/columns/boolean.ts +27 -0
- package/src/databend-core/columns/common.ts +97 -0
- package/src/databend-core/columns/custom.ts +86 -0
- package/src/databend-core/columns/date.ts +49 -0
- package/src/databend-core/columns/decimal.ts +44 -0
- package/src/databend-core/columns/double.ts +34 -0
- package/src/databend-core/columns/float.ts +31 -0
- package/src/databend-core/columns/index.ts +21 -0
- package/src/databend-core/columns/integer.ts +27 -0
- package/src/databend-core/columns/map.ts +49 -0
- package/src/databend-core/columns/smallint.ts +27 -0
- package/src/databend-core/columns/text.ts +27 -0
- package/src/databend-core/columns/timestamp.ts +51 -0
- package/src/databend-core/columns/tinyint.ts +27 -0
- package/src/databend-core/columns/tuple.ts +46 -0
- package/src/databend-core/columns/varchar.ts +35 -0
- package/src/databend-core/columns/variant.ts +45 -0
- package/src/databend-core/db.ts +153 -0
- package/src/databend-core/dialect.ts +725 -0
- package/src/databend-core/index.ts +18 -0
- package/src/databend-core/indexes.ts +67 -0
- package/src/databend-core/primary-keys.ts +48 -0
- package/src/databend-core/query-builders/count.ts +47 -0
- package/src/databend-core/query-builders/delete.ts +56 -0
- package/src/databend-core/query-builders/index.ts +5 -0
- package/src/databend-core/query-builders/insert.ts +105 -0
- package/src/databend-core/query-builders/query-builder.ts +77 -0
- package/src/databend-core/query-builders/query.ts +124 -0
- package/src/databend-core/query-builders/raw.ts +37 -0
- package/src/databend-core/query-builders/select.ts +412 -0
- package/src/databend-core/query-builders/update.ts +82 -0
- package/src/databend-core/schema.ts +29 -0
- package/src/databend-core/session.ts +85 -0
- package/src/databend-core/subquery.ts +1 -0
- package/src/databend-core/table.ts +67 -0
- package/src/databend-core/utils.ts +34 -0
- package/src/databend-core/view-base.ts +6 -0
- package/src/databend-core/view-common.ts +1 -0
- package/src/databend-core/view.ts +127 -0
- package/src/dialect.ts +3 -119
- package/src/driver.ts +6 -7
- package/src/index.ts +27 -0
- package/src/migrator.ts +1 -2
- package/src/session.ts +42 -57
- package/src/sql/result-mapper.ts +12 -54
- package/src/sql/selection.ts +2 -1
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { aliasedTable, aliasedTableColumn, mapColumnsInAliasedSQLToAlias, mapColumnsInSQLToAlias } from 'drizzle-orm/alias';
|
|
2
|
+
import { CasingCache } from 'drizzle-orm/casing';
|
|
3
|
+
import { Column } from 'drizzle-orm/column';
|
|
4
|
+
import { entityKind, is } from 'drizzle-orm/entity';
|
|
5
|
+
import { DrizzleError } from 'drizzle-orm/errors';
|
|
6
|
+
import type { MigrationConfig, MigrationMeta } from 'drizzle-orm/migrator';
|
|
7
|
+
import {
|
|
8
|
+
getOperators,
|
|
9
|
+
getOrderByOperators,
|
|
10
|
+
Many,
|
|
11
|
+
normalizeRelation,
|
|
12
|
+
One,
|
|
13
|
+
} from 'drizzle-orm/relations';
|
|
14
|
+
import { and, eq, View } from 'drizzle-orm/sql';
|
|
15
|
+
import { Param, SQL, sql } from 'drizzle-orm/sql/sql';
|
|
16
|
+
import { Subquery } from 'drizzle-orm/subquery';
|
|
17
|
+
import { getTableName, getTableUniqueName, Table } from 'drizzle-orm/table';
|
|
18
|
+
// @ts-expect-error - orderSelectedFields is exported at runtime but not in .d.ts
|
|
19
|
+
import { orderSelectedFields } from 'drizzle-orm/utils';
|
|
20
|
+
import { ViewBaseConfig } from 'drizzle-orm/view-common';
|
|
21
|
+
import { DatabendColumn, DatabendDecimal } from './columns/index.ts';
|
|
22
|
+
import { DatabendTimestamp } from './columns/timestamp.ts';
|
|
23
|
+
import { DatabendVariant } from './columns/variant.ts';
|
|
24
|
+
import { DatabendTable } from './table.ts';
|
|
25
|
+
import { DatabendViewBase } from './view-base.ts';
|
|
26
|
+
|
|
27
|
+
export class DatabendDialect {
|
|
28
|
+
static readonly [entityKind]: string = 'DatabendDialect';
|
|
29
|
+
|
|
30
|
+
/** @internal */
|
|
31
|
+
casing: any;
|
|
32
|
+
|
|
33
|
+
constructor(config?: any) {
|
|
34
|
+
this.casing = new CasingCache(config?.casing);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async migrate(
|
|
38
|
+
migrations: MigrationMeta[],
|
|
39
|
+
session: any,
|
|
40
|
+
config: MigrationConfig | string
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
const migrationConfig: MigrationConfig =
|
|
43
|
+
typeof config === 'string' ? { migrationsFolder: config } : config;
|
|
44
|
+
|
|
45
|
+
const migrationsSchema = migrationConfig.migrationsSchema ?? 'default';
|
|
46
|
+
const migrationsTable =
|
|
47
|
+
migrationConfig.migrationsTable ?? '__drizzle_migrations';
|
|
48
|
+
|
|
49
|
+
const migrationTableCreate = sql`
|
|
50
|
+
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsSchema)}.${sql.identifier(
|
|
51
|
+
migrationsTable
|
|
52
|
+
)} (
|
|
53
|
+
id INT NOT NULL,
|
|
54
|
+
hash VARCHAR NOT NULL,
|
|
55
|
+
created_at BIGINT
|
|
56
|
+
)
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
await session.execute(migrationTableCreate);
|
|
60
|
+
|
|
61
|
+
const dbMigrations = await session.all(
|
|
62
|
+
sql`SELECT id, hash, created_at FROM ${sql.identifier(
|
|
63
|
+
migrationsSchema
|
|
64
|
+
)}.${sql.identifier(migrationsTable)} ORDER BY created_at DESC LIMIT 1`
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const lastDbMigration = dbMigrations[0] as any;
|
|
68
|
+
|
|
69
|
+
await session.transaction(async (tx: any) => {
|
|
70
|
+
for await (const migration of migrations) {
|
|
71
|
+
if (
|
|
72
|
+
!lastDbMigration ||
|
|
73
|
+
Number(lastDbMigration.created_at) < migration.folderMillis
|
|
74
|
+
) {
|
|
75
|
+
for (const stmt of migration.sql) {
|
|
76
|
+
await tx.execute(sql.raw(stmt));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await tx.execute(
|
|
80
|
+
sql`INSERT INTO ${sql.identifier(
|
|
81
|
+
migrationsSchema
|
|
82
|
+
)}.${sql.identifier(migrationsTable)} (id, hash, created_at)
|
|
83
|
+
VALUES (
|
|
84
|
+
(SELECT COALESCE(MAX(id), 0) + 1 FROM ${sql.identifier(
|
|
85
|
+
migrationsSchema
|
|
86
|
+
)}.${sql.identifier(migrationsTable)}),
|
|
87
|
+
${migration.hash},
|
|
88
|
+
${migration.folderMillis}
|
|
89
|
+
)`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
escapeName(name: string): string {
|
|
97
|
+
return `"${name}"`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
escapeParam(num: number): string {
|
|
101
|
+
return `$${num + 1}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
escapeString(str: string): string {
|
|
105
|
+
return `'${str.replace(/'/g, "''")}'`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
buildWithCTE(queries: any[] | undefined) {
|
|
109
|
+
if (!queries?.length) return undefined;
|
|
110
|
+
const withSqlChunks = [sql`with `];
|
|
111
|
+
for (const [i, w] of queries.entries()) {
|
|
112
|
+
withSqlChunks.push(sql`${sql.identifier(w._.alias)} as (${w._.sql})`);
|
|
113
|
+
if (i < queries.length - 1) {
|
|
114
|
+
withSqlChunks.push(sql`, `);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
withSqlChunks.push(sql` `);
|
|
118
|
+
return sql.join(withSqlChunks);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
buildDeleteQuery({ table, where, withList }: any) {
|
|
122
|
+
const withSql = this.buildWithCTE(withList);
|
|
123
|
+
const whereSql = where ? sql` where ${where}` : undefined;
|
|
124
|
+
return sql`${withSql}delete from ${table}${whereSql}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
buildUpdateSet(table: any, set: any) {
|
|
128
|
+
const tableColumns = table[(Table as any).Symbol.Columns];
|
|
129
|
+
const columnNames = Object.keys(tableColumns).filter(
|
|
130
|
+
(colName) => set[colName] !== undefined || tableColumns[colName]?.onUpdateFn !== undefined
|
|
131
|
+
);
|
|
132
|
+
const setSize = columnNames.length;
|
|
133
|
+
return sql.join(
|
|
134
|
+
columnNames.flatMap((colName, i) => {
|
|
135
|
+
const col = tableColumns[colName];
|
|
136
|
+
const value = set[colName] ?? sql.param(col.onUpdateFn(), col);
|
|
137
|
+
const res = sql`${sql.identifier(this.casing.getColumnCasing(col))} = ${value}`;
|
|
138
|
+
if (i < setSize - 1) {
|
|
139
|
+
return [res, sql.raw(', ')];
|
|
140
|
+
}
|
|
141
|
+
return [res];
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
buildUpdateQuery({ table, set, where, withList }: any) {
|
|
147
|
+
const withSql = this.buildWithCTE(withList);
|
|
148
|
+
const tableName = (table as any)[(DatabendTable as any).Symbol.Name];
|
|
149
|
+
const tableSchema = (table as any)[(DatabendTable as any).Symbol.Schema];
|
|
150
|
+
const origTableName = (table as any)[(DatabendTable as any).Symbol.OriginalName];
|
|
151
|
+
const alias = tableName === origTableName ? undefined : tableName;
|
|
152
|
+
const tableSql = sql`${tableSchema ? sql`${sql.identifier(tableSchema)}.` : undefined}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`}`;
|
|
153
|
+
const setSql = this.buildUpdateSet(table, set);
|
|
154
|
+
const whereSql = where ? sql` where ${where}` : undefined;
|
|
155
|
+
return sql`${withSql}update ${tableSql} set ${setSql}${whereSql}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
buildSelection(fields: any[], { isSingleTable = false } = {}) {
|
|
159
|
+
const columnsLen = fields.length;
|
|
160
|
+
const chunks = fields.flatMap(({ field }: any, i: number) => {
|
|
161
|
+
const chunk: any[] = [];
|
|
162
|
+
if (is(field, SQL.Aliased) && (field as any).isSelectionField) {
|
|
163
|
+
chunk.push(sql.identifier(field.fieldAlias));
|
|
164
|
+
} else if (is(field, SQL.Aliased) || is(field, SQL)) {
|
|
165
|
+
const query = is(field, SQL.Aliased) ? field.sql : field;
|
|
166
|
+
if (isSingleTable) {
|
|
167
|
+
chunk.push(
|
|
168
|
+
new SQL(
|
|
169
|
+
query.queryChunks.map((c: any) => {
|
|
170
|
+
if (is(c, DatabendColumn)) {
|
|
171
|
+
return sql.identifier(this.casing.getColumnCasing(c));
|
|
172
|
+
}
|
|
173
|
+
return c;
|
|
174
|
+
})
|
|
175
|
+
)
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
chunk.push(query);
|
|
179
|
+
}
|
|
180
|
+
if (is(field, SQL.Aliased)) {
|
|
181
|
+
chunk.push(sql` as ${sql.identifier(field.fieldAlias)}`);
|
|
182
|
+
}
|
|
183
|
+
} else if (is(field, Column)) {
|
|
184
|
+
if (isSingleTable) {
|
|
185
|
+
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
|
|
186
|
+
} else {
|
|
187
|
+
chunk.push(field);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (i < columnsLen - 1) {
|
|
191
|
+
chunk.push(sql`, `);
|
|
192
|
+
}
|
|
193
|
+
return chunk;
|
|
194
|
+
});
|
|
195
|
+
return sql.join(chunks);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
buildJoins(joins: any[] | undefined) {
|
|
199
|
+
if (!joins || joins.length === 0) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
const joinsArray: any[] = [];
|
|
203
|
+
for (const [index, joinMeta] of joins.entries()) {
|
|
204
|
+
if (index === 0) {
|
|
205
|
+
joinsArray.push(sql` `);
|
|
206
|
+
}
|
|
207
|
+
const table = joinMeta.table;
|
|
208
|
+
const lateralSql = joinMeta.lateral ? sql` lateral` : undefined;
|
|
209
|
+
const onClause = joinMeta.on ? sql` on ${joinMeta.on}` : undefined;
|
|
210
|
+
if (is(table, DatabendTable)) {
|
|
211
|
+
const t = table as any;
|
|
212
|
+
const tableName = t[(DatabendTable as any).Symbol.Name];
|
|
213
|
+
const tableSchema = t[(DatabendTable as any).Symbol.Schema];
|
|
214
|
+
const origTableName = t[(DatabendTable as any).Symbol.OriginalName];
|
|
215
|
+
const alias = tableName === origTableName ? undefined : joinMeta.alias;
|
|
216
|
+
joinsArray.push(
|
|
217
|
+
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${tableSchema ? sql`${sql.identifier(tableSchema)}.` : undefined}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`}${onClause}`
|
|
218
|
+
);
|
|
219
|
+
} else if (is(table, View)) {
|
|
220
|
+
const viewName = (table as any)[ViewBaseConfig].name;
|
|
221
|
+
const viewSchema = (table as any)[ViewBaseConfig].schema;
|
|
222
|
+
const origViewName = (table as any)[ViewBaseConfig].originalName;
|
|
223
|
+
const alias = viewName === origViewName ? undefined : joinMeta.alias;
|
|
224
|
+
joinsArray.push(
|
|
225
|
+
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${viewSchema ? sql`${sql.identifier(viewSchema)}.` : undefined}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`}${onClause}`
|
|
226
|
+
);
|
|
227
|
+
} else {
|
|
228
|
+
joinsArray.push(
|
|
229
|
+
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table}${onClause}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (index < joins.length - 1) {
|
|
233
|
+
joinsArray.push(sql` `);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return sql.join(joinsArray);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
buildFromTable(table: any) {
|
|
240
|
+
if (is(table, Table)) {
|
|
241
|
+
const t = table as any;
|
|
242
|
+
if (t[(Table as any).Symbol.OriginalName] !== t[(Table as any).Symbol.Name]) {
|
|
243
|
+
let fullName = sql`${sql.identifier(t[(Table as any).Symbol.OriginalName])}`;
|
|
244
|
+
if (t[(Table as any).Symbol.Schema]) {
|
|
245
|
+
fullName = sql`${sql.identifier(t[(Table as any).Symbol.Schema])}.${fullName}`;
|
|
246
|
+
}
|
|
247
|
+
return sql`${fullName} ${sql.identifier(t[(Table as any).Symbol.Name])}`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return table;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
buildSelectQuery({
|
|
254
|
+
withList,
|
|
255
|
+
fields,
|
|
256
|
+
fieldsFlat,
|
|
257
|
+
where,
|
|
258
|
+
having,
|
|
259
|
+
table,
|
|
260
|
+
joins,
|
|
261
|
+
orderBy,
|
|
262
|
+
groupBy,
|
|
263
|
+
limit,
|
|
264
|
+
offset,
|
|
265
|
+
distinct,
|
|
266
|
+
setOperators,
|
|
267
|
+
}: any) {
|
|
268
|
+
const fieldsList = fieldsFlat ?? orderSelectedFields(fields);
|
|
269
|
+
for (const f of fieldsList) {
|
|
270
|
+
if (
|
|
271
|
+
is(f.field, Column) &&
|
|
272
|
+
getTableName(f.field.table) !==
|
|
273
|
+
(is(table, Subquery)
|
|
274
|
+
? table._.alias
|
|
275
|
+
: is(table, DatabendViewBase)
|
|
276
|
+
? (table as any)[ViewBaseConfig].name
|
|
277
|
+
: is(table, SQL)
|
|
278
|
+
? undefined
|
|
279
|
+
: getTableName(table)) &&
|
|
280
|
+
!((table2: any) =>
|
|
281
|
+
joins?.some(
|
|
282
|
+
({ alias }: any) =>
|
|
283
|
+
alias ===
|
|
284
|
+
(table2[(Table as any).Symbol.IsAlias]
|
|
285
|
+
? getTableName(table2)
|
|
286
|
+
: table2[(Table as any).Symbol.BaseName])
|
|
287
|
+
))(f.field.table)
|
|
288
|
+
) {
|
|
289
|
+
const tableName = getTableName(f.field.table);
|
|
290
|
+
throw new Error(
|
|
291
|
+
`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?`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const isSingleTable = !joins || joins.length === 0;
|
|
297
|
+
const withSql = this.buildWithCTE(withList);
|
|
298
|
+
let distinctSql: SQL | undefined;
|
|
299
|
+
if (distinct) {
|
|
300
|
+
distinctSql = distinct === true ? sql` distinct` : sql` distinct on (${sql.join(distinct.on, sql`, `)})`;
|
|
301
|
+
}
|
|
302
|
+
const selection = this.buildSelection(fieldsList, { isSingleTable });
|
|
303
|
+
const tableSql = this.buildFromTable(table);
|
|
304
|
+
const joinsSql = this.buildJoins(joins);
|
|
305
|
+
const whereSql = where ? sql` where ${where}` : undefined;
|
|
306
|
+
const havingSql = having ? sql` having ${having}` : undefined;
|
|
307
|
+
let orderBySql: SQL | undefined;
|
|
308
|
+
if (orderBy && orderBy.length > 0) {
|
|
309
|
+
orderBySql = sql` order by ${sql.join(orderBy, sql`, `)}`;
|
|
310
|
+
}
|
|
311
|
+
let groupBySql: SQL | undefined;
|
|
312
|
+
if (groupBy && groupBy.length > 0) {
|
|
313
|
+
groupBySql = sql` group by ${sql.join(groupBy, sql`, `)}`;
|
|
314
|
+
}
|
|
315
|
+
const limitSql =
|
|
316
|
+
typeof limit === 'object' || (typeof limit === 'number' && limit >= 0)
|
|
317
|
+
? sql` limit ${limit}`
|
|
318
|
+
: undefined;
|
|
319
|
+
const offsetSql = offset ? sql` offset ${offset}` : undefined;
|
|
320
|
+
const finalQuery = sql`${withSql}select${distinctSql} ${selection} from ${tableSql}${joinsSql}${whereSql}${groupBySql}${havingSql}${orderBySql}${limitSql}${offsetSql}`;
|
|
321
|
+
if (setOperators.length > 0) {
|
|
322
|
+
return this.buildSetOperations(finalQuery, setOperators);
|
|
323
|
+
}
|
|
324
|
+
return finalQuery;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
buildSetOperations(leftSelect: any, setOperators: any[]): any {
|
|
328
|
+
const [setOperator, ...rest] = setOperators;
|
|
329
|
+
if (!setOperator) {
|
|
330
|
+
throw new Error('Cannot pass undefined values to any set operator');
|
|
331
|
+
}
|
|
332
|
+
if (rest.length === 0) {
|
|
333
|
+
return this.buildSetOperationQuery({ leftSelect, setOperator });
|
|
334
|
+
}
|
|
335
|
+
return this.buildSetOperations(
|
|
336
|
+
this.buildSetOperationQuery({ leftSelect, setOperator }),
|
|
337
|
+
rest
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
buildSetOperationQuery({
|
|
342
|
+
leftSelect,
|
|
343
|
+
setOperator: { type, isAll, rightSelect, limit, orderBy, offset },
|
|
344
|
+
}: any) {
|
|
345
|
+
const leftChunk = sql`(${leftSelect.getSQL()}) `;
|
|
346
|
+
const rightChunk = sql`(${rightSelect.getSQL()})`;
|
|
347
|
+
let orderBySql: SQL | undefined;
|
|
348
|
+
if (orderBy && orderBy.length > 0) {
|
|
349
|
+
const orderByValues: any[] = [];
|
|
350
|
+
for (const singleOrderBy of orderBy) {
|
|
351
|
+
if (is(singleOrderBy, DatabendColumn)) {
|
|
352
|
+
orderByValues.push(sql.identifier(singleOrderBy.name));
|
|
353
|
+
} else if (is(singleOrderBy, SQL)) {
|
|
354
|
+
for (let i = 0; i < singleOrderBy.queryChunks.length; i++) {
|
|
355
|
+
const chunk = singleOrderBy.queryChunks[i];
|
|
356
|
+
if (is(chunk, DatabendColumn)) {
|
|
357
|
+
singleOrderBy.queryChunks[i] = sql.identifier(chunk.name);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
orderByValues.push(sql`${singleOrderBy}`);
|
|
361
|
+
} else {
|
|
362
|
+
orderByValues.push(sql`${singleOrderBy}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
orderBySql = sql` order by ${sql.join(orderByValues, sql`, `)} `;
|
|
366
|
+
}
|
|
367
|
+
const limitSql =
|
|
368
|
+
typeof limit === 'object' || (typeof limit === 'number' && limit >= 0)
|
|
369
|
+
? sql` limit ${limit}`
|
|
370
|
+
: undefined;
|
|
371
|
+
const operatorChunk = sql.raw(`${type} ${isAll ? 'all ' : ''}`);
|
|
372
|
+
const offsetSql = offset ? sql` offset ${offset}` : undefined;
|
|
373
|
+
return sql`${leftChunk}${operatorChunk}${rightChunk}${orderBySql}${limitSql}${offsetSql}`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
buildInsertQuery({ table, values: valuesOrSelect, withList, select }: any) {
|
|
377
|
+
const valuesSqlList: any[] = [];
|
|
378
|
+
const columns = table[(Table as any).Symbol.Columns];
|
|
379
|
+
const colEntries = Object.entries(columns).filter(([_, col]: [string, any]) => !col.shouldDisableInsert());
|
|
380
|
+
const insertOrder = colEntries.map(
|
|
381
|
+
([, column]: [string, any]) => sql.identifier(this.casing.getColumnCasing(column))
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (select) {
|
|
385
|
+
const select2 = valuesOrSelect;
|
|
386
|
+
if (is(select2, SQL)) {
|
|
387
|
+
valuesSqlList.push(select2);
|
|
388
|
+
} else {
|
|
389
|
+
valuesSqlList.push(select2.getSQL());
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
const values = valuesOrSelect;
|
|
393
|
+
valuesSqlList.push(sql.raw('values '));
|
|
394
|
+
for (const [valueIndex, value] of values.entries()) {
|
|
395
|
+
const valueList: any[] = [];
|
|
396
|
+
for (const [fieldName, col] of colEntries) {
|
|
397
|
+
const colValue = value[fieldName];
|
|
398
|
+
if (colValue === undefined || (is(colValue, Param) && colValue.value === undefined)) {
|
|
399
|
+
if ((col as any).defaultFn !== undefined) {
|
|
400
|
+
const defaultFnResult = (col as any).defaultFn();
|
|
401
|
+
const defaultValue = is(defaultFnResult, SQL)
|
|
402
|
+
? defaultFnResult
|
|
403
|
+
: sql.param(defaultFnResult, col as any);
|
|
404
|
+
valueList.push(defaultValue);
|
|
405
|
+
} else if (!(col as any).default && (col as any).onUpdateFn !== undefined) {
|
|
406
|
+
const onUpdateFnResult = (col as any).onUpdateFn();
|
|
407
|
+
const newValue = is(onUpdateFnResult, SQL)
|
|
408
|
+
? onUpdateFnResult
|
|
409
|
+
: sql.param(onUpdateFnResult, col as any);
|
|
410
|
+
valueList.push(newValue);
|
|
411
|
+
} else {
|
|
412
|
+
valueList.push(sql`default`);
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
valueList.push(colValue);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
valuesSqlList.push(valueList);
|
|
419
|
+
if (valueIndex < values.length - 1) {
|
|
420
|
+
valuesSqlList.push(sql`, `);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const withSql = this.buildWithCTE(withList);
|
|
426
|
+
const valuesSql = sql.join(valuesSqlList);
|
|
427
|
+
return sql`${withSql}insert into ${table} ${insertOrder} ${valuesSql}`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
prepareTyping(encoder: any): string {
|
|
431
|
+
if (is(encoder, DatabendDecimal) || is(encoder, DatabendColumn)) {
|
|
432
|
+
const sqlType = encoder.getSQLType?.();
|
|
433
|
+
if (sqlType) {
|
|
434
|
+
const lower = sqlType.toLowerCase();
|
|
435
|
+
if (
|
|
436
|
+
lower === 'integer' || lower === 'int' ||
|
|
437
|
+
lower === 'smallint' || lower === 'tinyint' ||
|
|
438
|
+
lower === 'bigint' || lower === 'real' ||
|
|
439
|
+
lower === 'double' || lower === 'float' ||
|
|
440
|
+
lower.startsWith('decimal')
|
|
441
|
+
) {
|
|
442
|
+
return 'decimal';
|
|
443
|
+
}
|
|
444
|
+
if (lower === 'timestamp') {
|
|
445
|
+
return 'timestamp';
|
|
446
|
+
}
|
|
447
|
+
if (lower === 'date') {
|
|
448
|
+
return 'date';
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (is(encoder, DatabendTimestamp)) {
|
|
453
|
+
return 'timestamp';
|
|
454
|
+
}
|
|
455
|
+
if (is(encoder, DatabendVariant)) {
|
|
456
|
+
return 'none';
|
|
457
|
+
}
|
|
458
|
+
return 'none';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
sqlToQuery(sql2: any, invokeSource?: any) {
|
|
462
|
+
return sql2.toQuery({
|
|
463
|
+
casing: this.casing,
|
|
464
|
+
escapeName: this.escapeName,
|
|
465
|
+
escapeParam: this.escapeParam,
|
|
466
|
+
escapeString: this.escapeString,
|
|
467
|
+
prepareTyping: this.prepareTyping.bind(this),
|
|
468
|
+
invokeSource,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
buildRelationalQueryWithoutPK({
|
|
473
|
+
fullSchema,
|
|
474
|
+
schema,
|
|
475
|
+
tableNamesMap,
|
|
476
|
+
table,
|
|
477
|
+
tableConfig,
|
|
478
|
+
queryConfig: config,
|
|
479
|
+
tableAlias,
|
|
480
|
+
nestedQueryRelation,
|
|
481
|
+
joinOn,
|
|
482
|
+
}: any): any {
|
|
483
|
+
let selection: any[] = [];
|
|
484
|
+
let limit: any, offset: any, orderBy: any[] = [], where: any;
|
|
485
|
+
const joins: any[] = [];
|
|
486
|
+
|
|
487
|
+
if (config === true) {
|
|
488
|
+
const selectionEntries = Object.entries(tableConfig.columns);
|
|
489
|
+
selection = selectionEntries.map(([key, value]: [string, any]) => ({
|
|
490
|
+
dbKey: value.name,
|
|
491
|
+
tsKey: key,
|
|
492
|
+
field: aliasedTableColumn(value, tableAlias),
|
|
493
|
+
relationTableTsKey: undefined,
|
|
494
|
+
isJson: false,
|
|
495
|
+
selection: [],
|
|
496
|
+
}));
|
|
497
|
+
} else {
|
|
498
|
+
const aliasedColumns = Object.fromEntries(
|
|
499
|
+
Object.entries(tableConfig.columns).map(([key, value]: [string, any]) => [key, aliasedTableColumn(value, tableAlias)])
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
if (config.where) {
|
|
503
|
+
const whereSql = typeof config.where === 'function'
|
|
504
|
+
? config.where(aliasedColumns, getOperators())
|
|
505
|
+
: config.where;
|
|
506
|
+
where = whereSql && mapColumnsInSQLToAlias(whereSql, tableAlias);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const fieldsSelection: any[] = [];
|
|
510
|
+
let selectedColumns: string[] = [];
|
|
511
|
+
|
|
512
|
+
if (config.columns) {
|
|
513
|
+
let isIncludeMode = false;
|
|
514
|
+
for (const [field, value] of Object.entries(config.columns)) {
|
|
515
|
+
if (value === undefined) continue;
|
|
516
|
+
if (field in tableConfig.columns) {
|
|
517
|
+
if (!isIncludeMode && value === true) {
|
|
518
|
+
isIncludeMode = true;
|
|
519
|
+
}
|
|
520
|
+
selectedColumns.push(field);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (selectedColumns.length > 0) {
|
|
524
|
+
selectedColumns = isIncludeMode
|
|
525
|
+
? selectedColumns.filter((c) => (config.columns as any)?.[c] === true)
|
|
526
|
+
: Object.keys(tableConfig.columns).filter((key) => !selectedColumns.includes(key));
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
selectedColumns = Object.keys(tableConfig.columns);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
for (const field of selectedColumns) {
|
|
533
|
+
const column = tableConfig.columns[field];
|
|
534
|
+
fieldsSelection.push({ tsKey: field, value: column });
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
let selectedRelations: any[] = [];
|
|
538
|
+
if (config.with) {
|
|
539
|
+
selectedRelations = Object.entries(config.with)
|
|
540
|
+
.filter((entry: any) => !!entry[1])
|
|
541
|
+
.map(([tsKey, queryConfig]: [string, any]) => ({
|
|
542
|
+
tsKey,
|
|
543
|
+
queryConfig,
|
|
544
|
+
relation: tableConfig.relations[tsKey],
|
|
545
|
+
}));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (config.extras) {
|
|
549
|
+
const extras = typeof config.extras === 'function'
|
|
550
|
+
? config.extras(aliasedColumns, { sql })
|
|
551
|
+
: config.extras;
|
|
552
|
+
for (const [tsKey, value] of Object.entries(extras)) {
|
|
553
|
+
fieldsSelection.push({
|
|
554
|
+
tsKey,
|
|
555
|
+
value: mapColumnsInAliasedSQLToAlias(value as any, tableAlias),
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (const { tsKey, value } of fieldsSelection) {
|
|
561
|
+
selection.push({
|
|
562
|
+
dbKey: is(value, SQL.Aliased) ? value.fieldAlias : tableConfig.columns[tsKey].name,
|
|
563
|
+
tsKey,
|
|
564
|
+
field: is(value, Column) ? aliasedTableColumn(value, tableAlias) : value,
|
|
565
|
+
relationTableTsKey: undefined,
|
|
566
|
+
isJson: false,
|
|
567
|
+
selection: [],
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
let orderByOrig: any = typeof config.orderBy === 'function'
|
|
572
|
+
? config.orderBy(aliasedColumns, getOrderByOperators())
|
|
573
|
+
: config.orderBy ?? [];
|
|
574
|
+
if (!Array.isArray(orderByOrig)) {
|
|
575
|
+
orderByOrig = [orderByOrig];
|
|
576
|
+
}
|
|
577
|
+
orderBy = orderByOrig.map((orderByValue: any) => {
|
|
578
|
+
if (is(orderByValue, Column)) {
|
|
579
|
+
return aliasedTableColumn(orderByValue, tableAlias);
|
|
580
|
+
}
|
|
581
|
+
return mapColumnsInSQLToAlias(orderByValue, tableAlias);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
limit = config.limit;
|
|
585
|
+
offset = config.offset;
|
|
586
|
+
|
|
587
|
+
for (const {
|
|
588
|
+
tsKey: selectedRelationTsKey,
|
|
589
|
+
queryConfig: selectedRelationConfigValue,
|
|
590
|
+
relation,
|
|
591
|
+
} of selectedRelations) {
|
|
592
|
+
const normalizedRelation = normalizeRelation(schema, tableNamesMap, relation);
|
|
593
|
+
const relationTableName = getTableUniqueName(relation.referencedTable);
|
|
594
|
+
const relationTableTsName = tableNamesMap[relationTableName];
|
|
595
|
+
const relationTableAlias = `${tableAlias}_${selectedRelationTsKey}`;
|
|
596
|
+
const joinOn2 = and(
|
|
597
|
+
...normalizedRelation.fields.map(
|
|
598
|
+
(field2: any, i: number) =>
|
|
599
|
+
eq(
|
|
600
|
+
aliasedTableColumn(normalizedRelation.references[i], relationTableAlias),
|
|
601
|
+
aliasedTableColumn(field2, tableAlias)
|
|
602
|
+
)
|
|
603
|
+
)
|
|
604
|
+
);
|
|
605
|
+
const builtRelation = this.buildRelationalQueryWithoutPK({
|
|
606
|
+
fullSchema,
|
|
607
|
+
schema,
|
|
608
|
+
tableNamesMap,
|
|
609
|
+
table: fullSchema[relationTableTsName],
|
|
610
|
+
tableConfig: schema[relationTableTsName],
|
|
611
|
+
queryConfig: is(relation, One)
|
|
612
|
+
? selectedRelationConfigValue === true
|
|
613
|
+
? { limit: 1 }
|
|
614
|
+
: { ...selectedRelationConfigValue, limit: 1 }
|
|
615
|
+
: selectedRelationConfigValue,
|
|
616
|
+
tableAlias: relationTableAlias,
|
|
617
|
+
joinOn: joinOn2,
|
|
618
|
+
nestedQueryRelation: relation,
|
|
619
|
+
});
|
|
620
|
+
const field = sql`${sql.identifier(relationTableAlias)}.${sql.identifier('data')}`.as(selectedRelationTsKey);
|
|
621
|
+
joins.push({
|
|
622
|
+
on: sql`true`,
|
|
623
|
+
table: new Subquery(builtRelation.sql, {}, relationTableAlias),
|
|
624
|
+
alias: relationTableAlias,
|
|
625
|
+
joinType: 'left',
|
|
626
|
+
lateral: true,
|
|
627
|
+
});
|
|
628
|
+
selection.push({
|
|
629
|
+
dbKey: selectedRelationTsKey,
|
|
630
|
+
tsKey: selectedRelationTsKey,
|
|
631
|
+
field,
|
|
632
|
+
relationTableTsKey: relationTableTsName,
|
|
633
|
+
isJson: true,
|
|
634
|
+
selection: builtRelation.selection,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (selection.length === 0) {
|
|
640
|
+
throw new DrizzleError({ message: `No fields selected for table "${tableConfig.tsName}" ("${tableAlias}")` });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
let result: any;
|
|
644
|
+
where = and(joinOn, where);
|
|
645
|
+
|
|
646
|
+
if (nestedQueryRelation) {
|
|
647
|
+
let field: any = sql`json_build_array(${sql.join(
|
|
648
|
+
selection.map(
|
|
649
|
+
({ field: field2, tsKey, isJson }: any) =>
|
|
650
|
+
isJson
|
|
651
|
+
? sql`${sql.identifier(`${tableAlias}_${tsKey}`)}.${sql.identifier('data')}`
|
|
652
|
+
: is(field2, SQL.Aliased)
|
|
653
|
+
? field2.sql
|
|
654
|
+
: field2
|
|
655
|
+
),
|
|
656
|
+
sql`, `
|
|
657
|
+
)})`;
|
|
658
|
+
if (is(nestedQueryRelation, Many)) {
|
|
659
|
+
field = sql`coalesce(json_agg(${field}${orderBy.length > 0 ? sql` order by ${sql.join(orderBy, sql`, `)}` : undefined}), '[]'::json)`;
|
|
660
|
+
}
|
|
661
|
+
const nestedSelection = [{
|
|
662
|
+
dbKey: 'data',
|
|
663
|
+
tsKey: 'data',
|
|
664
|
+
field: field.as('data'),
|
|
665
|
+
isJson: true,
|
|
666
|
+
relationTableTsKey: tableConfig.tsName,
|
|
667
|
+
selection,
|
|
668
|
+
}];
|
|
669
|
+
const needsSubquery = limit !== undefined || offset !== undefined || orderBy.length > 0;
|
|
670
|
+
if (needsSubquery) {
|
|
671
|
+
result = this.buildSelectQuery({
|
|
672
|
+
table: aliasedTable(table, tableAlias),
|
|
673
|
+
fields: {},
|
|
674
|
+
fieldsFlat: [{ path: [], field: sql.raw('*') }],
|
|
675
|
+
where,
|
|
676
|
+
limit,
|
|
677
|
+
offset,
|
|
678
|
+
orderBy,
|
|
679
|
+
setOperators: [],
|
|
680
|
+
});
|
|
681
|
+
where = undefined;
|
|
682
|
+
limit = undefined;
|
|
683
|
+
offset = undefined;
|
|
684
|
+
orderBy = [];
|
|
685
|
+
} else {
|
|
686
|
+
result = aliasedTable(table, tableAlias);
|
|
687
|
+
}
|
|
688
|
+
result = this.buildSelectQuery({
|
|
689
|
+
table: is(result, DatabendTable) ? result : new Subquery(result, {}, tableAlias),
|
|
690
|
+
fields: {},
|
|
691
|
+
fieldsFlat: nestedSelection.map(({ field: field2 }: any) => ({
|
|
692
|
+
path: [],
|
|
693
|
+
field: is(field2, Column) ? aliasedTableColumn(field2, tableAlias) : field2,
|
|
694
|
+
})),
|
|
695
|
+
joins,
|
|
696
|
+
where,
|
|
697
|
+
limit,
|
|
698
|
+
offset,
|
|
699
|
+
orderBy,
|
|
700
|
+
setOperators: [],
|
|
701
|
+
});
|
|
702
|
+
} else {
|
|
703
|
+
result = this.buildSelectQuery({
|
|
704
|
+
table: aliasedTable(table, tableAlias),
|
|
705
|
+
fields: {},
|
|
706
|
+
fieldsFlat: selection.map(({ field }: any) => ({
|
|
707
|
+
path: [],
|
|
708
|
+
field: is(field, Column) ? aliasedTableColumn(field, tableAlias) : field,
|
|
709
|
+
})),
|
|
710
|
+
joins,
|
|
711
|
+
where,
|
|
712
|
+
limit,
|
|
713
|
+
offset,
|
|
714
|
+
orderBy,
|
|
715
|
+
setOperators: [],
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
tableTsKey: tableConfig.tsName,
|
|
721
|
+
sql: result,
|
|
722
|
+
selection,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
}
|