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.
Files changed (33) hide show
  1. package/lib/index.js +215 -28
  2. package/lib/private/build-std-adapter-method.js +8 -4
  3. package/lib/private/machines/avg-records.js +1 -1
  4. package/lib/private/machines/begin-transaction.js +51 -0
  5. package/lib/private/machines/commit-transaction.js +50 -0
  6. package/lib/private/machines/compile-statement.js +62 -0
  7. package/lib/private/machines/count-records.js +1 -1
  8. package/lib/private/machines/create-each-record.js +1 -1
  9. package/lib/private/machines/create-record.js +2 -2
  10. package/lib/private/machines/define-physical-model.js +18 -9
  11. package/lib/private/machines/destroy-records.js +21 -8
  12. package/lib/private/machines/drop-physical-model.js +2 -2
  13. package/lib/private/machines/find-records.js +3 -3
  14. package/lib/private/machines/join.js +232 -66
  15. package/lib/private/machines/lease-connection.js +58 -0
  16. package/lib/private/machines/parse-native-query-error.js +90 -0
  17. package/lib/private/machines/parse-native-query-result.js +59 -0
  18. package/lib/private/machines/private/build-sqlite-where-clause.js +10 -8
  19. package/lib/private/machines/private/compile-statement.js +382 -0
  20. package/lib/private/machines/private/generate-join-sql-query.js +14 -6
  21. package/lib/private/machines/private/process-each-record.js +21 -4
  22. package/lib/private/machines/private/process-native-error.js +85 -40
  23. package/lib/private/machines/rollback-transaction.js +50 -0
  24. package/lib/private/machines/send-native-query.js +100 -0
  25. package/lib/private/machines/sum-records.js +1 -1
  26. package/lib/private/machines/update-records.js +27 -10
  27. package/package.json +8 -3
  28. package/tests/index.js +1 -1
  29. package/tests/runner.js +99 -0
  30. package/tests/transaction.test.js +562 -0
  31. package/tests/adapter.test.js +0 -534
  32. package/tests/datatypes.test.js +0 -293
  33. 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
- attributes: modelInfo.definition || modelInfo.attributes,
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: function join(datastoreName, query, cb) {
537
- var datastore = registeredDsEntries[datastoreName]
538
- // Models are stored globally, not per datastore
539
- var models = registeredDryModels
540
-
541
- // Add redactPasswords function if not defined
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 (err) {
52
- return done(
53
- Object.assign(new Error('Not unique'), { code: 'E_UNIQUE' })
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(${numericFieldName}), 0) as average FROM ${tableName}`
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 ${tableName}`
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 ${tableName} WHERE rowid = ?`
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
- db.prepare('BEGIN').run()
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 ${inputs.tableName} (`
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 = `${column.columnName} ${column.autoIncrement ? 'INTEGER' : getSqliteType(columnType)}`
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 ${inputs.tableName} (${column.columnName})`
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
- db.prepare('COMMIT').run()
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
- db.prepare('ROLLBACK').run()
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
- db.exec('BEGIN TRANSACTION')
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 = `SELECT * FROM ${tableName} WHERE ${sqliteWhere}`
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 = `DELETE FROM ${tableName} WHERE ${sqliteWhere}`
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
- db.exec('COMMIT')
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
- db.exec('ROLLBACK')
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 ${inputs.tableName}`
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 ${tableName}`
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 `${key} ${direction}`
82
+ return `\`${key}\` ${direction}`
83
83
  })
84
84
  sqlQuery += ` ORDER BY ${sortClauses.join(', ')}`
85
85
  }