metal-orm 1.0.4 → 1.0.6

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 (57) hide show
  1. package/README.md +299 -113
  2. package/docs/CHANGES.md +104 -0
  3. package/docs/advanced-features.md +92 -1
  4. package/docs/api-reference.md +13 -4
  5. package/docs/dml-operations.md +156 -0
  6. package/docs/getting-started.md +122 -55
  7. package/docs/hydration.md +77 -3
  8. package/docs/index.md +19 -14
  9. package/docs/multi-dialect-support.md +25 -0
  10. package/docs/query-builder.md +60 -0
  11. package/docs/runtime.md +105 -0
  12. package/docs/schema-definition.md +52 -1
  13. package/package.json +1 -1
  14. package/src/ast/expression.ts +630 -592
  15. package/src/ast/query.ts +110 -49
  16. package/src/builder/delete-query-state.ts +42 -0
  17. package/src/builder/delete.ts +57 -0
  18. package/src/builder/hydration-manager.ts +3 -2
  19. package/src/builder/hydration-planner.ts +163 -107
  20. package/src/builder/insert-query-state.ts +62 -0
  21. package/src/builder/insert.ts +59 -0
  22. package/src/builder/operations/relation-manager.ts +1 -23
  23. package/src/builder/relation-conditions.ts +45 -1
  24. package/src/builder/relation-service.ts +81 -18
  25. package/src/builder/relation-types.ts +15 -0
  26. package/src/builder/relation-utils.ts +12 -0
  27. package/src/builder/select.ts +427 -394
  28. package/src/builder/update-query-state.ts +59 -0
  29. package/src/builder/update.ts +61 -0
  30. package/src/constants/sql-operator-config.ts +3 -0
  31. package/src/constants/sql.ts +38 -32
  32. package/src/dialect/abstract.ts +107 -47
  33. package/src/dialect/mssql/index.ts +31 -6
  34. package/src/dialect/mysql/index.ts +31 -6
  35. package/src/dialect/postgres/index.ts +45 -6
  36. package/src/dialect/sqlite/index.ts +45 -6
  37. package/src/index.ts +22 -11
  38. package/src/playground/features/playground/data/scenarios/hydration.ts +23 -11
  39. package/src/playground/features/playground/data/scenarios/types.ts +18 -15
  40. package/src/playground/features/playground/data/schema.ts +6 -2
  41. package/src/playground/features/playground/services/QueryExecutionService.ts +2 -1
  42. package/src/runtime/entity-meta.ts +52 -0
  43. package/src/runtime/entity.ts +252 -0
  44. package/src/runtime/execute.ts +36 -0
  45. package/src/runtime/hydration.ts +100 -38
  46. package/src/runtime/lazy-batch.ts +205 -0
  47. package/src/runtime/orm-context.ts +539 -0
  48. package/src/runtime/relations/belongs-to.ts +92 -0
  49. package/src/runtime/relations/has-many.ts +111 -0
  50. package/src/runtime/relations/many-to-many.ts +149 -0
  51. package/src/schema/column.ts +15 -1
  52. package/src/schema/relation.ts +105 -40
  53. package/src/schema/table.ts +34 -22
  54. package/src/schema/types.ts +76 -0
  55. package/tests/belongs-to-many.test.ts +57 -0
  56. package/tests/dml.test.ts +206 -0
  57. package/tests/orm-runtime.test.ts +254 -0
@@ -0,0 +1,59 @@
1
+ import { TableDef } from '../schema/table';
2
+ import { ColumnNode, ExpressionNode, valueToOperand } from '../ast/expression';
3
+ import { TableNode, UpdateQueryNode, UpdateAssignmentNode } from '../ast/query';
4
+
5
+ const createTableNode = (table: TableDef): TableNode => ({
6
+ type: 'Table',
7
+ name: table.name
8
+ });
9
+
10
+ /**
11
+ * Immutable state for UPDATE queries
12
+ */
13
+ export class UpdateQueryState {
14
+ public readonly table: TableDef;
15
+ public readonly ast: UpdateQueryNode;
16
+
17
+ constructor(table: TableDef, ast?: UpdateQueryNode) {
18
+ this.table = table;
19
+ this.ast = ast ?? {
20
+ type: 'UpdateQuery',
21
+ table: createTableNode(table),
22
+ set: []
23
+ };
24
+ }
25
+
26
+ private clone(nextAst: UpdateQueryNode): UpdateQueryState {
27
+ return new UpdateQueryState(this.table, nextAst);
28
+ }
29
+
30
+ withSet(values: Record<string, unknown>): UpdateQueryState {
31
+ const assignments: UpdateAssignmentNode[] = Object.entries(values).map(([column, value]) => ({
32
+ column: {
33
+ type: 'Column',
34
+ table: this.table.name,
35
+ name: column
36
+ },
37
+ value: valueToOperand(value)
38
+ }));
39
+
40
+ return this.clone({
41
+ ...this.ast,
42
+ set: assignments
43
+ });
44
+ }
45
+
46
+ withWhere(expr: ExpressionNode): UpdateQueryState {
47
+ return this.clone({
48
+ ...this.ast,
49
+ where: expr
50
+ });
51
+ }
52
+
53
+ withReturning(columns: ColumnNode[]): UpdateQueryState {
54
+ return this.clone({
55
+ ...this.ast,
56
+ returning: [...columns]
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,61 @@
1
+ import { TableDef } from '../schema/table';
2
+ import { ColumnDef } from '../schema/column';
3
+ import { ColumnNode, ExpressionNode } from '../ast/expression';
4
+ import { CompiledQuery, UpdateCompiler } from '../dialect/abstract';
5
+ import { UpdateQueryNode } from '../ast/query';
6
+ import { UpdateQueryState } from './update-query-state';
7
+
8
+ const buildColumnNode = (table: TableDef, column: ColumnDef | ColumnNode): ColumnNode => {
9
+ if ((column as ColumnNode).type === 'Column') {
10
+ return column as ColumnNode;
11
+ }
12
+ const def = column as ColumnDef;
13
+ return {
14
+ type: 'Column',
15
+ table: def.table || table.name,
16
+ name: def.name
17
+ };
18
+ };
19
+
20
+ /**
21
+ * Builder for UPDATE queries
22
+ */
23
+ export class UpdateQueryBuilder<T> {
24
+ private readonly table: TableDef;
25
+ private readonly state: UpdateQueryState;
26
+
27
+ constructor(table: TableDef, state?: UpdateQueryState) {
28
+ this.table = table;
29
+ this.state = state ?? new UpdateQueryState(table);
30
+ }
31
+
32
+ private clone(state: UpdateQueryState): UpdateQueryBuilder<T> {
33
+ return new UpdateQueryBuilder(this.table, state);
34
+ }
35
+
36
+ set(values: Record<string, unknown>): UpdateQueryBuilder<T> {
37
+ return this.clone(this.state.withSet(values));
38
+ }
39
+
40
+ where(expr: ExpressionNode): UpdateQueryBuilder<T> {
41
+ return this.clone(this.state.withWhere(expr));
42
+ }
43
+
44
+ returning(...columns: (ColumnDef | ColumnNode)[]): UpdateQueryBuilder<T> {
45
+ if (!columns.length) return this;
46
+ const nodes = columns.map(column => buildColumnNode(this.table, column));
47
+ return this.clone(this.state.withReturning(nodes));
48
+ }
49
+
50
+ compile(compiler: UpdateCompiler): CompiledQuery {
51
+ return compiler.compileUpdate(this.state.ast);
52
+ }
53
+
54
+ toSql(compiler: UpdateCompiler): string {
55
+ return this.compile(compiler).sql;
56
+ }
57
+
58
+ getAST(): UpdateQueryNode {
59
+ return this.state.ast;
60
+ }
61
+ }
@@ -19,8 +19,11 @@ export interface SqlOperatorConfig {
19
19
  */
20
20
  export const SQL_OPERATOR_REGISTRY: Record<SqlOperator, SqlOperatorConfig> = {
21
21
  [SQL_OPERATORS.EQUALS]: { sql: SQL_OPERATORS.EQUALS, tsName: 'eq' },
22
+ [SQL_OPERATORS.NOT_EQUALS]: { sql: SQL_OPERATORS.NOT_EQUALS, tsName: 'neq' },
22
23
  [SQL_OPERATORS.GREATER_THAN]: { sql: SQL_OPERATORS.GREATER_THAN, tsName: 'gt' },
24
+ [SQL_OPERATORS.GREATER_OR_EQUAL]: { sql: SQL_OPERATORS.GREATER_OR_EQUAL, tsName: 'gte' },
23
25
  [SQL_OPERATORS.LESS_THAN]: { sql: SQL_OPERATORS.LESS_THAN, tsName: 'lt' },
26
+ [SQL_OPERATORS.LESS_OR_EQUAL]: { sql: SQL_OPERATORS.LESS_OR_EQUAL, tsName: 'lte' },
24
27
  [SQL_OPERATORS.LIKE]: { sql: SQL_OPERATORS.LIKE, tsName: 'like' },
25
28
  [SQL_OPERATORS.NOT_LIKE]: { sql: SQL_OPERATORS.NOT_LIKE, tsName: 'notLike' },
26
29
  [SQL_OPERATORS.IN]: { sql: SQL_OPERATORS.IN, tsName: 'inList' },
@@ -33,38 +33,44 @@ export const SQL_KEYWORDS = {
33
33
  /**
34
34
  * SQL operators used in query conditions
35
35
  */
36
- export const SQL_OPERATORS = {
37
- /** Equality operator */
38
- EQUALS: '=',
39
- /** Greater than operator */
40
- GREATER_THAN: '>',
41
- /** Less than operator */
42
- LESS_THAN: '<',
43
- /** LIKE pattern matching operator */
44
- LIKE: 'LIKE',
45
- /** NOT LIKE pattern matching operator */
46
- NOT_LIKE: 'NOT LIKE',
47
- /** IN membership operator */
48
- IN: 'IN',
49
- /** NOT IN membership operator */
50
- NOT_IN: 'NOT IN',
51
- /** BETWEEN range operator */
52
- BETWEEN: 'BETWEEN',
53
- /** NOT BETWEEN range operator */
54
- NOT_BETWEEN: 'NOT BETWEEN',
55
- /** IS NULL null check operator */
56
- IS_NULL: 'IS NULL',
57
- /** IS NOT NULL null check operator */
58
- IS_NOT_NULL: 'IS NOT NULL',
59
- /** Logical AND operator */
60
- AND: 'AND',
61
- /** Logical OR operator */
62
- OR: 'OR',
63
- /** EXISTS operator */
64
- EXISTS: 'EXISTS',
65
- /** NOT EXISTS operator */
66
- NOT_EXISTS: 'NOT EXISTS'
67
- } as const;
36
+ export const SQL_OPERATORS = {
37
+ /** Equality operator */
38
+ EQUALS: '=',
39
+ /** Not equals operator */
40
+ NOT_EQUALS: '!=',
41
+ /** Greater than operator */
42
+ GREATER_THAN: '>',
43
+ /** Greater than or equal operator */
44
+ GREATER_OR_EQUAL: '>=',
45
+ /** Less than operator */
46
+ LESS_THAN: '<',
47
+ /** Less than or equal operator */
48
+ LESS_OR_EQUAL: '<=',
49
+ /** LIKE pattern matching operator */
50
+ LIKE: 'LIKE',
51
+ /** NOT LIKE pattern matching operator */
52
+ NOT_LIKE: 'NOT LIKE',
53
+ /** IN membership operator */
54
+ IN: 'IN',
55
+ /** NOT IN membership operator */
56
+ NOT_IN: 'NOT IN',
57
+ /** BETWEEN range operator */
58
+ BETWEEN: 'BETWEEN',
59
+ /** NOT BETWEEN range operator */
60
+ NOT_BETWEEN: 'NOT BETWEEN',
61
+ /** IS NULL null check operator */
62
+ IS_NULL: 'IS NULL',
63
+ /** IS NOT NULL null check operator */
64
+ IS_NOT_NULL: 'IS NOT NULL',
65
+ /** Logical AND operator */
66
+ AND: 'AND',
67
+ /** Logical OR operator */
68
+ OR: 'OR',
69
+ /** EXISTS operator */
70
+ EXISTS: 'EXISTS',
71
+ /** NOT EXISTS operator */
72
+ NOT_EXISTS: 'NOT EXISTS'
73
+ } as const;
68
74
 
69
75
  /**
70
76
  * Type representing any supported SQL operator
@@ -1,4 +1,4 @@
1
- import { SelectQueryNode } from '../ast/query';
1
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../ast/query';
2
2
  import {
3
3
  ExpressionNode,
4
4
  BinaryExpressionNode,
@@ -30,39 +30,91 @@ export interface CompilerContext {
30
30
  /**
31
31
  * Result of SQL compilation
32
32
  */
33
- export interface CompiledQuery {
34
- /** Generated SQL string */
35
- sql: string;
36
- /** Parameters for the query */
37
- params: unknown[];
38
- }
39
-
40
- /**
41
- * Abstract base class for SQL dialect implementations
42
- */
43
- export abstract class Dialect {
33
+ export interface CompiledQuery {
34
+ /** Generated SQL string */
35
+ sql: string;
36
+ /** Parameters for the query */
37
+ params: unknown[];
38
+ }
39
+
40
+ export interface SelectCompiler {
41
+ compileSelect(ast: SelectQueryNode): CompiledQuery;
42
+ }
43
+
44
+ export interface InsertCompiler {
45
+ compileInsert(ast: InsertQueryNode): CompiledQuery;
46
+ }
47
+
48
+ export interface UpdateCompiler {
49
+ compileUpdate(ast: UpdateQueryNode): CompiledQuery;
50
+ }
51
+
52
+ export interface DeleteCompiler {
53
+ compileDelete(ast: DeleteQueryNode): CompiledQuery;
54
+ }
55
+
56
+ /**
57
+ * Abstract base class for SQL dialect implementations
58
+ */
59
+ export abstract class Dialect
60
+ implements SelectCompiler, InsertCompiler, UpdateCompiler, DeleteCompiler
61
+ {
44
62
  /**
45
63
  * Compiles a SELECT query AST to SQL
46
64
  * @param ast - Query AST to compile
47
65
  * @returns Compiled query with SQL and parameters
48
66
  */
49
- compileSelect(ast: SelectQueryNode): CompiledQuery {
50
- const ctx = this.createCompilerContext();
51
- const rawSql = this.compileSelectAst(ast, ctx).trim();
52
- const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
53
- return {
54
- sql,
55
- params: [...ctx.params]
56
- };
57
- }
58
-
59
- /**
60
- * Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
61
- * @param ast - Query AST
62
- * @param ctx - Compiler context
63
- * @returns SQL string
64
- */
65
- protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
67
+ compileSelect(ast: SelectQueryNode): CompiledQuery {
68
+ const ctx = this.createCompilerContext();
69
+ const rawSql = this.compileSelectAst(ast, ctx).trim();
70
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
71
+ return {
72
+ sql,
73
+ params: [...ctx.params]
74
+ };
75
+ }
76
+
77
+ compileInsert(ast: InsertQueryNode): CompiledQuery {
78
+ const ctx = this.createCompilerContext();
79
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
80
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
81
+ return {
82
+ sql,
83
+ params: [...ctx.params]
84
+ };
85
+ }
86
+
87
+ compileUpdate(ast: UpdateQueryNode): CompiledQuery {
88
+ const ctx = this.createCompilerContext();
89
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
90
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
91
+ return {
92
+ sql,
93
+ params: [...ctx.params]
94
+ };
95
+ }
96
+
97
+ compileDelete(ast: DeleteQueryNode): CompiledQuery {
98
+ const ctx = this.createCompilerContext();
99
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
100
+ const sql = rawSql.endsWith(';') ? rawSql : `${rawSql};`;
101
+ return {
102
+ sql,
103
+ params: [...ctx.params]
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Compiles SELECT query AST to SQL (to be implemented by concrete dialects)
109
+ * @param ast - Query AST
110
+ * @param ctx - Compiler context
111
+ * @returns SQL string
112
+ */
113
+ protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
114
+
115
+ protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
116
+ protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
117
+ protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
66
118
 
67
119
  /**
68
120
  * Quotes an SQL identifier (to be implemented by concrete dialects)
@@ -77,10 +129,18 @@ export abstract class Dialect {
77
129
  * @param ctx - Compiler context
78
130
  * @returns SQL WHERE clause or empty string
79
131
  */
80
- protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
81
- if (!where) return '';
82
- return ` WHERE ${this.compileExpression(where, ctx)}`;
83
- }
132
+ protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
133
+ if (!where) return '';
134
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
135
+ }
136
+
137
+ protected compileReturning(
138
+ returning: ColumnNode[] | undefined,
139
+ ctx: CompilerContext
140
+ ): string {
141
+ if (!returning || returning.length === 0) return '';
142
+ throw new Error('RETURNING is not supported by this dialect.');
143
+ }
84
144
 
85
145
  /**
86
146
  * Generates subquery for EXISTS expressions
@@ -163,13 +223,13 @@ export abstract class Dialect {
163
223
  * @param ctx - Compiler context
164
224
  * @returns Compiled SQL expression
165
225
  */
166
- protected compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
167
- const compiler = this.expressionCompilers.get(node.type);
168
- if (!compiler) {
169
- throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
170
- }
171
- return compiler(node, ctx);
172
- }
226
+ protected compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
227
+ const compiler = this.expressionCompilers.get(node.type);
228
+ if (!compiler) {
229
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
230
+ }
231
+ return compiler(node, ctx);
232
+ }
173
233
 
174
234
  /**
175
235
  * Compiles an operand node
@@ -177,13 +237,13 @@ export abstract class Dialect {
177
237
  * @param ctx - Compiler context
178
238
  * @returns Compiled SQL operand
179
239
  */
180
- protected compileOperand(node: OperandNode, ctx: CompilerContext): string {
181
- const compiler = this.operandCompilers.get(node.type);
182
- if (!compiler) {
183
- throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
184
- }
185
- return compiler(node, ctx);
186
- }
240
+ protected compileOperand(node: OperandNode, ctx: CompilerContext): string {
241
+ const compiler = this.operandCompilers.get(node.type);
242
+ if (!compiler) {
243
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
244
+ }
245
+ return compiler(node, ctx);
246
+ }
187
247
 
188
248
  private registerDefaultExpressionCompilers(): void {
189
249
  this.registerExpressionCompiler('BinaryExpression', (binary: BinaryExpressionNode, ctx) => {
@@ -1,6 +1,6 @@
1
- import { CompilerContext, Dialect } from '../abstract';
2
- import { SelectQueryNode } from '../../ast/query';
3
- import { JsonPathNode } from '../../ast/expression';
1
+ import { CompilerContext, Dialect } from '../abstract';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query';
3
+ import { JsonPathNode } from '../../ast/expression';
4
4
 
5
5
  /**
6
6
  * Microsoft SQL Server dialect implementation
@@ -48,7 +48,7 @@ export class SqlServerDialect extends Dialect {
48
48
  * @param ctx - Compiler context
49
49
  * @returns SQL Server SQL string
50
50
  */
51
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
51
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
52
52
  const columns = ast.columns.map(c => {
53
53
  let expr = '';
54
54
  if (c.type === 'Function') {
@@ -111,6 +111,31 @@ export class SqlServerDialect extends Dialect {
111
111
  }).join(', ') + ' '
112
112
  : '';
113
113
 
114
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy};`;
115
- }
114
+ return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy};`;
115
+ }
116
+
117
+ protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
118
+ const table = this.quoteIdentifier(ast.into.name);
119
+ const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
120
+ const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
121
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
122
+ }
123
+
124
+ protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
125
+ const table = this.quoteIdentifier(ast.table.name);
126
+ const assignments = ast.set.map(assignment => {
127
+ const col = assignment.column;
128
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
129
+ const value = this.compileOperand(assignment.value, ctx);
130
+ return `${target} = ${value}`;
131
+ }).join(', ');
132
+ const whereClause = this.compileWhere(ast.where, ctx);
133
+ return `UPDATE ${table} SET ${assignments}${whereClause};`;
134
+ }
135
+
136
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
137
+ const table = this.quoteIdentifier(ast.from.name);
138
+ const whereClause = this.compileWhere(ast.where, ctx);
139
+ return `DELETE FROM ${table}${whereClause};`;
140
+ }
116
141
  }
@@ -1,6 +1,6 @@
1
- import { CompilerContext, Dialect } from '../abstract';
2
- import { SelectQueryNode } from '../../ast/query';
3
- import { JsonPathNode } from '../../ast/expression';
1
+ import { CompilerContext, Dialect } from '../abstract';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query';
3
+ import { JsonPathNode } from '../../ast/expression';
4
4
 
5
5
  /**
6
6
  * MySQL dialect implementation
@@ -39,7 +39,7 @@ export class MySqlDialect extends Dialect {
39
39
  * @param ctx - Compiler context
40
40
  * @returns MySQL SQL string
41
41
  */
42
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
42
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
43
43
  const columns = ast.columns.map(c => {
44
44
  let expr = '';
45
45
  if (c.type === 'Function') {
@@ -98,6 +98,31 @@ export class MySqlDialect extends Dialect {
98
98
  })()
99
99
  : '';
100
100
 
101
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
102
- }
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
+ }
103
128
  }
@@ -1,6 +1,6 @@
1
- import { CompilerContext, Dialect } from '../abstract';
2
- import { SelectQueryNode } from '../../ast/query';
3
- import { JsonPathNode } from '../../ast/expression';
1
+ import { CompilerContext, Dialect } from '../abstract';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query';
3
+ import { JsonPathNode, ColumnNode } from '../../ast/expression';
4
4
 
5
5
  /**
6
6
  * PostgreSQL dialect implementation
@@ -39,7 +39,7 @@ export class PostgresDialect extends Dialect {
39
39
  * @param ctx - Compiler context
40
40
  * @returns PostgreSQL SQL string
41
41
  */
42
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
42
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
43
43
  const columns = ast.columns.map(c => {
44
44
  let expr = '';
45
45
  if (c.type === 'Function') {
@@ -98,6 +98,45 @@ export class PostgresDialect extends Dialect {
98
98
  })()
99
99
  : '';
100
100
 
101
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
102
- }
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
+ }
103
142
  }
@@ -1,6 +1,6 @@
1
- import { CompilerContext, Dialect } from '../abstract';
2
- import { SelectQueryNode } from '../../ast/query';
3
- import { JsonPathNode } from '../../ast/expression';
1
+ import { CompilerContext, Dialect } from '../abstract';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query';
3
+ import { JsonPathNode, ColumnNode } from '../../ast/expression';
4
4
 
5
5
  /**
6
6
  * SQLite dialect implementation
@@ -39,7 +39,7 @@ export class SqliteDialect extends Dialect {
39
39
  * @param ctx - Compiler context
40
40
  * @returns SQLite SQL string
41
41
  */
42
- protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
42
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
43
43
  const columns = ast.columns.map(c => {
44
44
  let expr = '';
45
45
  if (c.type === 'Function') {
@@ -102,6 +102,45 @@ export class SqliteDialect extends Dialect {
102
102
  })()
103
103
  : '';
104
104
 
105
- return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
106
- }
105
+ return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
106
+ }
107
+
108
+ protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
109
+ const table = this.quoteIdentifier(ast.into.name);
110
+ const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
111
+ const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
112
+ const returning = this.compileReturning(ast.returning, ctx);
113
+ return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
114
+ }
115
+
116
+ protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
117
+ const table = this.quoteIdentifier(ast.table.name);
118
+ const assignments = ast.set.map(assignment => {
119
+ const col = assignment.column;
120
+ const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
121
+ const value = this.compileOperand(assignment.value, ctx);
122
+ return `${target} = ${value}`;
123
+ }).join(', ');
124
+ const whereClause = this.compileWhere(ast.where, ctx);
125
+ const returning = this.compileReturning(ast.returning, ctx);
126
+ return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
127
+ }
128
+
129
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
130
+ const table = this.quoteIdentifier(ast.from.name);
131
+ const whereClause = this.compileWhere(ast.where, ctx);
132
+ const returning = this.compileReturning(ast.returning, ctx);
133
+ return `DELETE FROM ${table}${whereClause}${returning};`;
134
+ }
135
+
136
+ protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
137
+ if (!returning || returning.length === 0) return '';
138
+ const columns = returning
139
+ .map(column => {
140
+ const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : '';
141
+ return `${tablePart}${this.quoteIdentifier(column.name)}`;
142
+ })
143
+ .join(', ');
144
+ return ` RETURNING ${columns}`;
145
+ }
107
146
  }