sails-sqlite 0.0.0 → 0.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/.github/FUNDING.yml +1 -0
- package/.github/workflows/prettier.yml +16 -0
- package/.github/workflows/test.yml +16 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc.js +5 -0
- package/CHANGELOG.md +161 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/lib/index.js +1104 -0
- package/lib/private/build-std-adapter-method.js +69 -0
- package/lib/private/constants/connection.input.js +15 -0
- package/lib/private/constants/dry-orm.input.js +23 -0
- package/lib/private/constants/meta.input.js +14 -0
- package/lib/private/constants/not-unique.exit.js +16 -0
- package/lib/private/constants/query.input.js +15 -0
- package/lib/private/constants/table-name.input.js +12 -0
- package/lib/private/machines/avg-records.js +74 -0
- package/lib/private/machines/begin-transaction.js +51 -0
- package/lib/private/machines/commit-transaction.js +50 -0
- package/lib/private/machines/count-records.js +78 -0
- package/lib/private/machines/create-each-record.js +163 -0
- package/lib/private/machines/create-manager.js +174 -0
- package/lib/private/machines/create-record.js +126 -0
- package/lib/private/machines/define-physical-model.js +111 -0
- package/lib/private/machines/destroy-manager.js +87 -0
- package/lib/private/machines/destroy-records.js +114 -0
- package/lib/private/machines/drop-physical-model.js +51 -0
- package/lib/private/machines/find-records.js +120 -0
- package/lib/private/machines/get-connection.js +54 -0
- package/lib/private/machines/join.js +259 -0
- package/lib/private/machines/lease-connection.js +58 -0
- package/lib/private/machines/private/build-sqlite-where-clause.js +91 -0
- package/lib/private/machines/private/compile-statement.js +334 -0
- package/lib/private/machines/private/generate-join-sql-query.js +385 -0
- package/lib/private/machines/private/process-each-record.js +106 -0
- package/lib/private/machines/private/process-native-error.js +104 -0
- package/lib/private/machines/private/process-native-record.js +104 -0
- package/lib/private/machines/private/reify-values-to-set.js +83 -0
- package/lib/private/machines/release-connection.js +70 -0
- package/lib/private/machines/rollback-transaction.js +50 -0
- package/lib/private/machines/set-physical-sequence.js +77 -0
- package/lib/private/machines/sum-records.js +75 -0
- package/lib/private/machines/update-records.js +162 -0
- package/lib/private/machines/verify-model-def.js +38 -0
- package/package.json +58 -5
- package/tests/index.js +88 -0
- package/tests/runner.js +99 -0
- package/tests/transaction.test.js +562 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a Waterline statement into a native SQLite query
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const buildSqliteWhereClause = require('./build-sqlite-where-clause')
|
|
6
|
+
|
|
7
|
+
module.exports = function compileStatement(statement) {
|
|
8
|
+
if (!statement) {
|
|
9
|
+
throw new Error('Statement is required')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let sql = ''
|
|
13
|
+
let bindings = []
|
|
14
|
+
|
|
15
|
+
// Handle UNION ALL queries
|
|
16
|
+
if (statement.unionAll && Array.isArray(statement.unionAll)) {
|
|
17
|
+
const unionQueries = []
|
|
18
|
+
let globalOrderBy = null
|
|
19
|
+
|
|
20
|
+
statement.unionAll.forEach((unionStatement) => {
|
|
21
|
+
let processedStatement = { ...unionStatement }
|
|
22
|
+
|
|
23
|
+
// Remove ORDER BY, LIMIT, SKIP from individual queries - apply globally
|
|
24
|
+
if (!globalOrderBy && unionStatement.orderBy) {
|
|
25
|
+
globalOrderBy = unionStatement.orderBy
|
|
26
|
+
}
|
|
27
|
+
delete processedStatement.orderBy
|
|
28
|
+
delete processedStatement.limit
|
|
29
|
+
delete processedStatement.skip
|
|
30
|
+
|
|
31
|
+
const compiledUnion = compileStatement(processedStatement)
|
|
32
|
+
unionQueries.push(compiledUnion.sql)
|
|
33
|
+
bindings = bindings.concat(compiledUnion.bindings || [])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
sql = unionQueries.join(' UNION ALL ')
|
|
37
|
+
|
|
38
|
+
// Apply global ORDER BY if present
|
|
39
|
+
if (
|
|
40
|
+
globalOrderBy &&
|
|
41
|
+
Array.isArray(globalOrderBy) &&
|
|
42
|
+
globalOrderBy.length > 0
|
|
43
|
+
) {
|
|
44
|
+
const orderClauses = globalOrderBy.map((orderItem) => {
|
|
45
|
+
if (typeof orderItem === 'string') {
|
|
46
|
+
return `\`${orderItem}\` ASC`
|
|
47
|
+
}
|
|
48
|
+
if (typeof orderItem === 'object') {
|
|
49
|
+
const key = Object.keys(orderItem)[0]
|
|
50
|
+
const direction =
|
|
51
|
+
orderItem[key].toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
|
|
52
|
+
return `\`${key}\` ${direction}`
|
|
53
|
+
}
|
|
54
|
+
return orderItem
|
|
55
|
+
})
|
|
56
|
+
sql += ` ORDER BY ${orderClauses.join(', ')}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { sql, bindings }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle regular SELECT statements
|
|
63
|
+
if (statement.select) {
|
|
64
|
+
// SELECT clause
|
|
65
|
+
if (Array.isArray(statement.select) && statement.select.length > 0) {
|
|
66
|
+
const selectColumns = statement.select.map((col) => {
|
|
67
|
+
// Handle columns with aliases (e.g., 'table.column as alias')
|
|
68
|
+
if (col.includes(' as ')) {
|
|
69
|
+
const [columnPart, aliasPart] = col.split(' as ')
|
|
70
|
+
const alias = aliasPart.trim()
|
|
71
|
+
|
|
72
|
+
// Process the column part
|
|
73
|
+
let formattedColumn
|
|
74
|
+
if (columnPart.includes('.')) {
|
|
75
|
+
const parts = columnPart.split('.')
|
|
76
|
+
if (parts.length === 2) {
|
|
77
|
+
const [tableName, columnName] = parts
|
|
78
|
+
formattedColumn = `\`${tableName}\`.\`${columnName}\``
|
|
79
|
+
} else {
|
|
80
|
+
formattedColumn = columnPart
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
formattedColumn = `\`${columnPart}\``
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return `${formattedColumn} AS ${alias}`
|
|
87
|
+
}
|
|
88
|
+
// Handle table-prefixed columns (e.g., 'tableName.columnName')
|
|
89
|
+
else if (col.includes('.')) {
|
|
90
|
+
const parts = col.split('.')
|
|
91
|
+
if (parts.length === 2) {
|
|
92
|
+
const [tableName, columnName] = parts
|
|
93
|
+
return `\`${tableName}\`.\`${columnName}\``
|
|
94
|
+
}
|
|
95
|
+
// Handle complex column expressions
|
|
96
|
+
return col
|
|
97
|
+
} else {
|
|
98
|
+
return `\`${col}\``
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
sql += `SELECT ${selectColumns.join(', ')}`
|
|
102
|
+
} else {
|
|
103
|
+
sql += 'SELECT *'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// FROM clause
|
|
107
|
+
if (statement.from) {
|
|
108
|
+
// Handle table aliases (e.g., "paymentTable as paymentTable__payments")
|
|
109
|
+
if (statement.from.includes(' as ')) {
|
|
110
|
+
const [tableName, alias] = statement.from.split(' as ')
|
|
111
|
+
sql += ` FROM \`${tableName.trim()}\` AS \`${alias.trim()}\``
|
|
112
|
+
} else {
|
|
113
|
+
sql += ` FROM \`${statement.from}\``
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// JOIN clauses
|
|
118
|
+
if (statement.leftOuterJoin && Array.isArray(statement.leftOuterJoin)) {
|
|
119
|
+
statement.leftOuterJoin.forEach((join) => {
|
|
120
|
+
if (join.from && join.on) {
|
|
121
|
+
// Handle table aliases in JOIN
|
|
122
|
+
let joinTable
|
|
123
|
+
if (join.from.includes(' as ')) {
|
|
124
|
+
const [tableName, alias] = join.from.split(' as ')
|
|
125
|
+
joinTable = `\`${tableName.trim()}\` AS \`${alias.trim()}\``
|
|
126
|
+
} else {
|
|
127
|
+
joinTable = `\`${join.from}\``
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
sql += ` LEFT OUTER JOIN ${joinTable} ON `
|
|
131
|
+
|
|
132
|
+
// Build the ON conditions
|
|
133
|
+
const onConditions = []
|
|
134
|
+
Object.keys(join.on).forEach((tableName) => {
|
|
135
|
+
const columnName = join.on[tableName]
|
|
136
|
+
// The key is a table name, value is a column name
|
|
137
|
+
// We need to format as table.column for both sides
|
|
138
|
+
const formattedTableCol = `\`${tableName}\`.\`${columnName}\``
|
|
139
|
+
onConditions.push(formattedTableCol)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Join conditions should be joined with =
|
|
143
|
+
// If we have 2 conditions, it should be table1.col1 = table2.col2
|
|
144
|
+
if (onConditions.length === 2) {
|
|
145
|
+
sql += `${onConditions[0]} = ${onConditions[1]}`
|
|
146
|
+
} else {
|
|
147
|
+
// Fallback for other cases
|
|
148
|
+
sql += onConditions.join(' AND ')
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// WHERE clause
|
|
155
|
+
if (statement.where) {
|
|
156
|
+
const whereResult = buildWhereClause(statement.where)
|
|
157
|
+
if (whereResult.clause) {
|
|
158
|
+
sql += ` WHERE ${whereResult.clause}`
|
|
159
|
+
bindings = bindings.concat(whereResult.bindings || [])
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ORDER BY clause
|
|
164
|
+
if (
|
|
165
|
+
statement.orderBy &&
|
|
166
|
+
Array.isArray(statement.orderBy) &&
|
|
167
|
+
statement.orderBy.length > 0
|
|
168
|
+
) {
|
|
169
|
+
const orderClauses = statement.orderBy.map((orderItem) => {
|
|
170
|
+
if (typeof orderItem === 'string') {
|
|
171
|
+
return `\`${orderItem}\` ASC`
|
|
172
|
+
}
|
|
173
|
+
if (typeof orderItem === 'object') {
|
|
174
|
+
const key = Object.keys(orderItem)[0]
|
|
175
|
+
const direction =
|
|
176
|
+
orderItem[key].toUpperCase() === 'DESC' ? 'DESC' : 'ASC'
|
|
177
|
+
return `\`${key}\` ${direction}`
|
|
178
|
+
}
|
|
179
|
+
return orderItem
|
|
180
|
+
})
|
|
181
|
+
sql += ` ORDER BY ${orderClauses.join(', ')}`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// LIMIT clause
|
|
185
|
+
if (typeof statement.limit === 'number') {
|
|
186
|
+
sql += ` LIMIT ${statement.limit}`
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// OFFSET clause
|
|
190
|
+
if (typeof statement.skip === 'number') {
|
|
191
|
+
sql += ` OFFSET ${statement.skip}`
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { sql, bindings }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Build WHERE clause from Waterline criteria
|
|
200
|
+
*/
|
|
201
|
+
function buildWhereClause(whereObj) {
|
|
202
|
+
if (!whereObj || typeof whereObj !== 'object') {
|
|
203
|
+
return { clause: '', bindings: [] }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const conditions = []
|
|
207
|
+
const bindings = []
|
|
208
|
+
|
|
209
|
+
// Handle AND conditions
|
|
210
|
+
if (whereObj.and && Array.isArray(whereObj.and)) {
|
|
211
|
+
const andConditions = []
|
|
212
|
+
whereObj.and.forEach((condition) => {
|
|
213
|
+
const result = buildWhereClause(condition)
|
|
214
|
+
if (result.clause) {
|
|
215
|
+
andConditions.push(result.clause)
|
|
216
|
+
bindings.push(...result.bindings)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
if (andConditions.length > 0) {
|
|
220
|
+
conditions.push(`(${andConditions.join(' AND ')})`)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Handle OR conditions
|
|
225
|
+
if (whereObj.or && Array.isArray(whereObj.or)) {
|
|
226
|
+
const orConditions = []
|
|
227
|
+
whereObj.or.forEach((condition) => {
|
|
228
|
+
const result = buildWhereClause(condition)
|
|
229
|
+
if (result.clause) {
|
|
230
|
+
orConditions.push(result.clause)
|
|
231
|
+
bindings.push(...result.bindings)
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
if (orConditions.length > 0) {
|
|
235
|
+
conditions.push(`(${orConditions.join(' OR ')})`)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Handle field conditions
|
|
240
|
+
Object.keys(whereObj).forEach((key) => {
|
|
241
|
+
if (key === 'and' || key === 'or') {
|
|
242
|
+
return // Already handled above
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const value = whereObj[key]
|
|
246
|
+
let columnName
|
|
247
|
+
|
|
248
|
+
// Handle table.column format
|
|
249
|
+
if (key.includes('.')) {
|
|
250
|
+
const parts = key.split('.')
|
|
251
|
+
if (parts.length === 2) {
|
|
252
|
+
const [tableName, colName] = parts
|
|
253
|
+
columnName = `\`${tableName}\`.\`${colName}\``
|
|
254
|
+
} else {
|
|
255
|
+
columnName = key // fallback for complex expressions
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
columnName = `\`${key}\``
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (typeof value === 'object' && value !== null) {
|
|
262
|
+
// Handle operators
|
|
263
|
+
Object.keys(value).forEach((operator) => {
|
|
264
|
+
const operatorValue = value[operator]
|
|
265
|
+
|
|
266
|
+
switch (operator) {
|
|
267
|
+
case 'in':
|
|
268
|
+
if (Array.isArray(operatorValue) && operatorValue.length > 0) {
|
|
269
|
+
const placeholders = operatorValue.map(() => '?').join(', ')
|
|
270
|
+
conditions.push(`${columnName} IN (${placeholders})`)
|
|
271
|
+
bindings.push(...operatorValue)
|
|
272
|
+
}
|
|
273
|
+
break
|
|
274
|
+
case 'nin':
|
|
275
|
+
if (Array.isArray(operatorValue) && operatorValue.length > 0) {
|
|
276
|
+
const placeholders = operatorValue.map(() => '?').join(', ')
|
|
277
|
+
conditions.push(`${columnName} NOT IN (${placeholders})`)
|
|
278
|
+
bindings.push(...operatorValue)
|
|
279
|
+
}
|
|
280
|
+
break
|
|
281
|
+
case '>':
|
|
282
|
+
conditions.push(`${columnName} > ?`)
|
|
283
|
+
bindings.push(operatorValue)
|
|
284
|
+
break
|
|
285
|
+
case '>=':
|
|
286
|
+
conditions.push(`${columnName} >= ?`)
|
|
287
|
+
bindings.push(operatorValue)
|
|
288
|
+
break
|
|
289
|
+
case '<':
|
|
290
|
+
conditions.push(`${columnName} < ?`)
|
|
291
|
+
bindings.push(operatorValue)
|
|
292
|
+
break
|
|
293
|
+
case '<=':
|
|
294
|
+
conditions.push(`${columnName} <= ?`)
|
|
295
|
+
bindings.push(operatorValue)
|
|
296
|
+
break
|
|
297
|
+
case '!=':
|
|
298
|
+
case 'ne':
|
|
299
|
+
conditions.push(`${columnName} != ?`)
|
|
300
|
+
bindings.push(operatorValue)
|
|
301
|
+
break
|
|
302
|
+
case 'like':
|
|
303
|
+
conditions.push(`${columnName} LIKE ?`)
|
|
304
|
+
bindings.push(operatorValue)
|
|
305
|
+
break
|
|
306
|
+
case 'contains':
|
|
307
|
+
conditions.push(`${columnName} LIKE ?`)
|
|
308
|
+
bindings.push(`%${operatorValue}%`)
|
|
309
|
+
break
|
|
310
|
+
case 'startsWith':
|
|
311
|
+
conditions.push(`${columnName} LIKE ?`)
|
|
312
|
+
bindings.push(`${operatorValue}%`)
|
|
313
|
+
break
|
|
314
|
+
case 'endsWith':
|
|
315
|
+
conditions.push(`${columnName} LIKE ?`)
|
|
316
|
+
bindings.push(`%${operatorValue}`)
|
|
317
|
+
break
|
|
318
|
+
default:
|
|
319
|
+
conditions.push(`${columnName} = ?`)
|
|
320
|
+
bindings.push(operatorValue)
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
} else {
|
|
324
|
+
// Simple equality
|
|
325
|
+
conditions.push(`${columnName} = ?`)
|
|
326
|
+
bindings.push(value)
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
clause: conditions.join(' AND '),
|
|
332
|
+
bindings: bindings
|
|
333
|
+
}
|
|
334
|
+
}
|