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/lib/index.js
CHANGED
|
@@ -18,7 +18,11 @@ 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
|
+
beginTransaction: require('./private/machines/begin-transaction'),
|
|
23
|
+
commitTransaction: require('./private/machines/commit-transaction'),
|
|
24
|
+
rollbackTransaction: require('./private/machines/rollback-transaction'),
|
|
25
|
+
leaseConnection: require('./private/machines/lease-connection')
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
const WET_MACHINES = Object.fromEntries(
|
|
@@ -255,11 +259,16 @@ module.exports = {
|
|
|
255
259
|
)
|
|
256
260
|
}
|
|
257
261
|
|
|
262
|
+
const modelDefinition = modelInfo.definition || modelInfo.attributes
|
|
263
|
+
|
|
258
264
|
registeredDryModels[modelIdentity] = {
|
|
265
|
+
identity: modelIdentity,
|
|
259
266
|
primaryKey: modelInfo.primaryKey,
|
|
260
|
-
|
|
267
|
+
definition: modelDefinition,
|
|
268
|
+
attributes: modelDefinition,
|
|
261
269
|
tableName: modelInfo.tableName,
|
|
262
|
-
identity: modelInfo.identity
|
|
270
|
+
identity: modelInfo.identity,
|
|
271
|
+
datastore: datastoreName
|
|
263
272
|
}
|
|
264
273
|
|
|
265
274
|
// Uncomment for detailed debugging:
|
|
@@ -354,6 +363,14 @@ module.exports = {
|
|
|
354
363
|
delete registeredDryModels[modelIdentity]
|
|
355
364
|
}
|
|
356
365
|
})
|
|
366
|
+
|
|
367
|
+
// Safety cleanup: If this is the last datastore being torn down,
|
|
368
|
+
// clear any remaining models to prevent registration conflicts
|
|
369
|
+
if (Object.keys(registeredDsEntries).length === 0) {
|
|
370
|
+
Object.keys(registeredDryModels).forEach((modelIdentity) => {
|
|
371
|
+
delete registeredDryModels[modelIdentity]
|
|
372
|
+
})
|
|
373
|
+
}
|
|
357
374
|
} catch (err) {
|
|
358
375
|
return done(err)
|
|
359
376
|
}
|
|
@@ -533,30 +550,12 @@ module.exports = {
|
|
|
533
550
|
// ║║║╠═╣ ║ ║╚╗╔╝║╣ ││ │││││ └─┐│ │├─┘├─┘│ │├┬┘ │
|
|
534
551
|
// ╝╚╝╩ ╩ ╩ ╩ ╚╝ ╚═╝ └┘└─┘┴┘└┘ └─┘└─┘┴ ┴ └─┘┴└─ ┴
|
|
535
552
|
// 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
|
-
},
|
|
553
|
+
join: buildStdAdapterMethod(
|
|
554
|
+
require('./private/machines/join'),
|
|
555
|
+
WET_MACHINES,
|
|
556
|
+
registeredDsEntries,
|
|
557
|
+
registeredDryModels
|
|
558
|
+
),
|
|
560
559
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
561
560
|
// ██████╗ ██████╗ ██╗ //
|
|
562
561
|
// ██╔══██╗██╔═══██╗██║ //
|
|
@@ -924,5 +923,182 @@ module.exports = {
|
|
|
924
923
|
*
|
|
925
924
|
* > https://github.com/node-machine/driver-interface/blob/master/layers/migratable/set-physical-sequence.js
|
|
926
925
|
*/
|
|
927
|
-
setPhysicalSequence: DRY_MACHINES.setPhysicalSequence
|
|
926
|
+
setPhysicalSequence: DRY_MACHINES.setPhysicalSequence,
|
|
927
|
+
|
|
928
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
929
|
+
// ████████╗██████╗ █████╗ ███╗ ███╗███████╗ █████╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗ //
|
|
930
|
+
// ╚══██╔══╝██╔══██╗██╔══██╗████╗ ████║██╔════╝██╔══██╗██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║ //
|
|
931
|
+
// ██║ ██████╔╝███████║██╔████╔██║███████╗███████║██║ ██║ ██║██║ ██║██╔██╗ ██║ //
|
|
932
|
+
// ██║ ██╔══██╗██╔══██║██║╚██╔╝██║╚════██║██╔══██║██║ ██║ ██║██║ ██║██║╚██╗██║ //
|
|
933
|
+
// ██║ ██║ ██║██║ ██║██║ ╚═╝ ██║███████║██║ ██║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║ //
|
|
934
|
+
// ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ //
|
|
935
|
+
// //
|
|
936
|
+
// Transaction methods: //
|
|
937
|
+
// Methods related to database transactions (begin, commit, rollback). //
|
|
938
|
+
//////////////////////////////////////////////////////////////////////////////////////////////////
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* ╔╗ ╔═╗╔═╗╦╔╗╔ ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
942
|
+
* ╠╩╗║╣ ║ ╦║║║║ │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
943
|
+
* ╚═╝╚═╝╚═╝╩╝╚╝ ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
944
|
+
* Begin a new database transaction.
|
|
945
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
946
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
947
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
948
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
949
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
950
|
+
* @param {Function} done Callback
|
|
951
|
+
* @param {Error?}
|
|
952
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
953
|
+
*/
|
|
954
|
+
beginTransaction: function (datastoreName, options, done) {
|
|
955
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
956
|
+
const connection = options.connection
|
|
957
|
+
const meta = options.meta || {}
|
|
958
|
+
|
|
959
|
+
if (!dsEntry) {
|
|
960
|
+
return done(
|
|
961
|
+
new Error(
|
|
962
|
+
`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.)`
|
|
963
|
+
)
|
|
964
|
+
)
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
WET_MACHINES.beginTransaction({
|
|
968
|
+
connection: connection,
|
|
969
|
+
meta: meta
|
|
970
|
+
}).switch({
|
|
971
|
+
error: function (err) {
|
|
972
|
+
return done(err)
|
|
973
|
+
},
|
|
974
|
+
success: function () {
|
|
975
|
+
return done()
|
|
976
|
+
}
|
|
977
|
+
})
|
|
978
|
+
},
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* ╔═╗╔═╗╔╦╗╔╦╗╦╔╦╗ ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
982
|
+
* ║ ║ ║║║║║║║║ ║ │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
983
|
+
* ╚═╝╚═╝╩ ╩╩ ╩╩ ╩ ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
984
|
+
* Commit the database transaction on the provided connection.
|
|
985
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
986
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
987
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
988
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
989
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
990
|
+
* @param {Function} done Callback
|
|
991
|
+
* @param {Error?}
|
|
992
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
993
|
+
*/
|
|
994
|
+
commitTransaction: function (datastoreName, options, done) {
|
|
995
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
996
|
+
const connection = options.connection
|
|
997
|
+
const meta = options.meta || {}
|
|
998
|
+
|
|
999
|
+
if (!dsEntry) {
|
|
1000
|
+
return done(
|
|
1001
|
+
new Error(
|
|
1002
|
+
`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.)`
|
|
1003
|
+
)
|
|
1004
|
+
)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
WET_MACHINES.commitTransaction({
|
|
1008
|
+
connection: connection,
|
|
1009
|
+
meta: meta
|
|
1010
|
+
}).switch({
|
|
1011
|
+
error: function (err) {
|
|
1012
|
+
return done(err)
|
|
1013
|
+
},
|
|
1014
|
+
success: function () {
|
|
1015
|
+
return done()
|
|
1016
|
+
}
|
|
1017
|
+
})
|
|
1018
|
+
},
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* ╦═╗╔═╗╦ ╦ ╔╗ ╔═╗╔═╗╦╔═
|
|
1022
|
+
* ╠╦╝║ ║║ ║ ╠╩╗╠═╣║ ╠╩╗
|
|
1023
|
+
* ╩╚═╚═╝╩═╝╩═╝╚═╝╩ ╩╚═╝╩ ╩
|
|
1024
|
+
* ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
1025
|
+
* │ ├┬┘├─┤│││└─┐├─┤│ │ ││ ││││
|
|
1026
|
+
* ┴ ┴└─┴ ┴┘└┘└─┘┴ ┴└─┘ ┴ ┴└─┘┘└┘
|
|
1027
|
+
* Rollback the database transaction on the provided connection.
|
|
1028
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1029
|
+
* @param {String} datastoreName The name of the datastore to perform the query on.
|
|
1030
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1031
|
+
* @param {Dictionary} options Transaction options (connection, meta).
|
|
1032
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1033
|
+
* @param {Function} done Callback
|
|
1034
|
+
* @param {Error?}
|
|
1035
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1036
|
+
*/
|
|
1037
|
+
rollbackTransaction: function (datastoreName, options, done) {
|
|
1038
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
1039
|
+
const connection = options.connection
|
|
1040
|
+
const meta = options.meta || {}
|
|
1041
|
+
|
|
1042
|
+
if (!dsEntry) {
|
|
1043
|
+
return done(
|
|
1044
|
+
new Error(
|
|
1045
|
+
`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.)`
|
|
1046
|
+
)
|
|
1047
|
+
)
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
WET_MACHINES.rollbackTransaction({
|
|
1051
|
+
connection: connection,
|
|
1052
|
+
meta: meta
|
|
1053
|
+
}).switch({
|
|
1054
|
+
error: function (err) {
|
|
1055
|
+
return done(err)
|
|
1056
|
+
},
|
|
1057
|
+
success: function () {
|
|
1058
|
+
return done()
|
|
1059
|
+
}
|
|
1060
|
+
})
|
|
1061
|
+
},
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* ╦ ╔═╗╔═╗╔═╗╔═╗ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
|
|
1065
|
+
* ║ ║╣ ╠═╣╚═╗║╣ │ │ │││││││├┤ │ │ ││ ││││
|
|
1066
|
+
* ╩═╝╚═╝╩ ╩╚═╝╚═╝ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
|
|
1067
|
+
* Lease a connection from the datastore for use in a transaction.
|
|
1068
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1069
|
+
* @param {String} datastoreName The name of the datastore to lease from.
|
|
1070
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1071
|
+
* @param {Dictionary} meta Meta options.
|
|
1072
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1073
|
+
* @param {Function} done Callback
|
|
1074
|
+
* @param {Error?}
|
|
1075
|
+
* @param {Dictionary?} connection
|
|
1076
|
+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
1077
|
+
*/
|
|
1078
|
+
leaseConnection: function (datastoreName, meta, done) {
|
|
1079
|
+
const dsEntry = registeredDsEntries[datastoreName]
|
|
1080
|
+
|
|
1081
|
+
if (!dsEntry) {
|
|
1082
|
+
return done(
|
|
1083
|
+
new Error(
|
|
1084
|
+
`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.)`
|
|
1085
|
+
)
|
|
1086
|
+
)
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
WET_MACHINES.leaseConnection({
|
|
1090
|
+
manager: dsEntry.manager,
|
|
1091
|
+
meta: meta || {}
|
|
1092
|
+
}).switch({
|
|
1093
|
+
error: function (err) {
|
|
1094
|
+
return done(err)
|
|
1095
|
+
},
|
|
1096
|
+
failed: function (err) {
|
|
1097
|
+
return done(err)
|
|
1098
|
+
},
|
|
1099
|
+
success: function (connection) {
|
|
1100
|
+
return done(null, connection)
|
|
1101
|
+
}
|
|
1102
|
+
})
|
|
1103
|
+
}
|
|
928
1104
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|