sails-sqlite 0.1.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/lib/index.js +204 -28
- package/lib/private/build-std-adapter-method.js +8 -4
- package/lib/private/machines/avg-records.js +1 -1
- 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 +1 -1
- package/lib/private/machines/create-each-record.js +1 -1
- package/lib/private/machines/create-record.js +2 -2
- package/lib/private/machines/define-physical-model.js +18 -9
- package/lib/private/machines/destroy-records.js +21 -8
- package/lib/private/machines/drop-physical-model.js +2 -2
- package/lib/private/machines/find-records.js +3 -3
- package/lib/private/machines/join.js +232 -66
- package/lib/private/machines/lease-connection.js +58 -0
- package/lib/private/machines/private/build-sqlite-where-clause.js +10 -8
- package/lib/private/machines/private/compile-statement.js +334 -0
- package/lib/private/machines/private/generate-join-sql-query.js +14 -6
- package/lib/private/machines/private/process-each-record.js +9 -2
- package/lib/private/machines/private/process-native-error.js +85 -40
- package/lib/private/machines/rollback-transaction.js +50 -0
- package/lib/private/machines/sum-records.js +1 -1
- package/lib/private/machines/update-records.js +27 -10
- package/package.json +8 -3
- package/tests/index.js +1 -1
- package/tests/runner.js +99 -0
- package/tests/transaction.test.js +562 -0
- package/tests/adapter.test.js +0 -534
- package/tests/datatypes.test.js +0 -293
- package/tests/sequence.test.js +0 -153
package/tests/datatypes.test.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
const { test, describe, before, after } = require('node:test')
|
|
2
|
-
const assert = require('node:assert')
|
|
3
|
-
const path = require('node:path')
|
|
4
|
-
const fs = require('node:fs')
|
|
5
|
-
|
|
6
|
-
// Import the adapter
|
|
7
|
-
const adapter = require('../lib/index.js')
|
|
8
|
-
|
|
9
|
-
describe('Data type conversions', () => {
|
|
10
|
-
let testDbPath
|
|
11
|
-
let datastore
|
|
12
|
-
let models
|
|
13
|
-
|
|
14
|
-
before(async () => {
|
|
15
|
-
testDbPath = path.join(__dirname, `test-datatypes-${Date.now()}.sqlite`)
|
|
16
|
-
datastore = {
|
|
17
|
-
identity: 'testDatastore',
|
|
18
|
-
adapter: 'sails-sqlite',
|
|
19
|
-
url: testDbPath
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
models = {
|
|
23
|
-
testmodel: {
|
|
24
|
-
identity: 'testmodel',
|
|
25
|
-
tableName: 'test_records',
|
|
26
|
-
primaryKey: 'id',
|
|
27
|
-
attributes: {
|
|
28
|
-
id: {
|
|
29
|
-
type: 'number',
|
|
30
|
-
autoIncrement: true,
|
|
31
|
-
columnName: 'id'
|
|
32
|
-
},
|
|
33
|
-
stringField: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
columnName: 'string_field'
|
|
36
|
-
},
|
|
37
|
-
numberField: {
|
|
38
|
-
type: 'number',
|
|
39
|
-
columnName: 'number_field'
|
|
40
|
-
},
|
|
41
|
-
booleanField: {
|
|
42
|
-
type: 'boolean',
|
|
43
|
-
columnName: 'boolean_field'
|
|
44
|
-
},
|
|
45
|
-
jsonField: {
|
|
46
|
-
type: 'json',
|
|
47
|
-
columnName: 'json_field'
|
|
48
|
-
},
|
|
49
|
-
timestampField: {
|
|
50
|
-
type: 'number',
|
|
51
|
-
columnName: 'timestamp_field'
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
await new Promise((resolve, reject) => {
|
|
58
|
-
adapter.registerDatastore(datastore, models, (err) => {
|
|
59
|
-
if (err) return reject(err)
|
|
60
|
-
resolve()
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const tableDef = {
|
|
65
|
-
id: {
|
|
66
|
-
type: 'number',
|
|
67
|
-
primaryKey: true,
|
|
68
|
-
autoIncrement: true,
|
|
69
|
-
required: true
|
|
70
|
-
},
|
|
71
|
-
string_field: {
|
|
72
|
-
type: 'string'
|
|
73
|
-
},
|
|
74
|
-
number_field: {
|
|
75
|
-
type: 'number',
|
|
76
|
-
columnType: 'float' // Specify float for decimal numbers
|
|
77
|
-
},
|
|
78
|
-
boolean_field: {
|
|
79
|
-
type: 'boolean'
|
|
80
|
-
},
|
|
81
|
-
json_field: {
|
|
82
|
-
type: 'json'
|
|
83
|
-
},
|
|
84
|
-
timestamp_field: {
|
|
85
|
-
type: 'number'
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
await new Promise((resolve, reject) => {
|
|
90
|
-
adapter.define('testDatastore', 'test_records', tableDef, (err) => {
|
|
91
|
-
if (err) return reject(err)
|
|
92
|
-
resolve()
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
after(async () => {
|
|
98
|
-
await new Promise((resolve, reject) => {
|
|
99
|
-
adapter.teardown('testDatastore', (err) => {
|
|
100
|
-
if (err) return reject(err)
|
|
101
|
-
resolve()
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
if (fs.existsSync(testDbPath)) {
|
|
106
|
-
try {
|
|
107
|
-
fs.unlinkSync(testDbPath)
|
|
108
|
-
} catch (err) {
|
|
109
|
-
// Ignore cleanup errors
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test('should convert number types correctly', async () => {
|
|
115
|
-
const testNumbers = [0, 42, 3.14159, -100, 1754501840042]
|
|
116
|
-
|
|
117
|
-
for (const testNumber of testNumbers) {
|
|
118
|
-
const query = {
|
|
119
|
-
using: 'test_records',
|
|
120
|
-
newRecord: {
|
|
121
|
-
number_field: testNumber,
|
|
122
|
-
timestamp_field: testNumber
|
|
123
|
-
},
|
|
124
|
-
meta: {
|
|
125
|
-
fetch: true
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const result = await new Promise((resolve, reject) => {
|
|
130
|
-
adapter.create('testDatastore', query, (err, result) => {
|
|
131
|
-
if (err) return reject(err)
|
|
132
|
-
resolve(result)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
assert.strictEqual(
|
|
137
|
-
typeof result.number_field,
|
|
138
|
-
'number',
|
|
139
|
-
`number_field should be a number, got ${typeof result.number_field}`
|
|
140
|
-
)
|
|
141
|
-
assert.strictEqual(
|
|
142
|
-
typeof result.timestamp_field,
|
|
143
|
-
'number',
|
|
144
|
-
`timestamp_field should be a number, got ${typeof result.timestamp_field}`
|
|
145
|
-
)
|
|
146
|
-
assert.strictEqual(
|
|
147
|
-
result.number_field,
|
|
148
|
-
testNumber,
|
|
149
|
-
`number_field should equal ${testNumber}`
|
|
150
|
-
)
|
|
151
|
-
assert.strictEqual(
|
|
152
|
-
result.timestamp_field,
|
|
153
|
-
testNumber,
|
|
154
|
-
`timestamp_field should equal ${testNumber}`
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test('should convert boolean types correctly', async () => {
|
|
160
|
-
const testCases = [
|
|
161
|
-
{ input: true, expected: true },
|
|
162
|
-
{ input: false, expected: false },
|
|
163
|
-
{ input: 1, expected: true },
|
|
164
|
-
{ input: 0, expected: false }
|
|
165
|
-
]
|
|
166
|
-
|
|
167
|
-
for (const testCase of testCases) {
|
|
168
|
-
const query = {
|
|
169
|
-
using: 'test_records',
|
|
170
|
-
newRecord: {
|
|
171
|
-
boolean_field: testCase.input
|
|
172
|
-
},
|
|
173
|
-
meta: {
|
|
174
|
-
fetch: true
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const result = await new Promise((resolve, reject) => {
|
|
179
|
-
adapter.create('testDatastore', query, (err, result) => {
|
|
180
|
-
if (err) return reject(err)
|
|
181
|
-
resolve(result)
|
|
182
|
-
})
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
assert.strictEqual(
|
|
186
|
-
typeof result.boolean_field,
|
|
187
|
-
'boolean',
|
|
188
|
-
'boolean_field should be a boolean'
|
|
189
|
-
)
|
|
190
|
-
assert.strictEqual(
|
|
191
|
-
result.boolean_field,
|
|
192
|
-
testCase.expected,
|
|
193
|
-
`boolean_field should equal ${testCase.expected}`
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
test('should convert JSON types correctly', async () => {
|
|
199
|
-
const testObjects = [
|
|
200
|
-
{ name: 'test', value: 42 },
|
|
201
|
-
{ array: [1, 2, 3], nested: { key: 'value' } },
|
|
202
|
-
null,
|
|
203
|
-
'simple string',
|
|
204
|
-
[1, 2, 3, 4, 5]
|
|
205
|
-
]
|
|
206
|
-
|
|
207
|
-
for (const testObject of testObjects) {
|
|
208
|
-
const query = {
|
|
209
|
-
using: 'test_records',
|
|
210
|
-
newRecord: {
|
|
211
|
-
json_field: testObject // Pass raw object, let the adapter handle JSON.stringify
|
|
212
|
-
},
|
|
213
|
-
meta: {
|
|
214
|
-
fetch: true
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const result = await new Promise((resolve, reject) => {
|
|
219
|
-
adapter.create('testDatastore', query, (err, result) => {
|
|
220
|
-
if (err) return reject(err)
|
|
221
|
-
resolve(result)
|
|
222
|
-
})
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
// The result should be the parsed JSON object, not a string
|
|
226
|
-
assert.deepStrictEqual(
|
|
227
|
-
result.json_field,
|
|
228
|
-
testObject,
|
|
229
|
-
'JSON field should be parsed correctly'
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
test('should handle string types correctly', async () => {
|
|
235
|
-
const testStrings = ['hello world', '', 'unicode: 🚀', 'numbers: 123']
|
|
236
|
-
|
|
237
|
-
for (const testString of testStrings) {
|
|
238
|
-
const query = {
|
|
239
|
-
using: 'test_records',
|
|
240
|
-
newRecord: {
|
|
241
|
-
string_field: testString
|
|
242
|
-
},
|
|
243
|
-
meta: {
|
|
244
|
-
fetch: true
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const result = await new Promise((resolve, reject) => {
|
|
249
|
-
adapter.create('testDatastore', query, (err, result) => {
|
|
250
|
-
if (err) return reject(err)
|
|
251
|
-
resolve(result)
|
|
252
|
-
})
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
assert.strictEqual(
|
|
256
|
-
typeof result.string_field,
|
|
257
|
-
'string',
|
|
258
|
-
'string_field should be a string'
|
|
259
|
-
)
|
|
260
|
-
assert.strictEqual(
|
|
261
|
-
result.string_field,
|
|
262
|
-
testString,
|
|
263
|
-
`string_field should equal "${testString}"`
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
test('should handle auto-increment ID conversion', async () => {
|
|
269
|
-
const query = {
|
|
270
|
-
using: 'test_records',
|
|
271
|
-
newRecord: {
|
|
272
|
-
string_field: 'test'
|
|
273
|
-
},
|
|
274
|
-
meta: {
|
|
275
|
-
fetch: true
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const result = await new Promise((resolve, reject) => {
|
|
280
|
-
adapter.create('testDatastore', query, (err, result) => {
|
|
281
|
-
if (err) return reject(err)
|
|
282
|
-
resolve(result)
|
|
283
|
-
})
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
assert.strictEqual(
|
|
287
|
-
typeof result.id,
|
|
288
|
-
'number',
|
|
289
|
-
'Auto-increment ID should be a number'
|
|
290
|
-
)
|
|
291
|
-
assert(result.id > 0, 'Auto-increment ID should be positive')
|
|
292
|
-
})
|
|
293
|
-
})
|
package/tests/sequence.test.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
const { test, describe, before, after } = require('node:test')
|
|
2
|
-
const assert = require('node:assert')
|
|
3
|
-
const path = require('node:path')
|
|
4
|
-
const fs = require('node:fs')
|
|
5
|
-
|
|
6
|
-
// Import the adapter
|
|
7
|
-
const adapter = require('../lib/index.js')
|
|
8
|
-
|
|
9
|
-
describe('Sequence name parsing', () => {
|
|
10
|
-
let testDbPath
|
|
11
|
-
let datastore
|
|
12
|
-
let models
|
|
13
|
-
|
|
14
|
-
before(async () => {
|
|
15
|
-
testDbPath = path.join(__dirname, `test-sequence-${Date.now()}.sqlite`)
|
|
16
|
-
datastore = {
|
|
17
|
-
identity: 'testDatastore',
|
|
18
|
-
adapter: 'sails-sqlite',
|
|
19
|
-
url: testDbPath
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
models = {
|
|
23
|
-
user: {
|
|
24
|
-
identity: 'user',
|
|
25
|
-
tableName: 'users',
|
|
26
|
-
primaryKey: 'id',
|
|
27
|
-
attributes: {
|
|
28
|
-
id: {
|
|
29
|
-
type: 'number',
|
|
30
|
-
autoIncrement: true,
|
|
31
|
-
columnName: 'id'
|
|
32
|
-
},
|
|
33
|
-
name: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
required: true,
|
|
36
|
-
columnName: 'name'
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
await new Promise((resolve, reject) => {
|
|
43
|
-
adapter.registerDatastore(datastore, models, (err) => {
|
|
44
|
-
if (err) return reject(err)
|
|
45
|
-
resolve()
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const tableDef = {
|
|
50
|
-
id: { type: 'number', primaryKey: true, autoIncrement: true },
|
|
51
|
-
name: { type: 'string' }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
await new Promise((resolve, reject) => {
|
|
55
|
-
adapter.define('testDatastore', 'users', tableDef, (err) => {
|
|
56
|
-
if (err) return reject(err)
|
|
57
|
-
resolve()
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
// Create a record to establish the sequence
|
|
62
|
-
await new Promise((resolve, reject) => {
|
|
63
|
-
const query = {
|
|
64
|
-
using: 'users',
|
|
65
|
-
newRecord: {
|
|
66
|
-
name: 'Test User'
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
adapter.create('testDatastore', query, (err) => {
|
|
70
|
-
if (err) return reject(err)
|
|
71
|
-
resolve()
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
after(async () => {
|
|
77
|
-
await new Promise((resolve, reject) => {
|
|
78
|
-
adapter.teardown('testDatastore', (err) => {
|
|
79
|
-
if (err) return reject(err)
|
|
80
|
-
resolve()
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
if (fs.existsSync(testDbPath)) {
|
|
85
|
-
try {
|
|
86
|
-
fs.unlinkSync(testDbPath)
|
|
87
|
-
} catch (err) {
|
|
88
|
-
// Ignore cleanup errors
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('should handle PostgreSQL-style sequence names', async () => {
|
|
94
|
-
const testCases = [
|
|
95
|
-
{
|
|
96
|
-
sequenceName: 'users_id_seq',
|
|
97
|
-
description: 'PostgreSQL-style: users_id_seq -> users'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
sequenceName: 'users_some_other_seq',
|
|
101
|
-
description: 'Complex name: users_some_other_seq -> users'
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
sequenceName: 'users',
|
|
105
|
-
description: 'Direct table name: users -> users'
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
for (const testCase of testCases) {
|
|
110
|
-
await new Promise((resolve, reject) => {
|
|
111
|
-
adapter.setSequence(
|
|
112
|
-
'testDatastore',
|
|
113
|
-
testCase.sequenceName,
|
|
114
|
-
100,
|
|
115
|
-
(err) => {
|
|
116
|
-
if (err) return reject(err)
|
|
117
|
-
resolve()
|
|
118
|
-
}
|
|
119
|
-
)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
assert(true, 'All sequence name patterns handled successfully')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
test('should extract correct table name from sequence name', async () => {
|
|
127
|
-
// Test that 'user_id_seq' gets parsed to 'user' table (which doesn't exist)
|
|
128
|
-
// and should gracefully handle the notFound case
|
|
129
|
-
try {
|
|
130
|
-
await new Promise((resolve, reject) => {
|
|
131
|
-
adapter.setSequence('testDatastore', 'user_id_seq', 100, (err) => {
|
|
132
|
-
// Should succeed even if table doesn't exist (handled by notFound exit)
|
|
133
|
-
resolve()
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
assert(true, 'Non-existent table handled gracefully')
|
|
137
|
-
} catch (err) {
|
|
138
|
-
// This should not throw due to our notFound handling
|
|
139
|
-
assert.fail('Should handle non-existent table gracefully')
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test('should handle sequence names without underscores', async () => {
|
|
144
|
-
await new Promise((resolve, reject) => {
|
|
145
|
-
adapter.setSequence('testDatastore', 'users', 200, (err) => {
|
|
146
|
-
if (err) return reject(err)
|
|
147
|
-
resolve()
|
|
148
|
-
})
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
assert(true, 'Direct table name handled successfully')
|
|
152
|
-
})
|
|
153
|
-
})
|