metal-orm 1.0.17 → 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.
- package/README.md +4 -3
- package/dist/decorators/index.cjs +192 -46
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +1 -1
- package/dist/decorators/index.d.ts +1 -1
- package/dist/decorators/index.js +192 -46
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +245 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -29
- package/dist/index.d.ts +16 -29
- package/dist/index.js +243 -66
- package/dist/index.js.map +1 -1
- package/dist/{select-BPCn6MOH.d.cts → select-BuMpVcVt.d.cts} +83 -11
- package/dist/{select-BPCn6MOH.d.ts → select-BuMpVcVt.d.ts} +83 -11
- package/package.json +4 -1
- package/src/codegen/naming-strategy.ts +15 -10
- package/src/core/ast/builders.ts +23 -3
- package/src/core/ast/expression-builders.ts +14 -1
- package/src/core/ast/expression-nodes.ts +11 -9
- package/src/core/ast/join-node.ts +5 -3
- package/src/core/ast/join.ts +16 -16
- package/src/core/ast/query.ts +44 -29
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
- package/src/core/dialect/base/sql-dialect.ts +58 -46
- package/src/core/dialect/mssql/index.ts +53 -28
- package/src/core/dialect/sqlite/index.ts +22 -13
- package/src/query-builder/column-selector.ts +9 -7
- package/src/query-builder/query-ast-service.ts +59 -38
- package/src/query-builder/relation-conditions.ts +38 -34
- package/src/query-builder/relation-manager.ts +8 -3
- package/src/query-builder/relation-service.ts +59 -46
- package/src/query-builder/select-query-state.ts +19 -7
- package/src/query-builder/select.ts +215 -135
- package/src/schema/column.ts +75 -39
- package/src/schema/types.ts +1 -0
|
@@ -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 |
|
|
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'
|
|
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
|
});
|
package/src/core/ast/join.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
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
|
+
}
|
package/src/core/ast/query.ts
CHANGED
|
@@ -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
|
|
49
|
-
*/
|
|
50
|
-
export interface
|
|
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:
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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 =
|
|
125
|
-
|
|
126
|
-
const joins = ast.joins.map(j => {
|
|
127
|
-
const table = this.
|
|
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 '';
|
|
@@ -6,14 +6,14 @@ import { SqliteFunctionStrategy } from './functions.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* SQLite dialect implementation
|
|
8
8
|
*/
|
|
9
|
-
export class SqliteDialect extends SqlDialectBase {
|
|
10
|
-
protected readonly dialect = 'sqlite';
|
|
11
|
-
/**
|
|
12
|
-
* Creates a new SqliteDialect instance
|
|
13
|
-
*/
|
|
14
|
-
public constructor() {
|
|
15
|
-
super(new SqliteFunctionStrategy());
|
|
16
|
-
}
|
|
9
|
+
export class SqliteDialect extends SqlDialectBase {
|
|
10
|
+
protected readonly dialect = 'sqlite';
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new SqliteDialect instance
|
|
13
|
+
*/
|
|
14
|
+
public constructor() {
|
|
15
|
+
super(new SqliteFunctionStrategy());
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Quotes an identifier using SQLite double-quote syntax
|
|
@@ -35,11 +35,20 @@ export class SqliteDialect extends SqlDialectBase {
|
|
|
35
35
|
return `json_extract(${col}, '${node.path}')`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
|
|
39
|
-
if (!returning || returning.length === 0) return '';
|
|
40
|
-
const columns = this.formatReturningColumns(returning);
|
|
41
|
-
return ` RETURNING ${columns}`;
|
|
42
|
-
}
|
|
38
|
+
protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
|
|
39
|
+
if (!returning || returning.length === 0) return '';
|
|
40
|
+
const columns = this.formatReturningColumns(returning);
|
|
41
|
+
return ` RETURNING ${columns}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected formatReturningColumns(returning: ColumnNode[]): string {
|
|
45
|
+
return returning
|
|
46
|
+
.map(column => {
|
|
47
|
+
const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : '';
|
|
48
|
+
return `${this.quoteIdentifier(column.name)}${alias}`;
|
|
49
|
+
})
|
|
50
|
+
.join(', ');
|
|
51
|
+
}
|
|
43
52
|
|
|
44
53
|
supportsReturning(): boolean {
|
|
45
54
|
return true;
|
|
@@ -69,10 +69,12 @@ export class ColumnSelector {
|
|
|
69
69
|
* @param columns - Columns to make distinct
|
|
70
70
|
* @returns Updated query context with DISTINCT clause
|
|
71
71
|
*/
|
|
72
|
-
distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
72
|
+
distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
|
|
73
|
+
const from = context.state.ast.from;
|
|
74
|
+
const tableRef = from.type === 'Table' && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
|
|
75
|
+
const nodes = columns.map(col => buildColumnNode(tableRef, col));
|
|
76
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
77
|
+
const nextState = astService.withDistinct(nodes);
|
|
78
|
+
return { state: nextState, hydration: context.hydration };
|
|
79
|
+
}
|
|
80
|
+
}
|