metal-orm 1.0.17 → 1.0.19

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 (39) hide show
  1. package/README.md +4 -3
  2. package/dist/decorators/index.cjs +192 -46
  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 +192 -46
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +245 -66
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +16 -29
  11. package/dist/index.d.ts +16 -29
  12. package/dist/index.js +243 -66
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BPCn6MOH.d.cts → select-BuMpVcVt.d.cts} +83 -11
  15. package/dist/{select-BPCn6MOH.d.ts → select-BuMpVcVt.d.ts} +83 -11
  16. package/package.json +61 -54
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/builders.ts +23 -3
  19. package/src/core/ast/expression-builders.ts +14 -1
  20. package/src/core/ast/expression-nodes.ts +11 -9
  21. package/src/core/ast/join-node.ts +5 -3
  22. package/src/core/ast/join.ts +16 -16
  23. package/src/core/ast/query.ts +44 -29
  24. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  25. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  26. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  28. package/src/core/dialect/base/sql-dialect.ts +58 -46
  29. package/src/core/dialect/mssql/index.ts +53 -28
  30. package/src/core/dialect/sqlite/index.ts +22 -13
  31. package/src/query-builder/column-selector.ts +9 -7
  32. package/src/query-builder/query-ast-service.ts +59 -38
  33. package/src/query-builder/relation-conditions.ts +38 -34
  34. package/src/query-builder/relation-manager.ts +8 -3
  35. package/src/query-builder/relation-service.ts +59 -46
  36. package/src/query-builder/select-query-state.ts +19 -7
  37. package/src/query-builder/select.ts +215 -135
  38. package/src/schema/column.ts +75 -39
  39. package/src/schema/types.ts +1 -0
@@ -1,4 +1,4 @@
1
- import type { TableNode, FunctionTableNode } from '../core/ast/query.js';
1
+ import type { TableNode, FunctionTableNode, DerivedTableNode, TableSourceNode } from '../core/ast/query.js';
2
2
  import type { ColumnNode } from '../core/ast/expression.js';
3
3
 
4
4
  /**
@@ -7,10 +7,10 @@ import type { ColumnNode } from '../core/ast/expression.js';
7
7
  export interface NamingStrategy {
8
8
  /**
9
9
  * Converts a table name to a TypeScript symbol name
10
- * @param table - Table node, function table node, or name
11
- * @returns Valid TypeScript identifier
12
- */
13
- tableToSymbol(table: TableNode | FunctionTableNode | string): string;
10
+ * @param table - Table node, function table node, or name
11
+ * @returns Valid TypeScript identifier
12
+ */
13
+ tableToSymbol(table: TableSourceNode | string): string;
14
14
 
15
15
  /**
16
16
  * Converts a column reference to a property name
@@ -27,11 +27,16 @@ export interface NamingStrategy {
27
27
  export class DefaultNamingStrategy implements NamingStrategy {
28
28
  /**
29
29
  * Converts table names to TypeScript symbols
30
- * @param table - Table node, function table node, or string name
31
- * @returns Capitalized table name (handles schema-qualified names)
32
- */
33
- tableToSymbol(table: TableNode | FunctionTableNode | string): string {
34
- const tableName = typeof table === 'string' ? table : table.name;
30
+ * @param table - Table node, function table node, or string name
31
+ * @returns Capitalized table name (handles schema-qualified names)
32
+ */
33
+ tableToSymbol(table: TableNode | FunctionTableNode | DerivedTableNode | string): string {
34
+ const tableName =
35
+ typeof table === 'string'
36
+ ? table
37
+ : table.type === 'DerivedTable'
38
+ ? table.alias
39
+ : table.name;
35
40
 
36
41
  // Handle schema-qualified names (e.g., "auth.user" → "AuthUser")
37
42
  if (tableName.includes('.')) {
@@ -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
+ });
@@ -63,7 +63,20 @@ const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode =>
63
63
  return toNode(val);
64
64
  };
65
65
 
66
- 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 });
67
80
 
68
81
  const createBinaryExpression = (
69
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
@@ -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,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
+ }
@@ -1,7 +1,8 @@
1
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
- import { MssqlFunctionStrategy } from './functions.js';
2
+ import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode, DerivedTableNode } from '../../ast/query.js';
3
+ import { JsonPathNode } from '../../ast/expression.js';
4
+ import { MssqlFunctionStrategy } from './functions.js';
5
+ import { FunctionTableFormatter } from '../base/function-table-formatter.js';
5
6
 
6
7
  /**
7
8
  * Microsoft SQL Server dialect implementation
@@ -94,13 +95,16 @@ export class SqlServerDialect extends Dialect {
94
95
  return `UPDATE ${table} SET ${assignments}${whereClause};`;
95
96
  }
96
97
 
97
- protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
98
- const table = this.quoteIdentifier(ast.from.name);
99
- const whereClause = this.compileWhere(ast.where, ctx);
100
- return `DELETE FROM ${table}${whereClause};`;
101
- }
102
-
103
- private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
98
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
99
+ if (ast.from.type !== 'Table') {
100
+ throw new Error('DELETE only supports base tables in the MSSQL dialect.');
101
+ }
102
+ const table = this.quoteIdentifier(ast.from.name);
103
+ const whereClause = this.compileWhere(ast.where, ctx);
104
+ return `DELETE FROM ${table}${whereClause};`;
105
+ }
106
+
107
+ private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
104
108
  const columns = ast.columns.map(c => {
105
109
  let expr = '';
106
110
  if (c.type === 'Function') {
@@ -119,15 +123,15 @@ export class SqlServerDialect extends Dialect {
119
123
  }
120
124
  return expr;
121
125
  }).join(', ');
122
-
123
- const distinct = ast.distinct ? 'DISTINCT ' : '';
124
- const from = `${this.quoteIdentifier(ast.from.name)}`;
125
-
126
- const joins = ast.joins.map(j => {
127
- const table = this.quoteIdentifier(j.table.name);
128
- const cond = this.compileExpression(j.condition, ctx);
129
- return `${j.kind} JOIN ${table} ON ${cond}`;
130
- }).join(' ');
126
+
127
+ const distinct = ast.distinct ? 'DISTINCT ' : '';
128
+ const from = this.compileTableSource(ast.from, ctx);
129
+
130
+ const joins = ast.joins.map(j => {
131
+ const table = this.compileTableSource(j.table, ctx);
132
+ const cond = this.compileExpression(j.condition, ctx);
133
+ return `${j.kind} JOIN ${table} ON ${cond}`;
134
+ }).join(' ');
131
135
  const whereClause = this.compileWhere(ast.where, ctx);
132
136
 
133
137
  const groupBy = ast.groupBy && ast.groupBy.length > 0
@@ -155,19 +159,40 @@ export class SqlServerDialect extends Dialect {
155
159
  .join(', ');
156
160
  }
157
161
 
158
- private compilePagination(ast: SelectQueryNode, orderBy: string): string {
159
- const hasLimit = ast.limit !== undefined;
160
- const hasOffset = ast.offset !== undefined;
161
- if (!hasLimit && !hasOffset) return '';
162
+ private compilePagination(ast: SelectQueryNode, orderBy: string): string {
163
+ const hasLimit = ast.limit !== undefined;
164
+ const hasOffset = ast.offset !== undefined;
165
+ if (!hasLimit && !hasOffset) return '';
162
166
 
163
167
  const off = ast.offset ?? 0;
164
168
  const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
165
169
  let pagination = `${orderClause} OFFSET ${off} ROWS`;
166
- if (hasLimit) {
167
- pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
168
- }
169
- return pagination;
170
- }
170
+ if (hasLimit) {
171
+ pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
172
+ }
173
+ return pagination;
174
+ }
175
+
176
+ private compileTableSource(table: TableSourceNode, ctx: CompilerContext): string {
177
+ if (table.type === 'FunctionTable') {
178
+ return FunctionTableFormatter.format(table, ctx, this as any);
179
+ }
180
+ if (table.type === 'DerivedTable') {
181
+ return this.compileDerivedTable(table, ctx);
182
+ }
183
+ const base = table.schema
184
+ ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`
185
+ : this.quoteIdentifier(table.name);
186
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
187
+ }
188
+
189
+ private compileDerivedTable(table: DerivedTableNode, ctx: CompilerContext): string {
190
+ const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, '');
191
+ const cols = table.columnAliases?.length
192
+ ? ` (${table.columnAliases.map(c => this.quoteIdentifier(c)).join(', ')})`
193
+ : '';
194
+ return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
195
+ }
171
196
 
172
197
  private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
173
198
  if (!ast.ctes || ast.ctes.length === 0) return '';