metal-orm 1.0.39 → 1.0.40
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/dist/index.cjs +230 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -24
- package/dist/index.d.ts +71 -24
- package/dist/index.js +225 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen/typescript.ts +60 -3
- package/src/core/ast/aggregate-functions.ts +15 -15
- package/src/core/ast/expression-builders.ts +357 -316
- package/src/core/ast/expression-nodes.ts +208 -186
- package/src/core/ast/expression-visitor.ts +40 -30
- package/src/core/ast/query.ts +142 -132
- package/src/core/ast/window-functions.ts +86 -86
- package/src/core/dialect/abstract.ts +505 -479
- package/src/core/dialect/base/groupby-compiler.ts +6 -6
- package/src/core/dialect/base/orderby-compiler.ts +20 -6
- package/src/core/dialect/base/sql-dialect.ts +154 -136
- package/src/core/dialect/mssql/index.ts +172 -161
- package/src/core/functions/standard-strategy.ts +46 -37
- package/src/query-builder/hydration-manager.ts +93 -79
- package/src/query-builder/query-ast-service.ts +207 -170
- package/src/query-builder/select-query-state.ts +169 -162
- package/src/query-builder/select.ts +15 -23
|
@@ -1,100 +1,101 @@
|
|
|
1
|
-
import { CompilerContext, Dialect } from '../abstract.js';
|
|
2
|
-
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode, DerivedTableNode } from '../../ast/query.js';
|
|
1
|
+
import { CompilerContext, Dialect } from '../abstract.js';
|
|
2
|
+
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode, DerivedTableNode, OrderByNode } from '../../ast/query.js';
|
|
3
3
|
import { JsonPathNode } from '../../ast/expression.js';
|
|
4
4
|
import { MssqlFunctionStrategy } from './functions.js';
|
|
5
5
|
import { FunctionTableFormatter } from '../base/function-table-formatter.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
* @
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*
|
|
42
|
-
* @
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
* @param
|
|
52
|
-
* @
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
6
|
+
import { OrderByCompiler } from '../base/orderby-compiler.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Microsoft SQL Server dialect implementation
|
|
10
|
+
*/
|
|
11
|
+
export class SqlServerDialect extends Dialect {
|
|
12
|
+
protected readonly dialect = 'mssql';
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new SqlServerDialect instance
|
|
15
|
+
*/
|
|
16
|
+
public constructor() {
|
|
17
|
+
super(new MssqlFunctionStrategy());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Quotes an identifier using SQL Server bracket syntax
|
|
22
|
+
* @param id - Identifier to quote
|
|
23
|
+
* @returns Quoted identifier
|
|
24
|
+
*/
|
|
25
|
+
quoteIdentifier(id: string): string {
|
|
26
|
+
return `[${id}]`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compiles JSON path expression using SQL Server syntax
|
|
31
|
+
* @param node - JSON path node
|
|
32
|
+
* @returns SQL Server JSON path expression
|
|
33
|
+
*/
|
|
34
|
+
protected compileJsonPath(node: JsonPathNode): string {
|
|
35
|
+
const col = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
36
|
+
// SQL Server uses JSON_VALUE(col, '$.path')
|
|
37
|
+
return `JSON_VALUE(${col}, '${node.path}')`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Formats parameter placeholders using SQL Server named parameter syntax
|
|
42
|
+
* @param index - Parameter index
|
|
43
|
+
* @returns Named parameter placeholder
|
|
44
|
+
*/
|
|
45
|
+
protected formatPlaceholder(index: number): string {
|
|
46
|
+
return `@p${index}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compiles SELECT query AST to SQL Server SQL
|
|
51
|
+
* @param ast - Query AST
|
|
52
|
+
* @param ctx - Compiler context
|
|
53
|
+
* @returns SQL Server SQL string
|
|
54
|
+
*/
|
|
55
|
+
protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
56
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
57
|
+
const ctes = this.compileCtes(ast, ctx);
|
|
58
|
+
|
|
59
|
+
const baseAst: SelectQueryNode = hasSetOps
|
|
60
|
+
? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
|
|
61
|
+
: ast;
|
|
62
|
+
|
|
63
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
64
|
+
|
|
65
|
+
if (!hasSetOps) {
|
|
66
|
+
return `${ctes}${baseSelect}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const compound = ast.setOps!
|
|
70
|
+
.map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
|
|
71
|
+
.join(' ');
|
|
72
|
+
|
|
73
|
+
const orderBy = this.compileOrderBy(ast, ctx);
|
|
74
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
75
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
76
|
+
const tail = pagination || orderBy;
|
|
77
|
+
return `${ctes}${combined}${tail}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
81
|
+
const table = this.quoteIdentifier(ast.into.name);
|
|
82
|
+
const columnList = ast.columns.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(', ');
|
|
83
|
+
const values = ast.values.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`).join(', ');
|
|
84
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
88
|
+
const table = this.quoteIdentifier(ast.table.name);
|
|
89
|
+
const assignments = ast.set.map(assignment => {
|
|
90
|
+
const col = assignment.column;
|
|
91
|
+
const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
|
|
92
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
93
|
+
return `${target} = ${value}`;
|
|
94
|
+
}).join(', ');
|
|
95
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
96
|
+
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
97
|
+
}
|
|
98
|
+
|
|
98
99
|
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
99
100
|
if (ast.from.type !== 'Table') {
|
|
100
101
|
throw new Error('DELETE only supports base tables in the MSSQL dialect.');
|
|
@@ -105,24 +106,24 @@ export class SqlServerDialect extends Dialect {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
108
|
-
const columns = ast.columns.map(c => {
|
|
109
|
-
let expr = '';
|
|
110
|
-
if (c.type === 'Function') {
|
|
111
|
-
expr = this.compileOperand(c, ctx);
|
|
112
|
-
} else if (c.type === 'Column') {
|
|
113
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
114
|
-
} else if (c.type === 'ScalarSubquery') {
|
|
115
|
-
expr = this.compileOperand(c, ctx);
|
|
116
|
-
} else if (c.type === 'WindowFunction') {
|
|
117
|
-
expr = this.compileOperand(c, ctx);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (c.alias) {
|
|
121
|
-
if (c.alias.includes('(')) return c.alias;
|
|
122
|
-
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
123
|
-
}
|
|
124
|
-
return expr;
|
|
125
|
-
}).join(', ');
|
|
109
|
+
const columns = ast.columns.map(c => {
|
|
110
|
+
let expr = '';
|
|
111
|
+
if (c.type === 'Function') {
|
|
112
|
+
expr = this.compileOperand(c, ctx);
|
|
113
|
+
} else if (c.type === 'Column') {
|
|
114
|
+
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
115
|
+
} else if (c.type === 'ScalarSubquery') {
|
|
116
|
+
expr = this.compileOperand(c, ctx);
|
|
117
|
+
} else if (c.type === 'WindowFunction') {
|
|
118
|
+
expr = this.compileOperand(c, ctx);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (c.alias) {
|
|
122
|
+
if (c.alias.includes('(')) return c.alias;
|
|
123
|
+
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
124
|
+
}
|
|
125
|
+
return expr;
|
|
126
|
+
}).join(', ');
|
|
126
127
|
|
|
127
128
|
const distinct = ast.distinct ? 'DISTINCT ' : '';
|
|
128
129
|
const from = this.compileTableSource(ast.from, ctx);
|
|
@@ -132,47 +133,57 @@ export class SqlServerDialect extends Dialect {
|
|
|
132
133
|
const cond = this.compileExpression(j.condition, ctx);
|
|
133
134
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
134
135
|
}).join(' ');
|
|
135
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
136
|
-
|
|
137
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0
|
|
138
|
-
? ' GROUP BY ' + ast.groupBy.map(
|
|
139
|
-
: '';
|
|
140
|
-
|
|
141
|
-
const having = ast.having
|
|
142
|
-
? ` HAVING ${this.compileExpression(ast.having, ctx)}`
|
|
143
|
-
: '';
|
|
144
|
-
|
|
145
|
-
const orderBy = this.compileOrderBy(ast);
|
|
146
|
-
const pagination = this.compilePagination(ast, orderBy);
|
|
147
|
-
|
|
148
|
-
if (pagination) {
|
|
149
|
-
return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${pagination}`;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private compileOrderBy(ast: SelectQueryNode): string {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
136
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
137
|
+
|
|
138
|
+
const groupBy = ast.groupBy && ast.groupBy.length > 0
|
|
139
|
+
? ' GROUP BY ' + ast.groupBy.map(term => this.compileOrderingTerm(term, ctx)).join(', ')
|
|
140
|
+
: '';
|
|
141
|
+
|
|
142
|
+
const having = ast.having
|
|
143
|
+
? ` HAVING ${this.compileExpression(ast.having, ctx)}`
|
|
144
|
+
: '';
|
|
145
|
+
|
|
146
|
+
const orderBy = this.compileOrderBy(ast, ctx);
|
|
147
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
148
|
+
|
|
149
|
+
if (pagination) {
|
|
150
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${pagination}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? ' ' + joins : ''}${whereClause}${groupBy}${having}${orderBy}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private compileOrderBy(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
157
|
+
return OrderByCompiler.compileOrderBy(
|
|
158
|
+
ast,
|
|
159
|
+
term => this.compileOrderingTerm(term, ctx),
|
|
160
|
+
this.renderOrderByNulls.bind(this),
|
|
161
|
+
this.renderOrderByCollation.bind(this)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
162
165
|
private compilePagination(ast: SelectQueryNode, orderBy: string): string {
|
|
163
166
|
const hasLimit = ast.limit !== undefined;
|
|
164
167
|
const hasOffset = ast.offset !== undefined;
|
|
165
168
|
if (!hasLimit && !hasOffset) return '';
|
|
166
|
-
|
|
167
|
-
const off = ast.offset ?? 0;
|
|
168
|
-
const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
|
|
169
|
-
let pagination = `${orderClause} OFFSET ${off} ROWS`;
|
|
169
|
+
|
|
170
|
+
const off = ast.offset ?? 0;
|
|
171
|
+
const orderClause = orderBy || ' ORDER BY (SELECT NULL)';
|
|
172
|
+
let pagination = `${orderClause} OFFSET ${off} ROWS`;
|
|
170
173
|
if (hasLimit) {
|
|
171
174
|
pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
|
|
172
175
|
}
|
|
173
176
|
return pagination;
|
|
174
177
|
}
|
|
175
178
|
|
|
179
|
+
private renderOrderByNulls(order: OrderByNode): string | undefined {
|
|
180
|
+
return order.nulls ? ` NULLS ${order.nulls}` : '';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private renderOrderByCollation(order: OrderByNode): string | undefined {
|
|
184
|
+
return order.collation ? ` COLLATE ${order.collation}` : '';
|
|
185
|
+
}
|
|
186
|
+
|
|
176
187
|
private compileTableSource(table: TableSourceNode, ctx: CompilerContext): string {
|
|
177
188
|
if (table.type === 'FunctionTable') {
|
|
178
189
|
return FunctionTableFormatter.format(table, ctx, this as any);
|
|
@@ -193,21 +204,21 @@ export class SqlServerDialect extends Dialect {
|
|
|
193
204
|
: '';
|
|
194
205
|
return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
|
|
195
206
|
}
|
|
196
|
-
|
|
197
|
-
private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
198
|
-
if (!ast.ctes || ast.ctes.length === 0) return '';
|
|
199
|
-
// MSSQL does not use RECURSIVE keyword, but supports recursion when CTE references itself.
|
|
200
|
-
const defs = ast.ctes.map(cte => {
|
|
201
|
-
const name = this.quoteIdentifier(cte.name);
|
|
202
|
-
const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
|
|
203
|
-
const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, '');
|
|
204
|
-
return `${name}${cols} AS (${query})`;
|
|
205
|
-
}).join(', ');
|
|
206
|
-
return `WITH ${defs} `;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
private wrapSetOperand(sql: string): string {
|
|
210
|
-
const trimmed = sql.trim().replace(/;$/, '');
|
|
211
|
-
return `(${trimmed})`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
207
|
+
|
|
208
|
+
private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
209
|
+
if (!ast.ctes || ast.ctes.length === 0) return '';
|
|
210
|
+
// MSSQL does not use RECURSIVE keyword, but supports recursion when CTE references itself.
|
|
211
|
+
const defs = ast.ctes.map(cte => {
|
|
212
|
+
const name = this.quoteIdentifier(cte.name);
|
|
213
|
+
const cols = cte.columns ? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})` : '';
|
|
214
|
+
const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, '');
|
|
215
|
+
return `${name}${cols} AS (${query})`;
|
|
216
|
+
}).join(', ');
|
|
217
|
+
return `WITH ${defs} `;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private wrapSetOperand(sql: string): string {
|
|
221
|
+
const trimmed = sql.trim().replace(/;$/, '');
|
|
222
|
+
return `(${trimmed})`;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { FunctionStrategy, FunctionRenderer, FunctionRenderContext } from './types.js';
|
|
2
|
-
import { LiteralNode, OperandNode } from '../ast/expression.js';
|
|
3
|
-
|
|
4
|
-
export class StandardFunctionStrategy implements FunctionStrategy {
|
|
5
|
-
protected renderers: Map<string, FunctionRenderer> = new Map();
|
|
6
|
-
|
|
7
|
-
constructor() {
|
|
8
|
-
this.registerStandard();
|
|
9
|
-
}
|
|
10
|
-
|
|
2
|
+
import { LiteralNode, OperandNode, isOperandNode } from '../ast/expression.js';
|
|
3
|
+
|
|
4
|
+
export class StandardFunctionStrategy implements FunctionStrategy {
|
|
5
|
+
protected renderers: Map<string, FunctionRenderer> = new Map();
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.registerStandard();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
11
|
protected registerStandard() {
|
|
12
12
|
// Register ANSI standard implementations
|
|
13
13
|
this.add('COUNT', ({ compiledArgs }) => `COUNT(${compiledArgs.join(', ')})`);
|
|
@@ -18,36 +18,36 @@ export class StandardFunctionStrategy implements FunctionStrategy {
|
|
|
18
18
|
this.add('ABS', ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
|
|
19
19
|
this.add('UPPER', ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
|
|
20
20
|
this.add('LOWER', ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
|
|
21
|
-
this.add('LENGTH', ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
|
|
22
|
-
this.add('TRIM', ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
|
|
23
|
-
this.add('LTRIM', ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
|
|
24
|
-
this.add('RTRIM', ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
|
|
25
|
-
this.add('SUBSTRING', ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(', ')})`);
|
|
26
|
-
this.add('CONCAT', ({ compiledArgs }) => `CONCAT(${compiledArgs.join(', ')})`);
|
|
27
|
-
this.add('NOW', () => `NOW()`);
|
|
28
|
-
this.add('CURRENT_DATE', () => `CURRENT_DATE`);
|
|
29
|
-
this.add('CURRENT_TIME', () => `CURRENT_TIME`);
|
|
30
|
-
this.add('EXTRACT', ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
|
|
31
|
-
this.add('YEAR', ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
|
|
32
|
-
this.add('MONTH', ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
|
|
33
|
-
this.add('DAY', ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
|
|
34
|
-
this.add('DATE_ADD', ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
35
|
-
this.add('DATE_SUB', ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
36
|
-
this.add('DATE_DIFF', ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
37
|
-
this.add('DATE_FORMAT', ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
38
|
-
this.add('UNIX_TIMESTAMP', () => `UNIX_TIMESTAMP()`);
|
|
39
|
-
this.add('FROM_UNIXTIME', ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
|
|
40
|
-
this.add('END_OF_MONTH', ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
|
|
41
|
-
this.add('DAY_OF_WEEK', ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
|
|
21
|
+
this.add('LENGTH', ({ compiledArgs }) => `LENGTH(${compiledArgs[0]})`);
|
|
22
|
+
this.add('TRIM', ({ compiledArgs }) => `TRIM(${compiledArgs[0]})`);
|
|
23
|
+
this.add('LTRIM', ({ compiledArgs }) => `LTRIM(${compiledArgs[0]})`);
|
|
24
|
+
this.add('RTRIM', ({ compiledArgs }) => `RTRIM(${compiledArgs[0]})`);
|
|
25
|
+
this.add('SUBSTRING', ({ compiledArgs }) => `SUBSTRING(${compiledArgs.join(', ')})`);
|
|
26
|
+
this.add('CONCAT', ({ compiledArgs }) => `CONCAT(${compiledArgs.join(', ')})`);
|
|
27
|
+
this.add('NOW', () => `NOW()`);
|
|
28
|
+
this.add('CURRENT_DATE', () => `CURRENT_DATE`);
|
|
29
|
+
this.add('CURRENT_TIME', () => `CURRENT_TIME`);
|
|
30
|
+
this.add('EXTRACT', ({ compiledArgs }) => `EXTRACT(${compiledArgs[0]} FROM ${compiledArgs[1]})`);
|
|
31
|
+
this.add('YEAR', ({ compiledArgs }) => `EXTRACT(YEAR FROM ${compiledArgs[0]})`);
|
|
32
|
+
this.add('MONTH', ({ compiledArgs }) => `EXTRACT(MONTH FROM ${compiledArgs[0]})`);
|
|
33
|
+
this.add('DAY', ({ compiledArgs }) => `EXTRACT(DAY FROM ${compiledArgs[0]})`);
|
|
34
|
+
this.add('DATE_ADD', ({ compiledArgs }) => `(${compiledArgs[0]} + INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
35
|
+
this.add('DATE_SUB', ({ compiledArgs }) => `(${compiledArgs[0]} - INTERVAL ${compiledArgs[1]} ${compiledArgs[2]})`);
|
|
36
|
+
this.add('DATE_DIFF', ({ compiledArgs }) => `DATEDIFF(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
37
|
+
this.add('DATE_FORMAT', ({ compiledArgs }) => `DATE_FORMAT(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
38
|
+
this.add('UNIX_TIMESTAMP', () => `UNIX_TIMESTAMP()`);
|
|
39
|
+
this.add('FROM_UNIXTIME', ({ compiledArgs }) => `FROM_UNIXTIME(${compiledArgs[0]})`);
|
|
40
|
+
this.add('END_OF_MONTH', ({ compiledArgs }) => `LAST_DAY(${compiledArgs[0]})`);
|
|
41
|
+
this.add('DAY_OF_WEEK', ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
|
|
42
42
|
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
|
|
43
43
|
this.add('DATE_TRUNC', ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
|
|
44
44
|
this.add('GROUP_CONCAT', ctx => this.renderGroupConcat(ctx));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
protected add(name: string, renderer: FunctionRenderer) {
|
|
48
|
-
this.renderers.set(name, renderer);
|
|
49
|
-
}
|
|
50
|
-
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected add(name: string, renderer: FunctionRenderer) {
|
|
48
|
+
this.renderers.set(name, renderer);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
51
|
getRenderer(name: string): FunctionRenderer | undefined {
|
|
52
52
|
return this.renderers.get(name);
|
|
53
53
|
}
|
|
@@ -65,7 +65,16 @@ export class StandardFunctionStrategy implements FunctionStrategy {
|
|
|
65
65
|
if (!orderBy || orderBy.length === 0) {
|
|
66
66
|
return '';
|
|
67
67
|
}
|
|
68
|
-
const parts = orderBy.map(order =>
|
|
68
|
+
const parts = orderBy.map(order => {
|
|
69
|
+
const term = isOperandNode(order.term)
|
|
70
|
+
? ctx.compileOperand(order.term)
|
|
71
|
+
: (() => {
|
|
72
|
+
throw new Error('ORDER BY expressions inside functions must be operands');
|
|
73
|
+
})();
|
|
74
|
+
const collation = order.collation ? ` COLLATE ${order.collation}` : '';
|
|
75
|
+
const nulls = order.nulls ? ` NULLS ${order.nulls}` : '';
|
|
76
|
+
return `${term} ${order.direction}${collation}${nulls}`;
|
|
77
|
+
});
|
|
69
78
|
return `ORDER BY ${parts.join(', ')}`;
|
|
70
79
|
}
|
|
71
80
|
|