metal-orm 1.0.16 → 1.0.18

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 (64) hide show
  1. package/README.md +37 -40
  2. package/dist/decorators/index.cjs +344 -69
  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 +344 -69
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +567 -181
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +66 -30
  11. package/dist/index.d.ts +66 -30
  12. package/dist/index.js +559 -181
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BKZrMRCQ.d.cts → select-BuMpVcVt.d.cts} +265 -74
  15. package/dist/{select-BKZrMRCQ.d.ts → select-BuMpVcVt.d.ts} +265 -74
  16. package/package.json +5 -1
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/aggregate-functions.ts +50 -4
  19. package/src/core/ast/builders.ts +23 -3
  20. package/src/core/ast/expression-builders.ts +36 -16
  21. package/src/core/ast/expression-nodes.ts +17 -9
  22. package/src/core/ast/join-node.ts +5 -3
  23. package/src/core/ast/join.ts +16 -16
  24. package/src/core/ast/query.ts +44 -29
  25. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  26. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  27. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  28. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  29. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  30. package/src/core/dialect/abstract.ts +12 -8
  31. package/src/core/dialect/base/sql-dialect.ts +58 -46
  32. package/src/core/dialect/mssql/functions.ts +24 -15
  33. package/src/core/dialect/mssql/index.ts +53 -28
  34. package/src/core/dialect/postgres/functions.ts +33 -24
  35. package/src/core/dialect/sqlite/functions.ts +19 -12
  36. package/src/core/dialect/sqlite/index.ts +22 -13
  37. package/src/core/functions/datetime.ts +2 -1
  38. package/src/core/functions/numeric.ts +2 -1
  39. package/src/core/functions/standard-strategy.ts +52 -12
  40. package/src/core/functions/text.ts +2 -1
  41. package/src/core/functions/types.ts +8 -8
  42. package/src/index.ts +5 -4
  43. package/src/orm/domain-event-bus.ts +43 -25
  44. package/src/orm/entity-meta.ts +40 -0
  45. package/src/orm/execution-context.ts +6 -0
  46. package/src/orm/hydration-context.ts +6 -4
  47. package/src/orm/orm-session.ts +35 -24
  48. package/src/orm/orm.ts +10 -10
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/runtime-types.ts +60 -2
  51. package/src/orm/transaction-runner.ts +7 -0
  52. package/src/orm/unit-of-work.ts +1 -0
  53. package/src/query-builder/column-selector.ts +9 -7
  54. package/src/query-builder/insert-query-state.ts +13 -3
  55. package/src/query-builder/query-ast-service.ts +59 -38
  56. package/src/query-builder/relation-conditions.ts +38 -34
  57. package/src/query-builder/relation-manager.ts +8 -3
  58. package/src/query-builder/relation-service.ts +59 -46
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select-query-state.ts +19 -7
  61. package/src/query-builder/select.ts +339 -167
  62. package/src/query-builder/update-query-state.ts +31 -9
  63. package/src/schema/column.ts +75 -39
  64. package/src/schema/types.ts +17 -6
@@ -1,6 +1,8 @@
1
1
  import { ColumnNode, FunctionNode } from './expression-nodes.js';
2
- import { columnOperand } from './expression-builders.js';
2
+ import { columnOperand, valueToOperand, ValueOperandInput } from './expression-builders.js';
3
3
  import { ColumnRef } from './types.js';
4
+ import { OrderByNode } from './query.js';
5
+ import { ORDER_DIRECTIONS, OrderDirection } from '../sql/sql.js';
4
6
 
5
7
  const buildAggregate = (name: string) => (col: ColumnRef | ColumnNode): FunctionNode => ({
6
8
  type: 'Function',
@@ -25,6 +27,50 @@ export const sum = buildAggregate('SUM');
25
27
  /**
26
28
  * Creates an AVG function expression
27
29
  * @param col - Column to average
28
- * @returns Function node with AVG
29
- */
30
- export const avg = buildAggregate('AVG');
30
+ * @returns Function node with AVG
31
+ */
32
+ export const avg = buildAggregate('AVG');
33
+
34
+ /**
35
+ * Creates a MIN function expression
36
+ * @param col - Column to take the minimum of
37
+ * @returns Function node with MIN
38
+ */
39
+ export const min = buildAggregate('MIN');
40
+
41
+ /**
42
+ * Creates a MAX function expression
43
+ * @param col - Column to take the maximum of
44
+ * @returns Function node with MAX
45
+ */
46
+ export const max = buildAggregate('MAX');
47
+
48
+ type GroupConcatOrderByInput = {
49
+ column: ColumnRef | ColumnNode;
50
+ direction?: OrderDirection;
51
+ };
52
+
53
+ export type GroupConcatOptions = {
54
+ separator?: ValueOperandInput;
55
+ orderBy?: GroupConcatOrderByInput[];
56
+ };
57
+
58
+ const toOrderByNode = (order: GroupConcatOrderByInput): OrderByNode => ({
59
+ type: 'OrderBy',
60
+ column: columnOperand(order.column),
61
+ direction: order.direction ?? ORDER_DIRECTIONS.ASC
62
+ });
63
+
64
+ /**
65
+ * Aggregates grouped strings into a single value.
66
+ */
67
+ export const groupConcat = (
68
+ col: ColumnRef | ColumnNode,
69
+ options?: GroupConcatOptions
70
+ ): FunctionNode => ({
71
+ type: 'Function',
72
+ name: 'GROUP_CONCAT',
73
+ args: [columnOperand(col)],
74
+ orderBy: options?.orderBy?.map(toOrderByNode),
75
+ separator: options?.separator !== undefined ? valueToOperand(options.separator) : undefined
76
+ });
@@ -1,5 +1,5 @@
1
1
  import { ColumnNode } from './expression-nodes.js';
2
- import { TableNode, FunctionTableNode } from './query.js';
2
+ import { TableNode, FunctionTableNode, DerivedTableNode } from './query.js';
3
3
  import { ColumnRef, TableRef } from './types.js';
4
4
 
5
5
  /**
@@ -13,9 +13,15 @@ export const buildColumnNode = (table: TableRef, column: ColumnRef | ColumnNode)
13
13
  }
14
14
 
15
15
  const def = column as ColumnRef;
16
+ const baseTable = def.table
17
+ ? table.alias && def.table === table.name
18
+ ? table.alias
19
+ : def.table
20
+ : table.alias || table.name;
21
+
16
22
  return {
17
23
  type: 'Column',
18
- table: def.table || table.name,
24
+ table: baseTable,
19
25
  name: def.name
20
26
  };
21
27
  };
@@ -28,7 +34,7 @@ export const buildColumnNode = (table: TableRef, column: ColumnRef | ColumnNode)
28
34
  export const buildColumnNodes = (table: TableRef, names: string[]): ColumnNode[] =>
29
35
  names.map(name => ({
30
36
  type: 'Column',
31
- table: table.name,
37
+ table: table.alias || table.name,
32
38
  name
33
39
  }));
34
40
 
@@ -54,3 +60,17 @@ export const fnTable = (name: string, args: any[] = [], alias?: string, opts?: {
54
60
  columnAliases: opts?.columnAliases,
55
61
  schema: opts?.schema
56
62
  });
63
+
64
+ /**
65
+ * Creates a derived table node wrapping a subquery.
66
+ */
67
+ export const derivedTable = (
68
+ query: import('./query.js').SelectQueryNode,
69
+ alias: string,
70
+ columnAliases?: string[]
71
+ ): DerivedTableNode => ({
72
+ type: 'DerivedTable',
73
+ query,
74
+ alias,
75
+ columnAliases
76
+ });
@@ -19,22 +19,23 @@ import {
19
19
  isOperandNode
20
20
  } from './expression-nodes.js';
21
21
 
22
+ export type LiteralValue = LiteralNode['value'];
23
+ export type ValueOperandInput = OperandNode | LiteralValue;
24
+
22
25
  /**
23
26
  * Converts a primitive or existing operand into an operand node
24
27
  * @param value - Value or operand to normalize
25
28
  * @returns OperandNode representing the value
26
29
  */
27
- export const valueToOperand = (value: unknown): OperandNode => {
28
- if (
29
- value === null ||
30
- value === undefined ||
31
- typeof value === 'string' ||
32
- typeof value === 'number' ||
33
- typeof value === 'boolean'
34
- ) {
35
- return { type: 'Literal', value: value === undefined ? null : value } as LiteralNode;
30
+ export const valueToOperand = (value: ValueOperandInput): OperandNode => {
31
+ if (isOperandNode(value)) {
32
+ return value;
36
33
  }
37
- return value as OperandNode;
34
+
35
+ return {
36
+ type: 'Literal',
37
+ value
38
+ } as LiteralNode;
38
39
  };
39
40
 
40
41
  const toNode = (col: ColumnRef | OperandNode): OperandNode => {
@@ -48,15 +49,34 @@ const toLiteralNode = (value: string | number | boolean | null): LiteralNode =>
48
49
  value
49
50
  });
50
51
 
51
- const toOperand = (val: OperandNode | ColumnRef | string | number | boolean | null): OperandNode => {
52
- if (val === null) return { type: 'Literal', value: null };
53
- if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
54
- return { type: 'Literal', value: val };
52
+ const isLiteralValue = (value: unknown): value is LiteralValue =>
53
+ value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
54
+
55
+ export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
56
+ isOperandNode(value) || isLiteralValue(value);
57
+
58
+ const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => {
59
+ if (isLiteralValue(val)) {
60
+ return valueToOperand(val);
55
61
  }
56
- return toNode(val as OperandNode | ColumnRef);
62
+
63
+ return toNode(val);
57
64
  };
58
65
 
59
- export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
66
+ export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
67
+ /**
68
+ * Marks a column reference as an outer-scope reference for correlated subqueries.
69
+ * Primarily semantic; SQL rendering still uses the provided table/alias name.
70
+ */
71
+ export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
72
+ ...columnOperand(col),
73
+ scope: 'outer'
74
+ });
75
+
76
+ /**
77
+ * Creates an outer-scoped column reference using a specific table or alias name.
78
+ */
79
+ export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
60
80
 
61
81
  const createBinaryExpression = (
62
82
  operator: SqlOperator,
@@ -14,15 +14,17 @@ export interface LiteralNode {
14
14
  /**
15
15
  * AST node representing a column reference
16
16
  */
17
- export interface ColumnNode {
18
- type: 'Column';
19
- /** Table name the column belongs to */
20
- table: string;
21
- /** Column name */
22
- name: string;
23
- /** Optional alias for the column */
24
- alias?: string;
25
- }
17
+ export interface ColumnNode {
18
+ type: 'Column';
19
+ /** Table name the column belongs to */
20
+ table: string;
21
+ /** Column name */
22
+ name: string;
23
+ /** Optional alias for the column */
24
+ alias?: string;
25
+ /** Optional scope marker (e.g., 'outer' for correlated references) */
26
+ scope?: 'outer' | 'default';
27
+ }
26
28
 
27
29
  /**
28
30
  * AST node representing a function call
@@ -37,6 +39,12 @@ export interface FunctionNode {
37
39
  args: OperandNode[];
38
40
  /** Optional alias for the function result */
39
41
  alias?: string;
42
+ /** Optional ORDER BY clause used by aggregations like GROUP_CONCAT */
43
+ orderBy?: OrderByNode[];
44
+ /** Optional separator argument used by GROUP_CONCAT-like functions */
45
+ separator?: OperandNode;
46
+ /** Optional DISTINCT modifier */
47
+ distinct?: boolean;
40
48
  }
41
49
 
42
50
  /**
@@ -2,7 +2,7 @@ import { JoinNode } from './join.js';
2
2
  import { ExpressionNode } from './expression.js';
3
3
  import { JoinKind } from '../sql/sql.js';
4
4
  import { JoinMetadata } from './join-metadata.js';
5
- import { TableNode, FunctionTableNode } from './query.js';
5
+ import { TableSourceNode, TableNode, FunctionTableNode } from './query.js';
6
6
 
7
7
  /**
8
8
  * Creates a JoinNode ready for AST insertion.
@@ -10,13 +10,15 @@ import { TableNode, FunctionTableNode } from './query.js';
10
10
  */
11
11
  export const createJoinNode = (
12
12
  kind: JoinKind,
13
- tableName: string | TableNode | FunctionTableNode,
13
+ tableName: string | TableSourceNode,
14
14
  condition: ExpressionNode,
15
15
  relationName?: string
16
16
  ): JoinNode => ({
17
17
  type: 'Join',
18
18
  kind,
19
- table: typeof tableName === 'string' ? { type: 'Table', name: tableName } as TableNode : (tableName as TableNode | FunctionTableNode),
19
+ table: typeof tableName === 'string'
20
+ ? ({ type: 'Table', name: tableName } as TableNode)
21
+ : (tableName as TableSourceNode),
20
22
  condition,
21
23
  meta: relationName ? ({ relationName } as JoinMetadata) : undefined
22
24
  });
@@ -1,18 +1,18 @@
1
- import { TableNode, FunctionTableNode } from './query.js';
2
- import { ExpressionNode } from './expression.js';
3
- import { JoinKind } from '../sql/sql.js';
4
-
5
- /**
6
- * AST node representing a JOIN clause
1
+ import { TableSourceNode } from './query.js';
2
+ import { ExpressionNode } from './expression.js';
3
+ import { JoinKind } from '../sql/sql.js';
4
+
5
+ /**
6
+ * AST node representing a JOIN clause
7
7
  */
8
8
  export interface JoinNode {
9
- type: 'Join';
10
- /** Type of join (INNER, LEFT, RIGHT, etc.) */
11
- kind: JoinKind;
12
- /** Table to join */
13
- table: TableNode | FunctionTableNode;
14
- /** Join condition expression */
15
- condition: ExpressionNode;
16
- /** Optional metadata for non-SQL concerns (e.g., relation name) */
17
- meta?: Record<string, unknown>;
18
- }
9
+ type: 'Join';
10
+ /** Type of join (INNER, LEFT, RIGHT, etc.) */
11
+ kind: JoinKind;
12
+ /** Table to join */
13
+ table: TableSourceNode;
14
+ /** Join condition expression */
15
+ condition: ExpressionNode;
16
+ /** Optional metadata for non-SQL concerns (e.g., relation name) */
17
+ meta?: Record<string, unknown>;
18
+ }
@@ -13,18 +13,18 @@ import { OrderDirection } from '../sql/sql.js';
13
13
  /**
14
14
  * AST node representing a table reference in a query
15
15
  */
16
- export interface TableNode {
17
- type: 'Table';
18
- /** Table name */
19
- name: string;
20
- /** Optional schema name */
21
- schema?: string;
22
- /** Optional table alias */
23
- alias?: string;
24
- }
25
-
26
- /**
27
- * AST node representing a function used as a table source (table-valued function)
16
+ export interface TableNode {
17
+ type: 'Table';
18
+ /** Table name */
19
+ name: string;
20
+ /** Optional schema name */
21
+ schema?: string;
22
+ /** Optional table alias */
23
+ alias?: string;
24
+ }
25
+
26
+ /**
27
+ * AST node representing a function used as a table source (table-valued function)
28
28
  */
29
29
  export interface FunctionTableNode {
30
30
  type: 'FunctionTable';
@@ -37,17 +37,32 @@ export interface FunctionTableNode {
37
37
  /** Optional alias for the function table */
38
38
  alias?: string;
39
39
  /** LATERAL flag */
40
- lateral?: boolean;
41
- /** WITH ORDINALITY flag */
42
- withOrdinality?: boolean;
43
- /** Optional column aliases */
44
- columnAliases?: string[];
45
- }
46
-
47
- /**
48
- * AST node representing an ORDER BY clause
49
- */
50
- export interface OrderByNode {
40
+ lateral?: boolean;
41
+ /** WITH ORDINALITY flag */
42
+ withOrdinality?: boolean;
43
+ /** Optional column aliases */
44
+ columnAliases?: string[];
45
+ }
46
+
47
+ /**
48
+ * AST node representing a derived table (subquery with an alias)
49
+ */
50
+ export interface DerivedTableNode {
51
+ type: 'DerivedTable';
52
+ /** Subquery providing the rows */
53
+ query: SelectQueryNode;
54
+ /** Required alias for the derived table */
55
+ alias: string;
56
+ /** Optional column aliases */
57
+ columnAliases?: string[];
58
+ }
59
+
60
+ export type TableSourceNode = TableNode | FunctionTableNode | DerivedTableNode;
61
+
62
+ /**
63
+ * AST node representing an ORDER BY clause
64
+ */
65
+ export interface OrderByNode {
51
66
  type: 'OrderBy';
52
67
  /** Column to order by */
53
68
  column: ColumnNode;
@@ -89,12 +104,12 @@ export interface SetOperationNode {
89
104
  /**
90
105
  * AST node representing a complete SELECT query
91
106
  */
92
- export interface SelectQueryNode {
93
- type: 'SelectQuery';
94
- /** Optional CTEs (WITH clauses) */
95
- ctes?: CommonTableExpressionNode[];
96
- /** FROM clause table (either a Table or a FunctionTable) */
97
- from: TableNode | FunctionTableNode;
107
+ export interface SelectQueryNode {
108
+ type: 'SelectQuery';
109
+ /** Optional CTEs (WITH clauses) */
110
+ ctes?: CommonTableExpressionNode[];
111
+ /** FROM clause table (either a Table or a FunctionTable) */
112
+ from: TableSourceNode;
98
113
  /** SELECT clause columns */
99
114
  columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
100
115
  /** JOIN clauses */
@@ -65,6 +65,24 @@ export class MSSqlSchemaDialect extends BaseSchemaDialect {
65
65
  case 'TEXT':
66
66
  case 'text':
67
67
  return 'NVARCHAR(MAX)';
68
+ case 'BINARY':
69
+ case 'binary': {
70
+ const length = column.args?.[0];
71
+ return length !== undefined ? `BINARY(${length})` : 'BINARY(255)';
72
+ }
73
+ case 'VARBINARY':
74
+ case 'varbinary': {
75
+ const length = column.args?.[0];
76
+ if (typeof length === 'string' && length.toLowerCase() === 'max') {
77
+ return 'VARBINARY(MAX)';
78
+ }
79
+ return length !== undefined ? `VARBINARY(${length})` : 'VARBINARY(255)';
80
+ }
81
+ case 'BLOB':
82
+ case 'blob':
83
+ case 'BYTEA':
84
+ case 'bytea':
85
+ return 'VARBINARY(MAX)';
68
86
  case 'ENUM':
69
87
  case 'enum':
70
88
  return 'NVARCHAR(255)';
@@ -69,6 +69,17 @@ export class MySqlSchemaDialect extends BaseSchemaDialect {
69
69
  case 'TEXT':
70
70
  case 'text':
71
71
  return 'TEXT';
72
+ case 'BINARY':
73
+ case 'binary':
74
+ return column.args?.length ? `BINARY(${column.args[0]})` : 'BINARY(255)';
75
+ case 'VARBINARY':
76
+ case 'varbinary':
77
+ return column.args?.length ? `VARBINARY(${column.args[0]})` : 'VARBINARY(255)';
78
+ case 'BLOB':
79
+ case 'blob':
80
+ case 'BYTEA':
81
+ case 'bytea':
82
+ return 'BLOB';
72
83
  case 'ENUM':
73
84
  case 'enum':
74
85
  return column.args && Array.isArray(column.args) && column.args.length
@@ -70,6 +70,15 @@ export class PostgresSchemaDialect extends BaseSchemaDialect {
70
70
  case 'ENUM':
71
71
  case 'enum':
72
72
  return 'text';
73
+ case 'BINARY':
74
+ case 'binary':
75
+ case 'VARBINARY':
76
+ case 'varbinary':
77
+ case 'BLOB':
78
+ case 'blob':
79
+ case 'BYTEA':
80
+ case 'bytea':
81
+ return 'bytea';
73
82
  default:
74
83
  return String(column.type).toLowerCase();
75
84
  }
@@ -62,6 +62,15 @@ export class SQLiteSchemaDialect extends BaseSchemaDialect {
62
62
  case 'ENUM':
63
63
  case 'enum':
64
64
  return 'TEXT';
65
+ case 'BINARY':
66
+ case 'binary':
67
+ case 'VARBINARY':
68
+ case 'varbinary':
69
+ case 'BLOB':
70
+ case 'blob':
71
+ case 'BYTEA':
72
+ case 'bytea':
73
+ return 'BLOB';
65
74
  default:
66
75
  return 'TEXT';
67
76
  }
@@ -1,14 +1,10 @@
1
1
  // Small helpers to build Postgres-specific function calls as AST FunctionNodes
2
- import { columnOperand, valueToOperand } from '../../../ast/expression-builders.js';
2
+ import { valueToOperand } from '../../../ast/expression-builders.js';
3
3
  import type { OperandNode, FunctionNode } from '../../../ast/expression.js';
4
4
 
5
5
  type OperandInput = OperandNode | string | number | boolean | null;
6
6
 
7
- const toOperand = (v: OperandInput) => {
8
- if (v === null) return valueToOperand(null);
9
- if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') return valueToOperand(v);
10
- return v as OperandNode;
11
- };
7
+ const toOperand = (v: OperandInput): OperandNode => valueToOperand(v);
12
8
 
13
9
  const fn = (name: string, args: OperandInput[]): FunctionNode => ({
14
10
  type: 'Function',
@@ -476,12 +476,16 @@ export abstract class Dialect
476
476
  /**
477
477
  * Compiles a function operand, using the dialect's function strategy.
478
478
  */
479
- protected compileFunctionOperand(fnNode: FunctionNode, ctx: CompilerContext): string {
480
- const compiledArgs = fnNode.args.map(arg => this.compileOperand(arg, ctx));
481
- const renderer = this.functionStrategy.getRenderer(fnNode.name);
482
- if (renderer) {
483
- return renderer({ node: fnNode, compiledArgs });
484
- }
485
- return `${fnNode.name}(${compiledArgs.join(', ')})`;
486
- }
479
+ protected compileFunctionOperand(fnNode: FunctionNode, ctx: CompilerContext): string {
480
+ const compiledArgs = fnNode.args.map(arg => this.compileOperand(arg, ctx));
481
+ const renderer = this.functionStrategy.getRenderer(fnNode.name);
482
+ if (renderer) {
483
+ return renderer({
484
+ node: fnNode,
485
+ compiledArgs,
486
+ compileOperand: operand => this.compileOperand(operand, ctx)
487
+ });
488
+ }
489
+ return `${fnNode.name}(${compiledArgs.join(', ')})`;
490
+ }
487
491
  }
@@ -1,7 +1,7 @@
1
1
  import { CompilerContext, Dialect } from '../abstract.js';
2
- import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode, DerivedTableNode, FunctionTableNode } from '../../ast/query.js';
3
3
  import { ColumnNode } from '../../ast/expression.js';
4
- import { FunctionTableFormatter, FunctionTableNode } from './function-table-formatter.js';
4
+ import { FunctionTableFormatter } from './function-table-formatter.js';
5
5
  import { PaginationStrategy, StandardLimitOffsetPagination } from './pagination-strategy.js';
6
6
  import { CteCompiler } from './cte-compiler.js';
7
7
  import { ReturningStrategy, NoReturningStrategy } from './returning-strategy.js';
@@ -63,11 +63,9 @@ export abstract class SqlDialectBase extends Dialect {
63
63
  return this.returningStrategy.compileReturning(returning, ctx);
64
64
  }
65
65
 
66
- private compileInsertColumnList(columns: ColumnNode[]): string {
67
- return columns
68
- .map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`)
69
- .join(', ');
70
- }
66
+ private compileInsertColumnList(columns: ColumnNode[]): string {
67
+ return columns.map(column => this.quoteIdentifier(column.name)).join(', ');
68
+ }
71
69
 
72
70
  private compileInsertValues(values: any[][], ctx: CompilerContext): string {
73
71
  return values
@@ -99,15 +97,15 @@ export abstract class SqlDialectBase extends Dialect {
99
97
  assignments: { column: ColumnNode; value: any }[],
100
98
  ctx: CompilerContext
101
99
  ): string {
102
- return assignments
103
- .map(assignment => {
104
- const col = assignment.column;
105
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
106
- const value = this.compileOperand(assignment.value, ctx);
107
- return `${target} = ${value}`;
108
- })
109
- .join(', ');
110
- }
100
+ return assignments
101
+ .map(assignment => {
102
+ const col = assignment.column;
103
+ const target = this.quoteIdentifier(col.name);
104
+ const value = this.compileOperand(assignment.value, ctx);
105
+ return `${target} = ${value}`;
106
+ })
107
+ .join(', ');
108
+ }
111
109
 
112
110
  protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
113
111
  const table = this.compileTableName(ast.from);
@@ -135,29 +133,49 @@ export abstract class SqlDialectBase extends Dialect {
135
133
  }).join(', ');
136
134
  }
137
135
 
138
- protected compileFrom(ast: SelectQueryNode['from'], ctx?: CompilerContext): string {
139
- const tableSource = ast as any;
140
- if (tableSource.type === 'FunctionTable') {
141
- return this.compileFunctionTable(tableSource, ctx);
142
- }
143
- return this.compileTableSource(tableSource);
144
- }
145
-
146
- protected compileFunctionTable(fn: FunctionTableNode, ctx?: CompilerContext): string {
147
- return FunctionTableFormatter.format(fn, ctx, this);
148
- }
149
-
150
- protected compileTableSource(table: TableSourceNode): string {
151
- const base = this.compileTableName(table);
152
- return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
153
- }
154
-
155
- protected compileTableName(table: { name: string; schema?: string }): string {
156
- if (table.schema) {
157
- return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
158
- }
159
- return this.quoteIdentifier(table.name);
160
- }
136
+ protected compileFrom(ast: SelectQueryNode['from'], ctx?: CompilerContext): string {
137
+ const tableSource = ast as any;
138
+ if (tableSource.type === 'FunctionTable') {
139
+ return this.compileFunctionTable(tableSource, ctx);
140
+ }
141
+ if (tableSource.type === 'DerivedTable') {
142
+ return this.compileDerivedTable(tableSource, ctx);
143
+ }
144
+ return this.compileTableSource(tableSource);
145
+ }
146
+
147
+ protected compileFunctionTable(fn: FunctionTableNode, ctx?: CompilerContext): string {
148
+ return FunctionTableFormatter.format(fn, ctx, this);
149
+ }
150
+
151
+ protected compileDerivedTable(table: DerivedTableNode, ctx?: CompilerContext): string {
152
+ if (!table.alias) {
153
+ throw new Error('Derived tables must have an alias.');
154
+ }
155
+ const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx!).trim().replace(/;$/, '');
156
+ const columns = table.columnAliases?.length
157
+ ? ` (${table.columnAliases.map(c => this.quoteIdentifier(c)).join(', ')})`
158
+ : '';
159
+ return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
160
+ }
161
+
162
+ protected compileTableSource(table: TableSourceNode): string {
163
+ if (table.type === 'FunctionTable') {
164
+ return this.compileFunctionTable(table as FunctionTableNode);
165
+ }
166
+ if (table.type === 'DerivedTable') {
167
+ return this.compileDerivedTable(table as DerivedTableNode);
168
+ }
169
+ const base = this.compileTableName(table);
170
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
171
+ }
172
+
173
+ protected compileTableName(table: { name: string; schema?: string }): string {
174
+ if (table.schema) {
175
+ return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
176
+ }
177
+ return this.quoteIdentifier(table.name);
178
+ }
161
179
 
162
180
  protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
163
181
  if (!ast.having) return '';
@@ -172,10 +190,4 @@ export abstract class SqlDialectBase extends Dialect {
172
190
  const trimmed = this.stripTrailingSemicolon(sql);
173
191
  return `(${trimmed})`;
174
192
  }
175
- }
176
-
177
- interface TableSourceNode {
178
- name: string;
179
- schema?: string;
180
- alias?: string;
181
- }
193
+ }