metal-orm 1.0.10 → 1.0.12

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.
Files changed (40) hide show
  1. package/README.md +17 -15
  2. package/dist/decorators/index.cjs +15 -2
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -1
  5. package/dist/decorators/index.d.ts +1 -1
  6. package/dist/decorators/index.js +15 -2
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +1394 -149
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +257 -23
  11. package/dist/index.d.ts +257 -23
  12. package/dist/index.js +1376 -149
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-654m4qy8.d.cts → select-BKlr2ivY.d.cts} +141 -4
  15. package/dist/{select-654m4qy8.d.ts → select-BKlr2ivY.d.ts} +141 -4
  16. package/package.json +1 -1
  17. package/src/core/ddl/dialects/base-schema-dialect.ts +48 -0
  18. package/src/core/ddl/dialects/index.ts +5 -0
  19. package/src/core/ddl/dialects/mssql-schema-dialect.ts +97 -0
  20. package/src/core/ddl/dialects/mysql-schema-dialect.ts +109 -0
  21. package/src/core/ddl/dialects/postgres-schema-dialect.ts +99 -0
  22. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +103 -0
  23. package/src/core/ddl/introspect/mssql.ts +149 -0
  24. package/src/core/ddl/introspect/mysql.ts +99 -0
  25. package/src/core/ddl/introspect/postgres.ts +154 -0
  26. package/src/core/ddl/introspect/sqlite.ts +66 -0
  27. package/src/core/ddl/introspect/types.ts +19 -0
  28. package/src/core/ddl/introspect/utils.ts +27 -0
  29. package/src/core/ddl/schema-diff.ts +179 -0
  30. package/src/core/ddl/schema-generator.ts +229 -0
  31. package/src/core/ddl/schema-introspect.ts +32 -0
  32. package/src/core/ddl/schema-types.ts +39 -0
  33. package/src/core/dialect/base/sql-dialect.ts +161 -0
  34. package/src/core/dialect/mysql/index.ts +18 -112
  35. package/src/core/dialect/postgres/index.ts +30 -126
  36. package/src/core/dialect/sqlite/index.ts +29 -129
  37. package/src/index.ts +15 -10
  38. package/src/schema/column.ts +206 -27
  39. package/src/schema/table.ts +89 -32
  40. package/src/schema/types.ts +8 -5
@@ -0,0 +1,32 @@
1
+ import { DialectName } from './schema-generator.js';
2
+ import { DatabaseSchema } from './schema-types.js';
3
+ import { DbExecutor } from '../../orm/db-executor.js';
4
+ import type { IntrospectOptions, SchemaIntrospector } from './introspect/types.js';
5
+ import { postgresIntrospector } from './introspect/postgres.js';
6
+ import { mysqlIntrospector } from './introspect/mysql.js';
7
+ import { sqliteIntrospector } from './introspect/sqlite.js';
8
+ import { mssqlIntrospector } from './introspect/mssql.js';
9
+
10
+ const INTROSPECTORS: Record<DialectName, SchemaIntrospector> = {
11
+ postgres: postgresIntrospector,
12
+ mysql: mysqlIntrospector,
13
+ sqlite: sqliteIntrospector,
14
+ mssql: mssqlIntrospector
15
+ };
16
+
17
+ /**
18
+ * Introspects an existing database schema using the dialect-specific strategy.
19
+ */
20
+ export const introspectSchema = async (
21
+ executor: DbExecutor,
22
+ dialect: DialectName,
23
+ options: IntrospectOptions = {}
24
+ ): Promise<DatabaseSchema> => {
25
+ const handler = INTROSPECTORS[dialect];
26
+ if (!handler) {
27
+ throw new Error(`Unsupported dialect for introspection: ${dialect}`);
28
+ }
29
+ return handler.introspect(executor, options);
30
+ };
31
+
32
+ export type { IntrospectOptions, SchemaIntrospector };
@@ -0,0 +1,39 @@
1
+ import { ForeignKeyReference } from '../../schema/column.js';
2
+ import { IndexColumn } from '../../schema/table.js';
3
+
4
+ export interface DatabaseColumn {
5
+ name: string;
6
+ type: string;
7
+ notNull?: boolean;
8
+ default?: unknown;
9
+ autoIncrement?: boolean;
10
+ generated?: 'always' | 'byDefault';
11
+ unique?: boolean | string;
12
+ references?: ForeignKeyReference;
13
+ check?: string;
14
+ }
15
+
16
+ export interface DatabaseIndex {
17
+ name: string;
18
+ columns: IndexColumn[];
19
+ unique?: boolean;
20
+ where?: string;
21
+ }
22
+
23
+ export interface DatabaseCheck {
24
+ name?: string;
25
+ expression: string;
26
+ }
27
+
28
+ export interface DatabaseTable {
29
+ name: string;
30
+ schema?: string;
31
+ columns: DatabaseColumn[];
32
+ primaryKey?: string[];
33
+ indexes?: DatabaseIndex[];
34
+ checks?: DatabaseCheck[];
35
+ }
36
+
37
+ export interface DatabaseSchema {
38
+ tables: DatabaseTable[];
39
+ }
@@ -0,0 +1,161 @@
1
+ import { CompilerContext, Dialect } from '../abstract.js';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
3
+ import { ColumnNode } from '../../ast/expression.js';
4
+
5
+ /**
6
+ * Shared SQL compiler for dialects with standard LIMIT/OFFSET pagination.
7
+ * Concrete dialects override only the minimal hooks (identifier quoting,
8
+ * JSON path, placeholders, RETURNING support) instead of re-implementing
9
+ * the entire compile pipeline.
10
+ */
11
+ export abstract class SqlDialectBase extends Dialect {
12
+ /**
13
+ * Quotes an identifier (dialect-specific).
14
+ */
15
+ abstract quoteIdentifier(id: string): string;
16
+
17
+ /**
18
+ * Compiles SELECT query AST to SQL using common rules.
19
+ */
20
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
21
+ const ctes = this.compileCtes(ast, ctx);
22
+ const columns = this.compileSelectColumns(ast, ctx);
23
+ const from = this.compileFrom(ast.from);
24
+ const joins = this.compileJoins(ast, ctx);
25
+ const whereClause = this.compileWhere(ast.where, ctx);
26
+ const groupBy = this.compileGroupBy(ast);
27
+ const having = this.compileHaving(ast, ctx);
28
+ const orderBy = this.compileOrderBy(ast);
29
+ const pagination = this.compilePagination(ast, orderBy);
30
+
31
+ return `${ctes}SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
32
+ }
33
+
34
+ protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
35
+ const table = this.compileTableName(ast.into);
36
+ const columnList = ast.columns
37
+ .map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`)
38
+ .join(', ');
39
+ const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
40
+ const returning = this.compileReturning(ast.returning, ctx);
41
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
42
+ }
43
+
44
+ protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
45
+ const table = this.compileTableName(ast.table);
46
+ const assignments = ast.set.map(assignment => {
47
+ const col = assignment.column;
48
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
49
+ const value = this.compileOperand(assignment.value, ctx);
50
+ return `${target} = ${value}`;
51
+ }).join(', ');
52
+ const whereClause = this.compileWhere(ast.where, ctx);
53
+ const returning = this.compileReturning(ast.returning, ctx);
54
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
55
+ }
56
+
57
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
58
+ const table = this.compileTableName(ast.from);
59
+ const whereClause = this.compileWhere(ast.where, ctx);
60
+ const returning = this.compileReturning(ast.returning, ctx);
61
+ return `DELETE FROM ${table}${whereClause}${returning}`;
62
+ }
63
+
64
+ /**
65
+ * Default RETURNING compilation: no support.
66
+ */
67
+ protected compileReturning(returning: ColumnNode[] | undefined, _ctx: CompilerContext): string {
68
+ if (!returning || returning.length === 0) return '';
69
+ throw new Error('RETURNING is not supported by this dialect.');
70
+ }
71
+
72
+ /**
73
+ * DISTINCT clause. Override for DISTINCT ON support.
74
+ */
75
+ protected compileDistinct(ast: SelectQueryNode): string {
76
+ return ast.distinct ? 'DISTINCT ' : '';
77
+ }
78
+
79
+ protected compileSelectColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
80
+ return ast.columns.map(c => {
81
+ const expr = this.compileOperand(c, ctx);
82
+ if (c.alias) {
83
+ if (c.alias.includes('(')) return c.alias;
84
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
85
+ }
86
+ return expr;
87
+ }).join(', ');
88
+ }
89
+
90
+ protected compileFrom(ast: SelectQueryNode['from']): string {
91
+ const base = this.compileTableName(ast);
92
+ return ast.alias ? `${base} AS ${this.quoteIdentifier(ast.alias)}` : base;
93
+ }
94
+
95
+ protected compileTableName(table: { name: string; schema?: string }): string {
96
+ if (table.schema) {
97
+ return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
98
+ }
99
+ return this.quoteIdentifier(table.name);
100
+ }
101
+
102
+ protected compileJoins(ast: SelectQueryNode, ctx: CompilerContext): string {
103
+ if (!ast.joins || ast.joins.length === 0) return '';
104
+ const parts = ast.joins.map(j => {
105
+ const table = this.compileFrom(j.table);
106
+ const cond = this.compileExpression(j.condition, ctx);
107
+ return `${j.kind} JOIN ${table} ON ${cond}`;
108
+ });
109
+ return ` ${parts.join(' ')}`;
110
+ }
111
+
112
+ protected compileGroupBy(ast: SelectQueryNode): string {
113
+ if (!ast.groupBy || ast.groupBy.length === 0) return '';
114
+ const cols = ast.groupBy
115
+ .map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`)
116
+ .join(', ');
117
+ return ` GROUP BY ${cols}`;
118
+ }
119
+
120
+ protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
121
+ if (!ast.having) return '';
122
+ return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
123
+ }
124
+
125
+ protected compileOrderBy(ast: SelectQueryNode): string {
126
+ if (!ast.orderBy || ast.orderBy.length === 0) return '';
127
+ const parts = ast.orderBy
128
+ .map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`)
129
+ .join(', ');
130
+ return ` ORDER BY ${parts}`;
131
+ }
132
+
133
+ /**
134
+ * Default LIMIT/OFFSET pagination clause.
135
+ */
136
+ protected compilePagination(ast: SelectQueryNode, _orderByClause: string): string {
137
+ const parts: string[] = [];
138
+ if (ast.limit !== undefined) parts.push(`LIMIT ${ast.limit}`);
139
+ if (ast.offset !== undefined) parts.push(`OFFSET ${ast.offset}`);
140
+ return parts.length ? ` ${parts.join(' ')}` : '';
141
+ }
142
+
143
+ protected compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
144
+ if (!ast.ctes || ast.ctes.length === 0) return '';
145
+ const hasRecursive = ast.ctes.some(cte => cte.recursive);
146
+ const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
147
+ const cteDefs = ast.ctes.map(cte => {
148
+ const name = this.quoteIdentifier(cte.name);
149
+ const cols = cte.columns && cte.columns.length
150
+ ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})`
151
+ : '';
152
+ const query = this.stripTrailingSemicolon(this.compileSelectAst(cte.query, ctx));
153
+ return `${name}${cols} AS (${query})`;
154
+ }).join(', ');
155
+ return `${prefix}${cteDefs} `;
156
+ }
157
+
158
+ protected stripTrailingSemicolon(sql: string): string {
159
+ return sql.trim().replace(/;$/, '');
160
+ }
161
+ }
@@ -1,16 +1,15 @@
1
- import { CompilerContext, Dialect } from '../abstract.js';
2
- import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
3
- import { JsonPathNode } from '../../ast/expression.js';
4
-
5
- /**
6
- * MySQL dialect implementation
7
- */
8
- export class MySqlDialect extends Dialect {
9
- /**
10
- * Creates a new MySqlDialect instance
11
- */
12
- public constructor() {
13
- super();
1
+ import { JsonPathNode } from '../../ast/expression.js';
2
+ import { SqlDialectBase } from '../base/sql-dialect.js';
3
+
4
+ /**
5
+ * MySQL dialect implementation
6
+ */
7
+ export class MySqlDialect extends SqlDialectBase {
8
+ /**
9
+ * Creates a new MySqlDialect instance
10
+ */
11
+ public constructor() {
12
+ super();
14
13
  }
15
14
 
16
15
  /**
@@ -27,102 +26,9 @@ export class MySqlDialect extends Dialect {
27
26
  * @param node - JSON path node
28
27
  * @returns MySQL JSON path expression
29
28
  */
30
- protected compileJsonPath(node: JsonPathNode): string {
31
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
32
- // MySQL 5.7+ uses col->'$.path'
33
- return `${col}->'${node.path}'`;
34
- }
35
-
36
- /**
37
- * Compiles SELECT query AST to MySQL SQL
38
- * @param ast - Query AST
39
- * @param ctx - Compiler context
40
- * @returns MySQL SQL string
41
- */
42
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
43
- const columns = ast.columns.map(c => {
44
- let expr = '';
45
- if (c.type === 'Function') {
46
- expr = this.compileOperand(c, ctx);
47
- } else if (c.type === 'Column') {
48
- expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
49
- } else if (c.type === 'ScalarSubquery') {
50
- expr = this.compileOperand(c, ctx);
51
- } else if (c.type === 'WindowFunction') {
52
- expr = this.compileOperand(c, ctx);
53
- }
54
-
55
- if (c.alias) {
56
- if (c.alias.includes('(')) return c.alias;
57
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
58
- }
59
- return expr;
60
- }).join(', ');
61
-
62
- const distinct = ast.distinct ? 'DISTINCT ' : '';
63
- const from = `${this.quoteIdentifier(ast.from.name)}`;
64
-
65
- const joins = ast.joins.map(j => {
66
- const table = this.quoteIdentifier(j.table.name);
67
- const cond = this.compileExpression(j.condition, ctx);
68
- return `${j.kind} JOIN ${table} ON ${cond}`;
69
- }).join(' ');
70
- const whereClause = this.compileWhere(ast.where, ctx);
71
-
72
- const groupBy = ast.groupBy && ast.groupBy.length > 0
73
- ? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
74
- : '';
75
-
76
- const having = ast.having
77
- ? ` HAVING ${this.compileExpression(ast.having, ctx)}`
78
- : '';
79
-
80
- const orderBy = ast.orderBy && ast.orderBy.length > 0
81
- ? ' ORDER BY ' + ast.orderBy.map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(', ')
82
- : '';
83
-
84
- const limit = ast.limit ? ` LIMIT ${ast.limit}` : '';
85
- const offset = ast.offset ? ` OFFSET ${ast.offset}` : '';
86
-
87
- const ctes = ast.ctes && ast.ctes.length > 0
88
- ? (() => {
89
- const hasRecursive = ast.ctes.some(cte => cte.recursive);
90
- const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
91
- const cteDefs = ast.ctes.map(cte => {
92
- const name = this.quoteIdentifier(cte.name);
93
- const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
94
- const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, '');
95
- return `${name}${cols} AS (${query})`;
96
- }).join(', ');
97
- return prefix + cteDefs + ' ';
98
- })()
99
- : '';
100
-
101
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
102
- }
103
-
104
- protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
105
- const table = this.quoteIdentifier(ast.into.name);
106
- const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
107
- const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
108
- return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
109
- }
110
-
111
- protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
112
- const table = this.quoteIdentifier(ast.table.name);
113
- const assignments = ast.set.map(assignment => {
114
- const col = assignment.column;
115
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
116
- const value = this.compileOperand(assignment.value, ctx);
117
- return `${target} = ${value}`;
118
- }).join(', ');
119
- const whereClause = this.compileWhere(ast.where, ctx);
120
- return `UPDATE ${table} SET ${assignments}${whereClause};`;
121
- }
122
-
123
- protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
124
- const table = this.quoteIdentifier(ast.from.name);
125
- const whereClause = this.compileWhere(ast.where, ctx);
126
- return `DELETE FROM ${table}${whereClause};`;
127
- }
128
- }
29
+ protected compileJsonPath(node: JsonPathNode): string {
30
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
31
+ // MySQL 5.7+ uses col->'$.path'
32
+ return `${col}->'${node.path}'`;
33
+ }
34
+ }
@@ -1,16 +1,16 @@
1
- import { CompilerContext, Dialect } from '../abstract.js';
2
- import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
3
- import { JsonPathNode, ColumnNode } from '../../ast/expression.js';
4
-
5
- /**
6
- * PostgreSQL dialect implementation
7
- */
8
- export class PostgresDialect extends Dialect {
9
- /**
10
- * Creates a new PostgresDialect instance
11
- */
12
- public constructor() {
13
- super();
1
+ import { CompilerContext } from '../abstract.js';
2
+ import { JsonPathNode, ColumnNode } from '../../ast/expression.js';
3
+ import { SqlDialectBase } from '../base/sql-dialect.js';
4
+
5
+ /**
6
+ * PostgreSQL dialect implementation
7
+ */
8
+ export class PostgresDialect extends SqlDialectBase {
9
+ /**
10
+ * Creates a new PostgresDialect instance
11
+ */
12
+ public constructor() {
13
+ super();
14
14
  }
15
15
 
16
16
  /**
@@ -27,116 +27,20 @@ export class PostgresDialect extends Dialect {
27
27
  * @param node - JSON path node
28
28
  * @returns PostgreSQL JSON path expression
29
29
  */
30
- protected compileJsonPath(node: JsonPathNode): string {
31
- const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
32
- // Postgres uses col->>'path' for text extraction
33
- return `${col}->>'${node.path}'`;
34
- }
35
-
36
- /**
37
- * Compiles SELECT query AST to PostgreSQL SQL
38
- * @param ast - Query AST
39
- * @param ctx - Compiler context
40
- * @returns PostgreSQL SQL string
41
- */
42
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
43
- const columns = ast.columns.map(c => {
44
- let expr = '';
45
- if (c.type === 'Function') {
46
- expr = this.compileOperand(c, ctx);
47
- } else if (c.type === 'Column') {
48
- expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
49
- } else if (c.type === 'ScalarSubquery') {
50
- expr = this.compileOperand(c, ctx);
51
- } else if (c.type === 'WindowFunction') {
52
- expr = this.compileOperand(c, ctx);
53
- }
54
-
55
- if (c.alias) {
56
- if (c.alias.includes('(')) return c.alias;
57
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
58
- }
59
- return expr;
60
- }).join(', ');
61
-
62
- const distinct = ast.distinct ? 'DISTINCT ' : '';
63
- const from = `${this.quoteIdentifier(ast.from.name)}`;
64
-
65
- const joins = ast.joins.map(j => {
66
- const table = this.quoteIdentifier(j.table.name);
67
- const cond = this.compileExpression(j.condition, ctx);
68
- return `${j.kind} JOIN ${table} ON ${cond}`;
69
- }).join(' ');
70
- const whereClause = this.compileWhere(ast.where, ctx);
71
-
72
- const groupBy = ast.groupBy && ast.groupBy.length > 0
73
- ? ' GROUP BY ' + ast.groupBy.map(c => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(', ')
74
- : '';
75
-
76
- const having = ast.having
77
- ? ` HAVING ${this.compileExpression(ast.having, ctx)}`
78
- : '';
79
-
80
- const orderBy = ast.orderBy && ast.orderBy.length > 0
81
- ? ' ORDER BY ' + ast.orderBy.map(o => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(', ')
82
- : '';
83
-
84
- const limit = ast.limit ? ` LIMIT ${ast.limit}` : '';
85
- const offset = ast.offset ? ` OFFSET ${ast.offset}` : '';
86
-
87
- const ctes = ast.ctes && ast.ctes.length > 0
88
- ? (() => {
89
- const hasRecursive = ast.ctes.some(cte => cte.recursive);
90
- const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
91
- const cteDefs = ast.ctes.map(cte => {
92
- const name = this.quoteIdentifier(cte.name);
93
- const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
94
- const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, '');
95
- return `${name}${cols} AS (${query})`;
96
- }).join(', ');
97
- return prefix + cteDefs + ' ';
98
- })()
99
- : '';
100
-
101
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
102
- }
103
-
104
- protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
105
- const table = this.quoteIdentifier(ast.into.name);
106
- const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
107
- const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
108
- const returning = this.compileReturning(ast.returning, ctx);
109
- return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
110
- }
111
-
112
- protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
113
- const table = this.quoteIdentifier(ast.table.name);
114
- const assignments = ast.set.map(assignment => {
115
- const col = assignment.column;
116
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
117
- const value = this.compileOperand(assignment.value, ctx);
118
- return `${target} = ${value}`;
119
- }).join(', ');
120
- const whereClause = this.compileWhere(ast.where, ctx);
121
- const returning = this.compileReturning(ast.returning, ctx);
122
- return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
123
- }
124
-
125
- protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
126
- const table = this.quoteIdentifier(ast.from.name);
127
- const whereClause = this.compileWhere(ast.where, ctx);
128
- const returning = this.compileReturning(ast.returning, ctx);
129
- return `DELETE FROM ${table}${whereClause}${returning};`;
130
- }
131
-
132
- protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
133
- if (!returning || returning.length === 0) return '';
134
- const columns = returning
135
- .map(column => {
136
- const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : '';
137
- return `${tablePart}${this.quoteIdentifier(column.name)}`;
138
- })
139
- .join(', ');
140
- return ` RETURNING ${columns}`;
141
- }
142
- }
30
+ protected compileJsonPath(node: JsonPathNode): string {
31
+ const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
32
+ // Postgres uses col->>'path' for text extraction
33
+ return `${col}->>'${node.path}'`;
34
+ }
35
+
36
+ protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
37
+ if (!returning || returning.length === 0) return '';
38
+ const columns = returning
39
+ .map(column => {
40
+ const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : '';
41
+ return `${tablePart}${this.quoteIdentifier(column.name)}`;
42
+ })
43
+ .join(', ');
44
+ return ` RETURNING ${columns}`;
45
+ }
46
+ }