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.
Files changed (48) hide show
  1. package/.github/FUNDING.yml +1 -0
  2. package/.github/workflows/prettier.yml +16 -0
  3. package/.github/workflows/test.yml +16 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.prettierrc.js +5 -0
  6. package/CHANGELOG.md +161 -0
  7. package/LICENSE +21 -0
  8. package/README.md +247 -0
  9. package/lib/index.js +1104 -0
  10. package/lib/private/build-std-adapter-method.js +69 -0
  11. package/lib/private/constants/connection.input.js +15 -0
  12. package/lib/private/constants/dry-orm.input.js +23 -0
  13. package/lib/private/constants/meta.input.js +14 -0
  14. package/lib/private/constants/not-unique.exit.js +16 -0
  15. package/lib/private/constants/query.input.js +15 -0
  16. package/lib/private/constants/table-name.input.js +12 -0
  17. package/lib/private/machines/avg-records.js +74 -0
  18. package/lib/private/machines/begin-transaction.js +51 -0
  19. package/lib/private/machines/commit-transaction.js +50 -0
  20. package/lib/private/machines/count-records.js +78 -0
  21. package/lib/private/machines/create-each-record.js +163 -0
  22. package/lib/private/machines/create-manager.js +174 -0
  23. package/lib/private/machines/create-record.js +126 -0
  24. package/lib/private/machines/define-physical-model.js +111 -0
  25. package/lib/private/machines/destroy-manager.js +87 -0
  26. package/lib/private/machines/destroy-records.js +114 -0
  27. package/lib/private/machines/drop-physical-model.js +51 -0
  28. package/lib/private/machines/find-records.js +120 -0
  29. package/lib/private/machines/get-connection.js +54 -0
  30. package/lib/private/machines/join.js +259 -0
  31. package/lib/private/machines/lease-connection.js +58 -0
  32. package/lib/private/machines/private/build-sqlite-where-clause.js +91 -0
  33. package/lib/private/machines/private/compile-statement.js +334 -0
  34. package/lib/private/machines/private/generate-join-sql-query.js +385 -0
  35. package/lib/private/machines/private/process-each-record.js +106 -0
  36. package/lib/private/machines/private/process-native-error.js +104 -0
  37. package/lib/private/machines/private/process-native-record.js +104 -0
  38. package/lib/private/machines/private/reify-values-to-set.js +83 -0
  39. package/lib/private/machines/release-connection.js +70 -0
  40. package/lib/private/machines/rollback-transaction.js +50 -0
  41. package/lib/private/machines/set-physical-sequence.js +77 -0
  42. package/lib/private/machines/sum-records.js +75 -0
  43. package/lib/private/machines/update-records.js +162 -0
  44. package/lib/private/machines/verify-model-def.js +38 -0
  45. package/package.json +58 -5
  46. package/tests/index.js +88 -0
  47. package/tests/runner.js +99 -0
  48. 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
+ }