@leonardovida-md/drizzle-neo-duckdb 1.1.4 → 1.2.0
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 +2 -3
- package/dist/dialect.d.ts +3 -1
- package/dist/driver.d.ts +1 -3
- package/dist/duckdb-introspect.mjs +355 -829
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +371 -832
- package/dist/operators.d.ts +8 -0
- package/dist/options.d.ts +0 -3
- package/dist/session.d.ts +2 -5
- package/dist/sql/ast-transformer.d.ts +15 -0
- package/dist/sql/visitors/array-operators.d.ts +5 -0
- package/dist/sql/visitors/column-qualifier.d.ts +5 -0
- package/dist/utils.d.ts +0 -1
- package/package.json +3 -2
- package/src/dialect.ts +20 -0
- package/src/driver.ts +0 -8
- package/src/index.ts +1 -0
- package/src/operators.ts +27 -0
- package/src/options.ts +0 -15
- package/src/session.ts +10 -96
- package/src/sql/ast-transformer.ts +68 -0
- package/src/sql/visitors/array-operators.ts +214 -0
- package/src/sql/visitors/column-qualifier.ts +278 -0
- package/src/utils.ts +0 -1
- package/dist/sql/query-rewriters.d.ts +0 -15
- package/src/sql/query-rewriters.ts +0 -1161
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST visitor to transform Postgres array operators to DuckDB functions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AST,
|
|
7
|
+
Binary,
|
|
8
|
+
ExpressionValue,
|
|
9
|
+
Select,
|
|
10
|
+
From,
|
|
11
|
+
Join,
|
|
12
|
+
} from 'node-sql-parser';
|
|
13
|
+
|
|
14
|
+
const OPERATOR_MAP: Record<string, { fn: string; swap?: boolean }> = {
|
|
15
|
+
'@>': { fn: 'array_has_all' },
|
|
16
|
+
'<@': { fn: 'array_has_all', swap: true },
|
|
17
|
+
'&&': { fn: 'array_has_any' },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function walkExpression(
|
|
21
|
+
expr: ExpressionValue | null | undefined,
|
|
22
|
+
parent?: object,
|
|
23
|
+
key?: string
|
|
24
|
+
): boolean {
|
|
25
|
+
if (!expr || typeof expr !== 'object') return false;
|
|
26
|
+
|
|
27
|
+
let transformed = false;
|
|
28
|
+
const exprObj = expr as Record<string, unknown>;
|
|
29
|
+
|
|
30
|
+
if ('type' in expr && exprObj.type === 'binary_expr') {
|
|
31
|
+
const binary = expr as Binary;
|
|
32
|
+
const mapping = OPERATOR_MAP[binary.operator];
|
|
33
|
+
|
|
34
|
+
if (mapping) {
|
|
35
|
+
const fnExpr = {
|
|
36
|
+
type: 'function' as const,
|
|
37
|
+
name: { name: [{ type: 'default', value: mapping.fn }] },
|
|
38
|
+
args: {
|
|
39
|
+
type: 'expr_list' as const,
|
|
40
|
+
value: mapping.swap
|
|
41
|
+
? [binary.right, binary.left]
|
|
42
|
+
: [binary.left, binary.right],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (parent && key) {
|
|
47
|
+
(parent as Record<string, unknown>)[key] = fnExpr;
|
|
48
|
+
}
|
|
49
|
+
transformed = true;
|
|
50
|
+
} else {
|
|
51
|
+
transformed =
|
|
52
|
+
walkExpression(binary.left as ExpressionValue, binary, 'left') ||
|
|
53
|
+
transformed;
|
|
54
|
+
transformed =
|
|
55
|
+
walkExpression(binary.right as ExpressionValue, binary, 'right') ||
|
|
56
|
+
transformed;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if ('type' in expr && exprObj.type === 'unary_expr') {
|
|
61
|
+
if ('expr' in exprObj) {
|
|
62
|
+
transformed =
|
|
63
|
+
walkExpression(exprObj.expr as ExpressionValue, exprObj, 'expr') ||
|
|
64
|
+
transformed;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ('type' in expr && exprObj.type === 'case') {
|
|
69
|
+
if ('expr' in exprObj && exprObj.expr) {
|
|
70
|
+
transformed =
|
|
71
|
+
walkExpression(exprObj.expr as ExpressionValue, exprObj, 'expr') ||
|
|
72
|
+
transformed;
|
|
73
|
+
}
|
|
74
|
+
if ('args' in exprObj && Array.isArray(exprObj.args)) {
|
|
75
|
+
for (let i = 0; i < exprObj.args.length; i++) {
|
|
76
|
+
const whenClause = exprObj.args[i] as Record<string, unknown>;
|
|
77
|
+
if (whenClause.cond) {
|
|
78
|
+
transformed =
|
|
79
|
+
walkExpression(
|
|
80
|
+
whenClause.cond as ExpressionValue,
|
|
81
|
+
whenClause,
|
|
82
|
+
'cond'
|
|
83
|
+
) || transformed;
|
|
84
|
+
}
|
|
85
|
+
if (whenClause.result) {
|
|
86
|
+
transformed =
|
|
87
|
+
walkExpression(
|
|
88
|
+
whenClause.result as ExpressionValue,
|
|
89
|
+
whenClause,
|
|
90
|
+
'result'
|
|
91
|
+
) || transformed;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if ('args' in expr && exprObj.args) {
|
|
98
|
+
const args = exprObj.args as Record<string, unknown>;
|
|
99
|
+
if ('value' in args && Array.isArray(args.value)) {
|
|
100
|
+
for (let i = 0; i < args.value.length; i++) {
|
|
101
|
+
transformed =
|
|
102
|
+
walkExpression(
|
|
103
|
+
args.value[i] as ExpressionValue,
|
|
104
|
+
args.value,
|
|
105
|
+
String(i)
|
|
106
|
+
) || transformed;
|
|
107
|
+
}
|
|
108
|
+
} else if ('expr' in args) {
|
|
109
|
+
transformed =
|
|
110
|
+
walkExpression(args.expr as ExpressionValue, args, 'expr') ||
|
|
111
|
+
transformed;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if ('ast' in exprObj && exprObj.ast) {
|
|
116
|
+
const subAst = exprObj.ast as Select;
|
|
117
|
+
if (subAst.type === 'select') {
|
|
118
|
+
transformed = walkSelectImpl(subAst) || transformed;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if ('type' in expr && exprObj.type === 'expr_list') {
|
|
123
|
+
if ('value' in exprObj && Array.isArray(exprObj.value)) {
|
|
124
|
+
for (let i = 0; i < exprObj.value.length; i++) {
|
|
125
|
+
transformed =
|
|
126
|
+
walkExpression(
|
|
127
|
+
exprObj.value[i] as ExpressionValue,
|
|
128
|
+
exprObj.value,
|
|
129
|
+
String(i)
|
|
130
|
+
) || transformed;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return transformed;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function walkFrom(from: From[] | null | undefined): boolean {
|
|
139
|
+
if (!from || !Array.isArray(from)) return false;
|
|
140
|
+
|
|
141
|
+
let transformed = false;
|
|
142
|
+
|
|
143
|
+
for (const f of from) {
|
|
144
|
+
if ('join' in f) {
|
|
145
|
+
const join = f as Join;
|
|
146
|
+
transformed = walkExpression(join.on, join, 'on') || transformed;
|
|
147
|
+
}
|
|
148
|
+
if ('expr' in f && f.expr && 'ast' in f.expr) {
|
|
149
|
+
transformed = walkSelectImpl(f.expr.ast) || transformed;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return transformed;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function walkSelectImpl(select: Select): boolean {
|
|
157
|
+
let transformed = false;
|
|
158
|
+
|
|
159
|
+
if (select.with) {
|
|
160
|
+
for (const cte of select.with) {
|
|
161
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
162
|
+
if (cteSelect && cteSelect.type === 'select') {
|
|
163
|
+
transformed = walkSelectImpl(cteSelect as Select) || transformed;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (Array.isArray(select.from)) {
|
|
169
|
+
transformed = walkFrom(select.from) || transformed;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
transformed = walkExpression(select.where, select, 'where') || transformed;
|
|
173
|
+
|
|
174
|
+
if (select.having) {
|
|
175
|
+
if (Array.isArray(select.having)) {
|
|
176
|
+
for (let i = 0; i < select.having.length; i++) {
|
|
177
|
+
transformed =
|
|
178
|
+
walkExpression(select.having[i], select.having, String(i)) ||
|
|
179
|
+
transformed;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
transformed =
|
|
183
|
+
walkExpression(select.having as ExpressionValue, select, 'having') ||
|
|
184
|
+
transformed;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (Array.isArray(select.columns)) {
|
|
189
|
+
for (const col of select.columns) {
|
|
190
|
+
if ('expr' in col) {
|
|
191
|
+
transformed = walkExpression(col.expr, col, 'expr') || transformed;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (select._next) {
|
|
197
|
+
transformed = walkSelectImpl(select._next) || transformed;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return transformed;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function transformArrayOperators(ast: AST | AST[]): boolean {
|
|
204
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
205
|
+
let transformed = false;
|
|
206
|
+
|
|
207
|
+
for (const stmt of statements) {
|
|
208
|
+
if (stmt.type === 'select') {
|
|
209
|
+
transformed = walkSelectImpl(stmt as Select) || transformed;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return transformed;
|
|
214
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST visitor to qualify unqualified column references in JOIN ON clauses.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AST,
|
|
7
|
+
Binary,
|
|
8
|
+
ColumnRefItem,
|
|
9
|
+
ExpressionValue,
|
|
10
|
+
Select,
|
|
11
|
+
From,
|
|
12
|
+
Join,
|
|
13
|
+
OrderBy,
|
|
14
|
+
Column,
|
|
15
|
+
} from 'node-sql-parser';
|
|
16
|
+
|
|
17
|
+
type TableSource = {
|
|
18
|
+
name: string;
|
|
19
|
+
alias: string | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function getTableSource(from: From): TableSource | null {
|
|
23
|
+
if ('table' in from && from.table) {
|
|
24
|
+
return {
|
|
25
|
+
name: from.table,
|
|
26
|
+
alias: from.as ?? null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if ('expr' in from && from.as) {
|
|
30
|
+
return {
|
|
31
|
+
name: from.as,
|
|
32
|
+
alias: from.as,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getQualifier(source: TableSource): string {
|
|
39
|
+
return source.alias ?? source.name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isUnqualifiedColumnRef(expr: ExpressionValue): expr is ColumnRefItem {
|
|
43
|
+
return (
|
|
44
|
+
typeof expr === 'object' &&
|
|
45
|
+
expr !== null &&
|
|
46
|
+
'type' in expr &&
|
|
47
|
+
expr.type === 'column_ref' &&
|
|
48
|
+
!('table' in expr && expr.table)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getColumnName(col: ColumnRefItem): string | null {
|
|
53
|
+
if (typeof col.column === 'string') {
|
|
54
|
+
return col.column;
|
|
55
|
+
}
|
|
56
|
+
if (col.column && 'expr' in col.column && col.column.expr?.value) {
|
|
57
|
+
return String(col.column.expr.value);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function walkOnClause(
|
|
63
|
+
expr: Binary | null | undefined,
|
|
64
|
+
leftSource: string,
|
|
65
|
+
rightSource: string,
|
|
66
|
+
ambiguousColumns: Set<string>
|
|
67
|
+
): boolean {
|
|
68
|
+
if (!expr || typeof expr !== 'object') return false;
|
|
69
|
+
|
|
70
|
+
let transformed = false;
|
|
71
|
+
|
|
72
|
+
if (expr.type === 'binary_expr') {
|
|
73
|
+
if (expr.operator === '=') {
|
|
74
|
+
const left = expr.left as ExpressionValue;
|
|
75
|
+
const right = expr.right as ExpressionValue;
|
|
76
|
+
|
|
77
|
+
if (isUnqualifiedColumnRef(left) && isUnqualifiedColumnRef(right)) {
|
|
78
|
+
const leftColName = getColumnName(left);
|
|
79
|
+
const rightColName = getColumnName(right);
|
|
80
|
+
|
|
81
|
+
if (leftColName && rightColName && leftColName === rightColName) {
|
|
82
|
+
left.table = leftSource;
|
|
83
|
+
right.table = rightSource;
|
|
84
|
+
|
|
85
|
+
ambiguousColumns.add(leftColName);
|
|
86
|
+
transformed = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (expr.operator === 'AND' || expr.operator === 'OR') {
|
|
92
|
+
transformed =
|
|
93
|
+
walkOnClause(
|
|
94
|
+
expr.left as Binary,
|
|
95
|
+
leftSource,
|
|
96
|
+
rightSource,
|
|
97
|
+
ambiguousColumns
|
|
98
|
+
) || transformed;
|
|
99
|
+
transformed =
|
|
100
|
+
walkOnClause(
|
|
101
|
+
expr.right as Binary,
|
|
102
|
+
leftSource,
|
|
103
|
+
rightSource,
|
|
104
|
+
ambiguousColumns
|
|
105
|
+
) || transformed;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return transformed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function qualifyAmbiguousInExpression(
|
|
113
|
+
expr: ExpressionValue | null | undefined,
|
|
114
|
+
defaultQualifier: string,
|
|
115
|
+
ambiguousColumns: Set<string>
|
|
116
|
+
): boolean {
|
|
117
|
+
if (!expr || typeof expr !== 'object') return false;
|
|
118
|
+
|
|
119
|
+
let transformed = false;
|
|
120
|
+
|
|
121
|
+
if (isUnqualifiedColumnRef(expr)) {
|
|
122
|
+
const colName = getColumnName(expr);
|
|
123
|
+
if (colName && ambiguousColumns.has(colName)) {
|
|
124
|
+
expr.table = defaultQualifier;
|
|
125
|
+
transformed = true;
|
|
126
|
+
}
|
|
127
|
+
return transformed;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if ('type' in expr && expr.type === 'binary_expr') {
|
|
131
|
+
const binary = expr as Binary;
|
|
132
|
+
transformed =
|
|
133
|
+
qualifyAmbiguousInExpression(
|
|
134
|
+
binary.left as ExpressionValue,
|
|
135
|
+
defaultQualifier,
|
|
136
|
+
ambiguousColumns
|
|
137
|
+
) || transformed;
|
|
138
|
+
transformed =
|
|
139
|
+
qualifyAmbiguousInExpression(
|
|
140
|
+
binary.right as ExpressionValue,
|
|
141
|
+
defaultQualifier,
|
|
142
|
+
ambiguousColumns
|
|
143
|
+
) || transformed;
|
|
144
|
+
return transformed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if ('args' in expr && expr.args) {
|
|
148
|
+
const args = expr.args as {
|
|
149
|
+
value?: ExpressionValue[];
|
|
150
|
+
expr?: ExpressionValue;
|
|
151
|
+
};
|
|
152
|
+
if (args.value && Array.isArray(args.value)) {
|
|
153
|
+
for (const arg of args.value) {
|
|
154
|
+
transformed =
|
|
155
|
+
qualifyAmbiguousInExpression(
|
|
156
|
+
arg,
|
|
157
|
+
defaultQualifier,
|
|
158
|
+
ambiguousColumns
|
|
159
|
+
) || transformed;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (args.expr) {
|
|
163
|
+
transformed =
|
|
164
|
+
qualifyAmbiguousInExpression(
|
|
165
|
+
args.expr,
|
|
166
|
+
defaultQualifier,
|
|
167
|
+
ambiguousColumns
|
|
168
|
+
) || transformed;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return transformed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function walkSelect(select: Select): boolean {
|
|
176
|
+
let transformed = false;
|
|
177
|
+
const ambiguousColumns = new Set<string>();
|
|
178
|
+
|
|
179
|
+
if (Array.isArray(select.from) && select.from.length >= 2) {
|
|
180
|
+
const firstSource = getTableSource(select.from[0]);
|
|
181
|
+
const defaultQualifier = firstSource ? getQualifier(firstSource) : '';
|
|
182
|
+
let prevSource = firstSource;
|
|
183
|
+
|
|
184
|
+
for (const from of select.from) {
|
|
185
|
+
if ('join' in from) {
|
|
186
|
+
const join = from as Join;
|
|
187
|
+
const currentSource = getTableSource(join);
|
|
188
|
+
|
|
189
|
+
if (join.on && prevSource && currentSource) {
|
|
190
|
+
const leftQualifier = getQualifier(prevSource);
|
|
191
|
+
const rightQualifier = getQualifier(currentSource);
|
|
192
|
+
|
|
193
|
+
transformed =
|
|
194
|
+
walkOnClause(
|
|
195
|
+
join.on,
|
|
196
|
+
leftQualifier,
|
|
197
|
+
rightQualifier,
|
|
198
|
+
ambiguousColumns
|
|
199
|
+
) || transformed;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
prevSource = currentSource;
|
|
203
|
+
} else {
|
|
204
|
+
const source = getTableSource(from);
|
|
205
|
+
if (source) {
|
|
206
|
+
prevSource = source;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if ('expr' in from && from.expr && 'ast' in from.expr) {
|
|
211
|
+
transformed = walkSelect(from.expr.ast) || transformed;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (ambiguousColumns.size > 0 && defaultQualifier) {
|
|
216
|
+
if (Array.isArray(select.columns)) {
|
|
217
|
+
for (const col of select.columns as Column[]) {
|
|
218
|
+
if ('expr' in col) {
|
|
219
|
+
transformed =
|
|
220
|
+
qualifyAmbiguousInExpression(
|
|
221
|
+
col.expr,
|
|
222
|
+
defaultQualifier,
|
|
223
|
+
ambiguousColumns
|
|
224
|
+
) || transformed;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
transformed =
|
|
230
|
+
qualifyAmbiguousInExpression(
|
|
231
|
+
select.where,
|
|
232
|
+
defaultQualifier,
|
|
233
|
+
ambiguousColumns
|
|
234
|
+
) || transformed;
|
|
235
|
+
|
|
236
|
+
if (Array.isArray(select.orderby)) {
|
|
237
|
+
for (const order of select.orderby as OrderBy[]) {
|
|
238
|
+
if (order.expr) {
|
|
239
|
+
transformed =
|
|
240
|
+
qualifyAmbiguousInExpression(
|
|
241
|
+
order.expr,
|
|
242
|
+
defaultQualifier,
|
|
243
|
+
ambiguousColumns
|
|
244
|
+
) || transformed;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (select.with) {
|
|
252
|
+
for (const cte of select.with) {
|
|
253
|
+
const cteSelect = cte.stmt?.ast ?? cte.stmt;
|
|
254
|
+
if (cteSelect && cteSelect.type === 'select') {
|
|
255
|
+
transformed = walkSelect(cteSelect as Select) || transformed;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (select._next) {
|
|
261
|
+
transformed = walkSelect(select._next) || transformed;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return transformed;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function qualifyJoinColumns(ast: AST | AST[]): boolean {
|
|
268
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
269
|
+
let transformed = false;
|
|
270
|
+
|
|
271
|
+
for (const stmt of statements) {
|
|
272
|
+
if (stmt.type === 'select') {
|
|
273
|
+
transformed = walkSelect(stmt as Select) || transformed;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return transformed;
|
|
278
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export declare function scrubForRewrite(query: string): string;
|
|
2
|
-
export declare function adaptArrayOperators(query: string): string;
|
|
3
|
-
/**
|
|
4
|
-
* Qualifies unqualified column references in JOIN ON clauses, SELECT, WHERE,
|
|
5
|
-
* and ORDER BY clauses.
|
|
6
|
-
*
|
|
7
|
-
* Transforms patterns like:
|
|
8
|
-
* `select "col" from "a" left join "b" on "col" = "col" where "col" in (...)`
|
|
9
|
-
* To:
|
|
10
|
-
* `select "a"."col" from "a" left join "b" on "a"."col" = "b"."col" where "a"."col" in (...)`
|
|
11
|
-
*
|
|
12
|
-
* This fixes the issue where drizzle-orm generates unqualified column
|
|
13
|
-
* references when joining CTEs with eq().
|
|
14
|
-
*/
|
|
15
|
-
export declare function qualifyJoinColumns(query: string): string;
|