@leonardovida-md/drizzle-neo-duckdb 1.1.4 → 1.2.1

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.
@@ -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
+ }