sails-sqlite 0.1.0 → 0.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.
- package/lib/index.js +215 -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/compile-statement.js +62 -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/parse-native-query-error.js +90 -0
- package/lib/private/machines/parse-native-query-result.js +59 -0
- package/lib/private/machines/private/build-sqlite-where-clause.js +10 -8
- package/lib/private/machines/private/compile-statement.js +382 -0
- package/lib/private/machines/private/generate-join-sql-query.js +14 -6
- package/lib/private/machines/private/process-each-record.js +21 -4
- package/lib/private/machines/private/process-native-error.js +85 -40
- package/lib/private/machines/rollback-transaction.js +50 -0
- package/lib/private/machines/send-native-query.js +100 -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/lib/index.js
CHANGED
|
@@ -18,7 +18,15 @@ const DRY_MACHINES = {
|
|
|
18
18
|
definePhysicalModel: require('./private/machines/define-physical-model'),
|
|
19
19
|
dropPhysicalModel: require('./private/machines/drop-physical-model'),
|
|
20
20
|
setPhysicalSequence: require('./private/machines/set-physical-sequence'),
|
|
21
|
-
join: require('./private/machines/join')
|
|
21
|
+
join: require('./private/machines/join'),
|
|
22
|
+
sendNativeQuery: require('./private/machines/send-native-query'),
|
|
23
|
+
compileStatement: require('./private/machines/compile-statement'),
|
|
24
|
+
parseNativeQueryResult: require('./private/machines/parse-native-query-result'),
|
|
25
|
+
parseNativeQueryError: require('./private/machines/parse-native-query-error'),
|
|
26
|
+
beginTransaction: require('./private/machines/begin-transaction'),
|
|
27
|
+
commitTransaction: require('./private/machines/commit-transaction'),
|
|
28
|
+
rollbackTransaction: require('./private/machines/rollback-transaction'),
|
|
29
|
+
leaseConnection: require('./private/machines/lease-connection')
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
const WET_MACHINES = Object.fromEntries(
|
|
@@ -229,6 +237,13 @@ module.exports = {
|
|
|
229
237
|
destroyManager: WET_MACHINES.destroyManager,
|
|
230
238
|
getConnection: WET_MACHINES.getConnection,
|
|
231
239
|
releaseConnection: WET_MACHINES.releaseConnection,
|
|
240
|
+
sendNativeQuery: WET_MACHINES.sendNativeQuery,
|
|
241
|
+
compileStatement: WET_MACHINES.compileStatement,
|
|
242
|
+
parseNativeQueryResult: WET_MACHINES.parseNativeQueryResult,
|
|
243
|
+
parseNativeQueryError: WET_MACHINES.parseNativeQueryError,
|
|
244
|
+
beginTransaction: WET_MACHINES.beginTransaction,
|
|
245
|
+
commitTransaction: WET_MACHINES.commitTransaction,
|
|
246
|
+
rollbackTransaction: WET_MACHINES.rollbackTransaction,
|
|
232
247
|
Database
|
|
233
248
|
}
|
|
234
249
|
}
|
|
@@ -255,11 +270,16 @@ module.exports = {
|
|
|
255
270
|
)
|
|
256
271
|
}
|
|
257
272
|
|
|
273
|
+
const modelDefinition = modelInfo.definition || modelInfo.attributes
|
|
274
|
+
|
|
258
275
|
registeredDryModels[modelIdentity] = {
|
|
276
|
+
identity: modelIdentity,
|
|
259
277
|
primaryKey: modelInfo.primaryKey,
|
|
260
|
-
|
|
278
|
+
definition: modelDefinition,
|
|
279
|
+
attributes: modelDefinition,
|
|
261
280
|
tableName: modelInfo.tableName,
|
|
262
|
-
identity: modelInfo.identity
|
|
281
|
+
identity: modelInfo.identity,
|
|
282
|
+
datastore: datastoreName
|
|
263
283
|
}
|
|
264
284
|
|
|
265
285
|
// Uncomment for detailed debugging:
|
|
@@ -354,6 +374,14 @@ module.exports = {
|
|
|
354
374
|
delete registeredDryModels[modelIdentity]
|
|
355
375
|
}
|
|
356
376
|
})
|
|
377
|
+
|
|
378
|
+
// Safety cleanup: If this is the last datastore being torn down,
|
|
379
|
+
// clear any remaining models to prevent registration conflicts
|
|
380
|
+
if (Object.keys(registeredDsEntries).length === 0) {
|
|
381
|
+
Object.keys(registeredDryModels).forEach((modelIdentity) => {
|
|
382
|
+
delete registeredDryModels[modelIdentity]
|
|
383
|
+
})
|
|
384
|
+
}
|
|
357
385
|
} catch (err) {
|
|
358
386
|
return done(err)
|
|
359
387
|
}
|
|
@@ -533,30 +561,12 @@ module.exports = {
|
|
|
533
561
|
// ║║║╠═╣ ║ ║╚╗╔╝║╣ ││ │││││ └─┐│ │├─┘├─┘│ │├┬┘ │
|
|
534
562
|
// ╝╚╝╩ ╩ ╩ ╩ ╚╝ ╚═╝ └┘└─┘┴┘└┘ └─┘└─┘┴ ┴ └─┘┴└─ ┴
|
|
535
563
|
// Build up native joins to run on the adapter.
|
|
536
|
-
join:
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
function redactPasswords(err) {
|
|
543
|
-
// Simple implementation - in production you might want more sophisticated password redaction
|
|
544
|
-
return err
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
WET_MACHINES.join({
|
|
548
|
-
datastore: datastore,
|
|
549
|
-
models: models,
|
|
550
|
-
query: query
|
|
551
|
-
}).switch({
|
|
552
|
-
error: function error(err) {
|
|
553
|
-
return cb(redactPasswords(err))
|
|
554
|
-
},
|
|
555
|
-
success: function success(report) {
|
|
556
|
-
return cb(undefined, report)
|
|
557
|
-
}
|
|
558
|
-
})
|
|
559
|
-
},
|
|
564
|
+
join: buildStdAdapterMethod(
|
|
565
|
+
require('./private/machines/join'),
|
|
566
|
+
WET_MACHINES,
|
|
567
|
+
registeredDsEntries,
|
|
568
|
+
registeredDryModels
|
|
569
|
+
),
|
|
560
570
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
561
571
|
// ██████╗ ██████╗ ██╗ //
|
|
562
572
|
// ██╔══██╗██╔═══██╗██║ //
|
|
@@ -924,5 +934,182 @@ module.exports = {
|
|
|
924
934
|
*
|
|
925
935
|
* > https://github.com/node-machine/driver-interface/blob/master/layers/migratable/set-physical-sequence.js
|
|
926
936
|
*/
|
|
927
|
-
setPhysicalSequence: DRY_MACHINES.setPhysicalSequence
|
|
937
|
+
setPhysicalSequence: DRY_MACHINES.setPhysicalSequence,
|
|
938
|
+
|
|
939
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
940
|
+
// ████████╗██████╗ █████╗ ███╗ ███╗███████╗ █████╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗ //
|
|
941
|
+
// ╚══██╔══╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔══██╗██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║ //
|
|
942
|
+
// ██║ ██████╔╝███████║██╔████╔██║███████╗███████║██║ ██║ ██║██║ ██║██╔██╗ ██║ //
|
|
943
|
+
// ██║ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██╔══██║██║ ██║ ██║██║ ██║██║╚██╗██║ //
|
|
944
|
+
// ██║ ██║ ██║██║ ██║██║ ╚═╝ ██║███████║██║ ██║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║ //
|
|
945
|
+
// ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ //
|
|
946
|
+
// //
|
|
947
|
+
// Transaction methods: //
|
|
948
|
+
// Methods related to database transactions (begin, commit, rollback). //
|
|
949
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* ╔╗ ╔═╗╔═╗╦╔╗╔ ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
953
|
+
* ╠╩╗║╣ ║ ╦║║║║ │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
954
|
+
* ╚═╝╚═╝╚═╝╩╝╚╝ ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
955
|
+
* Begin a new database transaction.
|
|
956
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
957
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
958
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
959
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
960
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
961
|
+
* @param {Function} done Callback
|
|
962
|
+
* @param {Error?}
|
|
963
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
964
|
+
*/
|
|
965
|
+
beginTransaction: function (datastoreName, options, done) {
|
|
966
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
967
|
+
const connection = options.connection
|
|
968
|
+
const meta = options.meta || {}
|
|
969
|
+
|
|
970
|
+
if (!dsEntry) {
|
|
971
|
+
return done(
|
|
972
|
+
new Error(
|
|
973
|
+
`Consistency violation: Cannot do that with datastore (${datastoreName}) because no matching datastore entry is registered in this adapter! This is usually due to a race condition (e.g. a lifecycle callback still running after the ORM has been torn down), or it could be due to a bug in this adapter. (If you get stumped, reach out at http://sailsjs.com/support.)`
|
|
974
|
+
)
|
|
975
|
+
)
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
WET_MACHINES.beginTransaction({
|
|
979
|
+
connection: connection,
|
|
980
|
+
meta: meta
|
|
981
|
+
}).switch({
|
|
982
|
+
error: function (err) {
|
|
983
|
+
return done(err)
|
|
984
|
+
},
|
|
985
|
+
success: function () {
|
|
986
|
+
return done()
|
|
987
|
+
}
|
|
988
|
+
})
|
|
989
|
+
},
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* ╔═╗╔═╗╔╦╗╔╦╗╦╔╦╗ ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
993
|
+
* ║ ║ ║║║║║║║║ ║ │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
994
|
+
* ╚═╝╚═╝╩ ╩╩ ╩╩ ╩ ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
995
|
+
* Commit the database transaction on the provided connection.
|
|
996
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
997
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
998
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
999
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
1000
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1001
|
+
* @param {Function} done Callback
|
|
1002
|
+
* @param {Error?}
|
|
1003
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1004
|
+
*/
|
|
1005
|
+
commitTransaction: function (datastoreName, options, done) {
|
|
1006
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
1007
|
+
const connection = options.connection
|
|
1008
|
+
const meta = options.meta || {}
|
|
1009
|
+
|
|
1010
|
+
if (!dsEntry) {
|
|
1011
|
+
return done(
|
|
1012
|
+
new Error(
|
|
1013
|
+
`Consistency violation: Cannot do that with datastore (${datastoreName}) because no matching datastore entry is registered in this adapter! This is usually due to a race condition (e.g. a lifecycle callback still running after the ORM has been torn down), or it could be due to a bug in this adapter. (If you get stumped, reach out at http://sailsjs.com/support.)`
|
|
1014
|
+
)
|
|
1015
|
+
)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
WET_MACHINES.commitTransaction({
|
|
1019
|
+
connection: connection,
|
|
1020
|
+
meta: meta
|
|
1021
|
+
}).switch({
|
|
1022
|
+
error: function (err) {
|
|
1023
|
+
return done(err)
|
|
1024
|
+
},
|
|
1025
|
+
success: function () {
|
|
1026
|
+
return done()
|
|
1027
|
+
}
|
|
1028
|
+
})
|
|
1029
|
+
},
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* ╦═╗╔═╗╦ ╦ ╔╗ ╔═╗╔═╗╦╔═
|
|
1033
|
+
* ╠╦╝║ ║║ ║ ╠╩╗╠═╣║ ╠╩╗
|
|
1034
|
+
* ╩╚═╚═╝╩═╝╩═╝╚═╝╩ ╩╚═╝╩ ╩
|
|
1035
|
+
* ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
1036
|
+
* │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
1037
|
+
* ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
1038
|
+
* Rollback the database transaction on the provided connection.
|
|
1039
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1040
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
1041
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1042
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
1043
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1044
|
+
* @param {Function} done Callback
|
|
1045
|
+
* @param {Error?}
|
|
1046
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1047
|
+
*/
|
|
1048
|
+
rollbackTransaction: function (datastoreName, options, done) {
|
|
1049
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
1050
|
+
const connection = options.connection
|
|
1051
|
+
const meta = options.meta || {}
|
|
1052
|
+
|
|
1053
|
+
if (!dsEntry) {
|
|
1054
|
+
return done(
|
|
1055
|
+
new Error(
|
|
1056
|
+
`Consistency violation: Cannot do that with datastore (${datastoreName}) because no matching datastore entry is registered in this adapter! This is usually due to a race condition (e.g. a lifecycle callback still running after the ORM has been torn down), or it could be due to a bug in this adapter. (If you get stumped, reach out at http://sailsjs.com/support.)`
|
|
1057
|
+
)
|
|
1058
|
+
)
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
WET_MACHINES.rollbackTransaction({
|
|
1062
|
+
connection: connection,
|
|
1063
|
+
meta: meta
|
|
1064
|
+
}).switch({
|
|
1065
|
+
error: function (err) {
|
|
1066
|
+
return done(err)
|
|
1067
|
+
},
|
|
1068
|
+
success: function () {
|
|
1069
|
+
return done()
|
|
1070
|
+
}
|
|
1071
|
+
})
|
|
1072
|
+
},
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* ╦ ╔═╗╔═╗╔═╗╔═╗ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
1076
|
+
* ║ ║╣ ╠═╣╚═╗║╣ │ │ │││││││├┤ │ │ ││ ││││
|
|
1077
|
+
* ╩═╝╚═╝╩ ╩╚═╝╚═╝ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
|
|
1078
|
+
* Lease a connection from the datastore for use in a transaction.
|
|
1079
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1080
|
+
* @param {String} datastoreName The name of the datastore to lease from.
|
|
1081
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1082
|
+
* @param {Dictionary} meta Meta options.
|
|
1083
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1084
|
+
* @param {Function} done Callback
|
|
1085
|
+
* @param {Error?}
|
|
1086
|
+
* @param {Dictionary?} connection
|
|
1087
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1088
|
+
*/
|
|
1089
|
+
leaseConnection: function (datastoreName, meta, done) {
|
|
1090
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
1091
|
+
|
|
1092
|
+
if (!dsEntry) {
|
|
1093
|
+
return done(
|
|
1094
|
+
new Error(
|
|
1095
|
+
`Consistency violation: Cannot do that with datastore (${datastoreName}) because no matching datastore entry is registered in this adapter! This is usually due to a race condition (e.g. a lifecycle callback still running after the ORM has been torn down), or it could be due to a bug in this adapter. (If you get stumped, reach out at http://sailsjs.com/support.)`
|
|
1096
|
+
)
|
|
1097
|
+
)
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
WET_MACHINES.leaseConnection({
|
|
1101
|
+
manager: dsEntry.manager,
|
|
1102
|
+
meta: meta || {}
|
|
1103
|
+
}).switch({
|
|
1104
|
+
error: function (err) {
|
|
1105
|
+
return done(err)
|
|
1106
|
+
},
|
|
1107
|
+
failed: function (err) {
|
|
1108
|
+
return done(err)
|
|
1109
|
+
},
|
|
1110
|
+
success: function (connection) {
|
|
1111
|
+
return done(null, connection)
|
|
1112
|
+
}
|
|
1113
|
+
})
|
|
1114
|
+
}
|
|
928
1115
|
}
|
|
@@ -48,10 +48,14 @@ module.exports = function buildStdAdapterMethod(
|
|
|
48
48
|
|
|
49
49
|
// Only add notUnique handler if the machine defines this exit
|
|
50
50
|
if (machineDef.exits.notUnique) {
|
|
51
|
-
switchHandlers.notUnique = function (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
switchHandlers.notUnique = function (errInfo) {
|
|
52
|
+
// Create error in same format as sails-postgresql
|
|
53
|
+
const e = new Error(errInfo.message || 'Not unique')
|
|
54
|
+
e.code = 'E_UNIQUE'
|
|
55
|
+
if (errInfo.footprint) {
|
|
56
|
+
e.footprint = errInfo.footprint
|
|
57
|
+
}
|
|
58
|
+
return done(e)
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
@@ -58,7 +58,7 @@ module.exports = {
|
|
|
58
58
|
const db = inputs.connection
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
|
-
let avgQuery = `SELECT COALESCE(AVG(
|
|
61
|
+
let avgQuery = `SELECT COALESCE(AVG(\`${numericFieldName}\`), 0) as average FROM \`${tableName}\``
|
|
62
62
|
if (whereClause) {
|
|
63
63
|
avgQuery += ` WHERE ${whereClause}`
|
|
64
64
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module dependencies
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Begin Transaction
|
|
7
|
+
*
|
|
8
|
+
* Begin a new database transaction on the provided connection.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
friendlyName: 'Begin transaction',
|
|
13
|
+
|
|
14
|
+
description: 'Begin a new database transaction on the provided connection.',
|
|
15
|
+
|
|
16
|
+
moreInfoUrl:
|
|
17
|
+
'https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function',
|
|
18
|
+
|
|
19
|
+
inputs: {
|
|
20
|
+
connection: {
|
|
21
|
+
description:
|
|
22
|
+
'An active database connection that was acquired from a manager.',
|
|
23
|
+
example: '===',
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
meta: {
|
|
28
|
+
description: 'Additional options for this query.',
|
|
29
|
+
example: '==='
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
fn: function beginTransaction(inputs, exits) {
|
|
34
|
+
const db = inputs.connection
|
|
35
|
+
const meta = inputs.meta || {}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
if (db.inTransaction) {
|
|
39
|
+
return exits.error(
|
|
40
|
+
new Error('Transaction is already active on this connection.')
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
db.prepare('BEGIN TRANSACTION').run()
|
|
45
|
+
|
|
46
|
+
return exits.success()
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return exits.error(err)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module dependencies
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Commit Transaction
|
|
7
|
+
*
|
|
8
|
+
* Commit the current database transaction on the provided connection.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
friendlyName: 'Commit transaction',
|
|
13
|
+
|
|
14
|
+
description:
|
|
15
|
+
'Commit the current database transaction on the provided connection.',
|
|
16
|
+
|
|
17
|
+
moreInfoUrl:
|
|
18
|
+
'https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function',
|
|
19
|
+
|
|
20
|
+
inputs: {
|
|
21
|
+
connection: {
|
|
22
|
+
description:
|
|
23
|
+
'An active database connection that was acquired from a manager.',
|
|
24
|
+
example: '===',
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
meta: {
|
|
29
|
+
description: 'Additional options for this query.',
|
|
30
|
+
example: '==='
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
fn: function commitTransaction(inputs, exits) {
|
|
35
|
+
const db = inputs.connection
|
|
36
|
+
const meta = inputs.meta || {}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (!db.inTransaction) {
|
|
40
|
+
return exits.error(new Error('No active transaction to commit.'))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
db.prepare('COMMIT TRANSACTION').run()
|
|
44
|
+
|
|
45
|
+
return exits.success()
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return exits.error(err)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const compileStatementUtil = require('./private/compile-statement')
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
friendlyName: 'Compile statement',
|
|
5
|
+
|
|
6
|
+
description: 'Compile a Waterline statement to a native query for SQLite.',
|
|
7
|
+
|
|
8
|
+
inputs: {
|
|
9
|
+
statement: {
|
|
10
|
+
description:
|
|
11
|
+
'A Waterline statement (stage 4 query) to compile into a native query.',
|
|
12
|
+
extendedDescription:
|
|
13
|
+
'See documentation for info on Waterline statements.',
|
|
14
|
+
example: '===',
|
|
15
|
+
required: true
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
meta: {
|
|
19
|
+
friendlyName: 'Meta (custom)',
|
|
20
|
+
description: 'Additional stuff to pass to the driver.',
|
|
21
|
+
extendedDescription:
|
|
22
|
+
'This is reserved for custom driver-specific extensions.',
|
|
23
|
+
example: '==='
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
exits: {
|
|
28
|
+
success: {
|
|
29
|
+
description:
|
|
30
|
+
'The provided Waterline statement was compiled successfully.',
|
|
31
|
+
outputVariableName: 'report',
|
|
32
|
+
outputDescription:
|
|
33
|
+
'The `nativeQuery` property is the compiled native query for the database. The `valuesToEscape` property is an array of strings, numbers, or special literals (true, false, or null) to escape and include in the query, in order. The `meta` property is reserved for custom driver-specific extensions.',
|
|
34
|
+
outputExample: '==='
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
malformed: {
|
|
38
|
+
description: 'The provided Waterline statement could not be compiled.',
|
|
39
|
+
outputVariableName: 'report',
|
|
40
|
+
outputDescription:
|
|
41
|
+
'The `error` property is a JavaScript Error instance with more details about what went wrong. The `meta` property is reserved for custom driver-specific extensions.',
|
|
42
|
+
outputExample: '==='
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
fn: function compileStatement(inputs, exits) {
|
|
47
|
+
try {
|
|
48
|
+
const compiled = compileStatementUtil(inputs.statement)
|
|
49
|
+
|
|
50
|
+
return exits.success({
|
|
51
|
+
nativeQuery: compiled.sql,
|
|
52
|
+
valuesToEscape: compiled.bindings || [],
|
|
53
|
+
meta: inputs.meta
|
|
54
|
+
})
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return exits.malformed({
|
|
57
|
+
error: err,
|
|
58
|
+
meta: inputs.meta
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -62,7 +62,7 @@ module.exports = {
|
|
|
62
62
|
const db = inputs.connection
|
|
63
63
|
|
|
64
64
|
try {
|
|
65
|
-
let countQuery = `SELECT COUNT(*) as count FROM
|
|
65
|
+
let countQuery = `SELECT COUNT(*) as count FROM \`${tableName}\``
|
|
66
66
|
if (whereClause) {
|
|
67
67
|
countQuery += ` WHERE ${whereClause}`
|
|
68
68
|
}
|
|
@@ -94,7 +94,7 @@ module.exports = {
|
|
|
94
94
|
)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
const columns = columnNames.join(', ')
|
|
97
|
+
const columns = columnNames.map((col) => `\`${col}\``).join(', ')
|
|
98
98
|
const valueClause = `(${columnNames.map(() => '?').join(', ')})`
|
|
99
99
|
const allValueClauses = Array(s3q.newRecords.length)
|
|
100
100
|
.fill(valueClause)
|
|
@@ -74,7 +74,7 @@ module.exports = {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Prepare the INSERT statement with proper SQL escaping
|
|
77
|
-
const columns = columnNames.join(', ')
|
|
77
|
+
const columns = columnNames.map((col) => `\`${col}\``).join(', ')
|
|
78
78
|
const placeholders = columnNames.map(() => '?').join(', ')
|
|
79
79
|
const sql = `INSERT INTO \`${tableName}\` (${columns}) VALUES (${placeholders})`
|
|
80
80
|
|
|
@@ -94,7 +94,7 @@ module.exports = {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Otherwise, fetch the newly created record
|
|
97
|
-
const selectSql = `SELECT * FROM
|
|
97
|
+
const selectSql = `SELECT * FROM \`${tableName}\` WHERE rowid = ?`
|
|
98
98
|
const selectStmt = db.prepare(selectSql)
|
|
99
99
|
const phRecord = selectStmt.get(info.lastInsertRowid)
|
|
100
100
|
|
|
@@ -61,15 +61,20 @@ module.exports = {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Check if we're already in a transaction
|
|
65
|
+
const wasInTransaction = db.inTransaction
|
|
66
|
+
|
|
64
67
|
try {
|
|
65
|
-
// Start a transaction
|
|
66
|
-
|
|
68
|
+
// Start a transaction only if we're not already in one
|
|
69
|
+
if (!wasInTransaction) {
|
|
70
|
+
db.prepare('BEGIN').run()
|
|
71
|
+
}
|
|
67
72
|
|
|
68
73
|
// Build and execute the CREATE TABLE statement
|
|
69
|
-
let createTableSQL = `CREATE TABLE IF NOT EXISTS
|
|
74
|
+
let createTableSQL = `CREATE TABLE IF NOT EXISTS \`${inputs.tableName}\` (`
|
|
70
75
|
let columnDefs = inputs.columns.map((column) => {
|
|
71
76
|
const columnType = column.columnType ?? column.type
|
|
72
|
-
let def =
|
|
77
|
+
let def = `\`${column.columnName}\` ${column.autoIncrement ? 'INTEGER' : getSqliteType(columnType)}`
|
|
73
78
|
if (column.autoIncrement) {
|
|
74
79
|
def += ' PRIMARY KEY AUTOINCREMENT NOT NULL'
|
|
75
80
|
}
|
|
@@ -82,18 +87,22 @@ module.exports = {
|
|
|
82
87
|
// Create indexes
|
|
83
88
|
inputs.columns.forEach((column) => {
|
|
84
89
|
if (column.unique && !column.autoIncrement) {
|
|
85
|
-
const indexSQL = `CREATE UNIQUE INDEX IF NOT EXISTS idx_${inputs.tableName}_${column.columnName} ON
|
|
90
|
+
const indexSQL = `CREATE UNIQUE INDEX IF NOT EXISTS \`idx_${inputs.tableName}_${column.columnName}\` ON \`${inputs.tableName}\` (\`${column.columnName}\`)`
|
|
86
91
|
db.prepare(indexSQL).run()
|
|
87
92
|
}
|
|
88
93
|
})
|
|
89
94
|
|
|
90
|
-
// Commit the transaction
|
|
91
|
-
|
|
95
|
+
// Commit the transaction only if we started it
|
|
96
|
+
if (!wasInTransaction) {
|
|
97
|
+
db.prepare('COMMIT').run()
|
|
98
|
+
}
|
|
92
99
|
|
|
93
100
|
return exits.success()
|
|
94
101
|
} catch (error) {
|
|
95
|
-
// If there's an error, roll back the transaction
|
|
96
|
-
|
|
102
|
+
// If there's an error, roll back the transaction (only if we started it)
|
|
103
|
+
if (!wasInTransaction) {
|
|
104
|
+
db.prepare('ROLLBACK').run()
|
|
105
|
+
}
|
|
97
106
|
return exits.error(
|
|
98
107
|
new Error(`Error defining table ${inputs.tableName}: ${error.message}`)
|
|
99
108
|
)
|
|
@@ -62,25 +62,36 @@ module.exports = {
|
|
|
62
62
|
|
|
63
63
|
const db = inputs.connection
|
|
64
64
|
|
|
65
|
+
// Check if we're already in a transaction
|
|
66
|
+
const wasInTransaction = db.inTransaction
|
|
67
|
+
|
|
65
68
|
try {
|
|
66
|
-
// Start a transaction
|
|
67
|
-
|
|
69
|
+
// Start a transaction only if we're not already in one
|
|
70
|
+
if (!wasInTransaction) {
|
|
71
|
+
db.exec('BEGIN TRANSACTION')
|
|
72
|
+
}
|
|
68
73
|
|
|
69
74
|
let phRecords
|
|
70
75
|
if (isFetchEnabled) {
|
|
71
76
|
// Fetch matching records before deletion
|
|
72
|
-
const selectSql =
|
|
77
|
+
const selectSql = sqliteWhere
|
|
78
|
+
? `SELECT * FROM \`${tableName}\` WHERE ${sqliteWhere}`
|
|
79
|
+
: `SELECT * FROM \`${tableName}\``
|
|
73
80
|
const selectStmt = db.prepare(selectSql)
|
|
74
81
|
phRecords = selectStmt.all()
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
// Perform the deletion
|
|
78
|
-
const deleteSql =
|
|
85
|
+
const deleteSql = sqliteWhere
|
|
86
|
+
? `DELETE FROM \`${tableName}\` WHERE ${sqliteWhere}`
|
|
87
|
+
: `DELETE FROM \`${tableName}\``
|
|
79
88
|
const deleteStmt = db.prepare(deleteSql)
|
|
80
89
|
const deleteInfo = deleteStmt.run()
|
|
81
90
|
|
|
82
|
-
// Commit the transaction
|
|
83
|
-
|
|
91
|
+
// Commit the transaction only if we started it
|
|
92
|
+
if (!wasInTransaction) {
|
|
93
|
+
db.exec('COMMIT')
|
|
94
|
+
}
|
|
84
95
|
|
|
85
96
|
if (!isFetchEnabled) {
|
|
86
97
|
return exits.success()
|
|
@@ -93,8 +104,10 @@ module.exports = {
|
|
|
93
104
|
|
|
94
105
|
return exits.success(phRecords)
|
|
95
106
|
} catch (err) {
|
|
96
|
-
// Rollback the transaction in case of error
|
|
97
|
-
|
|
107
|
+
// Rollback the transaction in case of error (only if we started it)
|
|
108
|
+
if (!wasInTransaction) {
|
|
109
|
+
db.exec('ROLLBACK')
|
|
110
|
+
}
|
|
98
111
|
return exits.error(err)
|
|
99
112
|
}
|
|
100
113
|
}
|
|
@@ -24,8 +24,8 @@ module.exports = {
|
|
|
24
24
|
const db = inputs.connection
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
// SQL to drop the table
|
|
28
|
-
const dropTableSQL = `DROP TABLE IF EXISTS
|
|
27
|
+
// SQL to drop the table (properly escape the table name)
|
|
28
|
+
const dropTableSQL = `DROP TABLE IF EXISTS \`${inputs.tableName}\``
|
|
29
29
|
|
|
30
30
|
// Execute the drop table operation
|
|
31
31
|
db.prepare(dropTableSQL).run()
|
|
@@ -57,12 +57,12 @@ module.exports = {
|
|
|
57
57
|
|
|
58
58
|
// Handle SELECT clause
|
|
59
59
|
if (s3q.criteria.select) {
|
|
60
|
-
sqlQuery += s3q.criteria.select.join(', ')
|
|
60
|
+
sqlQuery += s3q.criteria.select.map((col) => `\`${col}\``).join(', ')
|
|
61
61
|
} else {
|
|
62
62
|
sqlQuery += '*'
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
sqlQuery += ` FROM
|
|
65
|
+
sqlQuery += ` FROM \`${tableName}\``
|
|
66
66
|
|
|
67
67
|
// Handle WHERE clause
|
|
68
68
|
const whereClause = buildSqliteWhereClause(
|
|
@@ -79,7 +79,7 @@ module.exports = {
|
|
|
79
79
|
const sortClauses = s3q.criteria.sort.map((sortObj) => {
|
|
80
80
|
const key = Object.keys(sortObj)[0]
|
|
81
81
|
const direction = sortObj[key] === 'ASC' ? 'ASC' : 'DESC'
|
|
82
|
-
return
|
|
82
|
+
return `\`${key}\` ${direction}`
|
|
83
83
|
})
|
|
84
84
|
sqlQuery += ` ORDER BY ${sortClauses.join(', ')}`
|
|
85
85
|
}
|