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.
- package/README.md +37 -40
- package/dist/decorators/index.cjs +344 -69
- 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 +344 -69
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +567 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -30
- package/dist/index.d.ts +66 -30
- package/dist/index.js +559 -181
- package/dist/index.js.map +1 -1
- package/dist/{select-BKZrMRCQ.d.cts → select-BuMpVcVt.d.cts} +265 -74
- package/dist/{select-BKZrMRCQ.d.ts → select-BuMpVcVt.d.ts} +265 -74
- package/package.json +5 -1
- package/src/codegen/naming-strategy.ts +15 -10
- package/src/core/ast/aggregate-functions.ts +50 -4
- package/src/core/ast/builders.ts +23 -3
- package/src/core/ast/expression-builders.ts +36 -16
- package/src/core/ast/expression-nodes.ts +17 -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/ddl/introspect/functions/postgres.ts +2 -6
- package/src/core/dialect/abstract.ts +12 -8
- package/src/core/dialect/base/sql-dialect.ts +58 -46
- package/src/core/dialect/mssql/functions.ts +24 -15
- package/src/core/dialect/mssql/index.ts +53 -28
- package/src/core/dialect/postgres/functions.ts +33 -24
- package/src/core/dialect/sqlite/functions.ts +19 -12
- package/src/core/dialect/sqlite/index.ts +22 -13
- package/src/core/functions/datetime.ts +2 -1
- package/src/core/functions/numeric.ts +2 -1
- package/src/core/functions/standard-strategy.ts +52 -12
- package/src/core/functions/text.ts +2 -1
- package/src/core/functions/types.ts +8 -8
- package/src/index.ts +5 -4
- package/src/orm/domain-event-bus.ts +43 -25
- package/src/orm/entity-meta.ts +40 -0
- package/src/orm/execution-context.ts +6 -0
- package/src/orm/hydration-context.ts +6 -4
- package/src/orm/orm-session.ts +35 -24
- package/src/orm/orm.ts +10 -10
- package/src/orm/query-logger.ts +15 -0
- package/src/orm/runtime-types.ts +60 -2
- package/src/orm/transaction-runner.ts +7 -0
- package/src/orm/unit-of-work.ts +1 -0
- package/src/query-builder/column-selector.ts +9 -7
- package/src/query-builder/insert-query-state.ts +13 -3
- 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-helpers.ts +50 -0
- package/src/query-builder/select-query-state.ts +19 -7
- package/src/query-builder/select.ts +339 -167
- package/src/query-builder/update-query-state.ts +31 -9
- package/src/schema/column.ts +75 -39
- 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
|
+
});
|
package/src/core/ast/builders.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
28
|
-
if (
|
|
29
|
-
value
|
|
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
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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 |
|
|
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,14 +1,10 @@
|
|
|
1
1
|
// Small helpers to build Postgres-specific function calls as AST FunctionNodes
|
|
2
|
-
import {
|
|
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({
|
|
484
|
-
|
|
485
|
-
|
|
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
|
|
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
|
+
}
|