meadow 2.0.17 → 2.0.18

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.
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * Meadow Provider - SQLite (via better-sqlite3)
3
+ *
2
4
  * @license MIT
3
5
  * @author <steven@velozo.com>
4
6
  */
@@ -14,21 +16,16 @@ var MeadowProvider = function ()
14
16
  var _Fable = pFable;
15
17
 
16
18
  /**
17
- * Build a connection pool, shared within this provider.
18
- * This may be more performant as a shared object.
19
+ * Get the better-sqlite3 database instance from the connection provider.
20
+ *
21
+ * The connection provider (meadow-connection-sqlite) stores the database
22
+ * instance on .db after connectAsync() completes.
19
23
  */
20
24
  var getDB = function ()
21
25
  {
22
- if (typeof (_Fable.MeadowMySQLConnectionPool) == 'object')
26
+ if (typeof (_Fable.MeadowSQLiteProvider) == 'object' && _Fable.MeadowSQLiteProvider.connected)
23
27
  {
24
- // This is where the old-style SQL Connection pool is. Refactor doesn't even look for it anymore
25
- return _Fable.MeadowMySQLConnectionPool;
26
- }
27
-
28
- // New-style default connection pool provider
29
- if (typeof (_Fable.MeadowMySQLProvider) == 'object' && _Fable.MeadowMySQLProvider.connected)
30
- {
31
- return _Fable.MeadowMySQLProvider.pool;
28
+ return _Fable.MeadowSQLiteProvider.db;
32
29
  }
33
30
 
34
31
  return false;
@@ -36,26 +33,43 @@ var MeadowProvider = function ()
36
33
 
37
34
  var getProvider = function ()
38
35
  {
39
- if (typeof (_Fable.MeadowMySQLConnectionPool) == 'object')
36
+ if (typeof (_Fable.MeadowSQLiteProvider) == 'object')
40
37
  {
41
- // This is where the old-style SQL Connection pool is. Refactor doesn't even look for it anymore
42
- return _Fable.MeadowMySQLConnectionPool;
38
+ return _Fable.MeadowSQLiteProvider;
43
39
  }
44
40
 
45
- // New-style default connection pool provider
46
- if (typeof (_Fable.MeadowMySQLProvider) == 'object')
41
+ return false;
42
+ };
43
+
44
+ /**
45
+ * Replace NOW() with SQLite-compatible datetime('now') in query bodies.
46
+ *
47
+ * The FoxHound SQLite dialect generates NOW() for date stamps, but SQLite
48
+ * does not support NOW(). We replace it at the provider level so the
49
+ * dialect can stay consistent with the other providers.
50
+ */
51
+ var fixDateFunctions = function (pQueryBody)
52
+ {
53
+ if (typeof (pQueryBody) !== 'string')
47
54
  {
48
- return _Fable.MeadowMySQLProvider;
55
+ return pQueryBody;
49
56
  }
57
+ // Replace NOW() and NOW(3) with SQLite's datetime function
58
+ return pQueryBody.replace(/NOW\(\d*\)/g, "datetime('now')");
59
+ };
50
60
 
51
- return false;
52
- }
61
+ /**
62
+ * Convert FoxHound named parameters (:name) to better-sqlite3 format.
63
+ *
64
+ * better-sqlite3 uses @name, $name, or :name for named parameters,
65
+ * but expects them passed as an object. FoxHound generates :name syntax
66
+ * which better-sqlite3 supports natively.
67
+ */
53
68
 
54
69
  // The Meadow marshaller also passes in the Schema as the third parameter, but this is a blunt function ATM.
55
70
  var marshalRecordFromSourceToObject = function (pObject, pRecord)
56
71
  {
57
72
  // For now, crudely assign everything in pRecord to pObject
58
- // This is safe in this context, and we don't want to slow down marshalling with millions of hasOwnProperty checks
59
73
  for (var tmpColumn in pRecord)
60
74
  {
61
75
  pObject[tmpColumn] = pRecord[tmpColumn];
@@ -68,205 +82,271 @@ var MeadowProvider = function ()
68
82
 
69
83
  pQuery.setDialect('SQLite').buildCreateQuery();
70
84
 
71
- // TODO: Test the query before executing
85
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
86
+
72
87
  if (pQuery.logLevel > 0)
73
88
  {
74
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
89
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
75
90
  }
76
91
 
77
- getDB().getConnection(function (pError, pDBConnection)
92
+ try
78
93
  {
79
- pDBConnection.query(
80
- pQuery.query.body,
81
- pQuery.query.parameters,
82
- function (pError, pRows)
83
- {
84
- pDBConnection.release();
85
- tmpResult.error = pError;
86
- tmpResult.value = false;
87
- try
88
- {
89
- tmpResult.value = pRows.insertId;
90
- }
91
- catch (pErrorGettingRowcount)
92
- {
93
- _Fable.log.warn('Error getting insert ID during create query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
94
- }
95
-
96
- tmpResult.executed = true;
97
- return fCallback();
98
- }
99
- );
100
- });
94
+ var tmpDB = getDB();
95
+ if (!tmpDB)
96
+ {
97
+ tmpResult.error = new Error('No SQLite database connection available.');
98
+ tmpResult.executed = true;
99
+ return fCallback();
100
+ }
101
+
102
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
103
+ var tmpInfo = tmpStatement.run(pQuery.query.parameters);
104
+
105
+ tmpResult.error = null;
106
+ tmpResult.value = false;
107
+ try
108
+ {
109
+ tmpResult.value = Number(tmpInfo.lastInsertRowid);
110
+ }
111
+ catch (pErrorGettingRowcount)
112
+ {
113
+ _Fable.log.warn('Error getting insert ID during create query', { Body: tmpQueryBody, Parameters: pQuery.query.parameters });
114
+ }
115
+
116
+ tmpResult.executed = true;
117
+ return fCallback();
118
+ }
119
+ catch (pError)
120
+ {
121
+ tmpResult.error = pError;
122
+ tmpResult.value = false;
123
+ tmpResult.executed = true;
124
+ return fCallback();
125
+ }
101
126
  };
102
127
 
103
- // This is a synchronous read, good for a few records.
104
- // TODO: Add a pipe-able read for huge sets
105
128
  var Read = function (pQuery, fCallback)
106
129
  {
107
130
  var tmpResult = pQuery.parameters.result;
108
131
 
109
- pQuery.setDialect('MySQL').buildReadQuery();
132
+ pQuery.setDialect('SQLite').buildReadQuery();
133
+
134
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
110
135
 
111
136
  if (pQuery.logLevel > 0)
112
137
  {
113
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
138
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
114
139
  }
115
140
 
116
- getDB().getConnection(function (pError, pDBConnection)
141
+ try
117
142
  {
118
- pDBConnection.query(
119
- pQuery.query.body,
120
- pQuery.query.parameters,
121
- function (pError, pRows)
122
- {
123
- pDBConnection.release();
124
- tmpResult.error = pError;
125
- tmpResult.value = pRows;
126
- tmpResult.executed = true;
127
- return fCallback();
128
- }
129
- );
130
- });
143
+ var tmpDB = getDB();
144
+ if (!tmpDB)
145
+ {
146
+ tmpResult.error = new Error('No SQLite database connection available.');
147
+ tmpResult.executed = true;
148
+ return fCallback();
149
+ }
150
+
151
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
152
+ var tmpRows = tmpStatement.all(pQuery.query.parameters);
153
+
154
+ tmpResult.error = null;
155
+ tmpResult.value = tmpRows;
156
+ tmpResult.executed = true;
157
+ return fCallback();
158
+ }
159
+ catch (pError)
160
+ {
161
+ tmpResult.error = pError;
162
+ tmpResult.value = false;
163
+ tmpResult.executed = true;
164
+ return fCallback();
165
+ }
131
166
  };
132
167
 
133
168
  var Update = function (pQuery, fCallback)
134
169
  {
135
170
  var tmpResult = pQuery.parameters.result;
136
171
 
137
- pQuery.setDialect('MySQL').buildUpdateQuery();
172
+ pQuery.setDialect('SQLite').buildUpdateQuery();
173
+
174
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
138
175
 
139
176
  if (pQuery.logLevel > 0)
140
177
  {
141
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
178
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
142
179
  }
143
180
 
144
- getDB().getConnection(function (pError, pDBConnection)
181
+ try
145
182
  {
146
- pDBConnection.query(
147
- pQuery.query.body,
148
- pQuery.query.parameters,
149
- function (pError, pRows)
150
- {
151
- pDBConnection.release();
152
- tmpResult.error = pError;
153
- tmpResult.value = pRows;
154
- tmpResult.executed = true;
155
- return fCallback();
156
- }
157
- );
158
- });
159
- }
183
+ var tmpDB = getDB();
184
+ if (!tmpDB)
185
+ {
186
+ tmpResult.error = new Error('No SQLite database connection available.');
187
+ tmpResult.executed = true;
188
+ return fCallback();
189
+ }
190
+
191
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
192
+ var tmpInfo = tmpStatement.run(pQuery.query.parameters);
193
+
194
+ tmpResult.error = null;
195
+ tmpResult.value = tmpInfo;
196
+ tmpResult.executed = true;
197
+ return fCallback();
198
+ }
199
+ catch (pError)
200
+ {
201
+ tmpResult.error = pError;
202
+ tmpResult.value = false;
203
+ tmpResult.executed = true;
204
+ return fCallback();
205
+ }
206
+ };
160
207
 
161
208
  var Delete = function (pQuery, fCallback)
162
209
  {
163
210
  var tmpResult = pQuery.parameters.result;
164
211
 
165
- pQuery.setDialect('MySQL').buildDeleteQuery();
212
+ pQuery.setDialect('SQLite').buildDeleteQuery();
213
+
214
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
166
215
 
167
216
  if (pQuery.logLevel > 0)
168
217
  {
169
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
218
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
170
219
  }
171
220
 
172
- getDB().getConnection(function (pError, pDBConnection)
221
+ try
173
222
  {
174
- pDBConnection.query
175
- (
176
- pQuery.query.body,
177
- pQuery.query.parameters,
178
- function (pError, pRows)
179
- {
180
- pDBConnection.release();
181
- tmpResult.error = pError;
182
- tmpResult.value = false;
183
- try
184
- {
185
- tmpResult.value = pRows.affectedRows;
186
- }
187
- catch (pErrorGettingRowcount)
188
- {
189
- _Fable.log.warn('Error getting affected rowcount during delete query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
190
- }
191
- tmpResult.executed = true;
192
- return fCallback();
193
- }
194
- );
195
- });
223
+ var tmpDB = getDB();
224
+ if (!tmpDB)
225
+ {
226
+ tmpResult.error = new Error('No SQLite database connection available.');
227
+ tmpResult.executed = true;
228
+ return fCallback();
229
+ }
230
+
231
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
232
+ var tmpInfo = tmpStatement.run(pQuery.query.parameters);
233
+
234
+ tmpResult.error = null;
235
+ tmpResult.value = false;
236
+ try
237
+ {
238
+ tmpResult.value = tmpInfo.changes;
239
+ }
240
+ catch (pErrorGettingRowcount)
241
+ {
242
+ _Fable.log.warn('Error getting affected rowcount during delete query', { Body: tmpQueryBody, Parameters: pQuery.query.parameters });
243
+ }
244
+ tmpResult.executed = true;
245
+ return fCallback();
246
+ }
247
+ catch (pError)
248
+ {
249
+ tmpResult.error = pError;
250
+ tmpResult.value = false;
251
+ tmpResult.executed = true;
252
+ return fCallback();
253
+ }
196
254
  };
197
255
 
198
256
  var Undelete = function (pQuery, fCallback)
199
257
  {
200
258
  var tmpResult = pQuery.parameters.result;
201
259
 
202
- pQuery.setDialect('MySQL').buildUndeleteQuery();
260
+ pQuery.setDialect('SQLite').buildUndeleteQuery();
261
+
262
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
203
263
 
204
264
  if (pQuery.logLevel > 0)
205
265
  {
206
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
266
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
207
267
  }
208
268
 
209
- getDB().getConnection(function (pError, pDBConnection)
269
+ try
210
270
  {
211
- pDBConnection.query
212
- (
213
- pQuery.query.body,
214
- pQuery.query.parameters,
215
- function (pError, pRows)
216
- {
217
- pDBConnection.release();
218
- tmpResult.error = pError;
219
- tmpResult.value = false;
220
- try
221
- {
222
- tmpResult.value = pRows.affectedRows;
223
- }
224
- catch (pErrorGettingRowcount)
225
- {
226
- _Fable.log.warn('Error getting affected rowcount during delete query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
227
- }
228
- tmpResult.executed = true;
229
- return fCallback();
230
- }
231
- );
232
- });
271
+ var tmpDB = getDB();
272
+ if (!tmpDB)
273
+ {
274
+ tmpResult.error = new Error('No SQLite database connection available.');
275
+ tmpResult.executed = true;
276
+ return fCallback();
277
+ }
278
+
279
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
280
+ var tmpInfo = tmpStatement.run(pQuery.query.parameters);
281
+
282
+ tmpResult.error = null;
283
+ tmpResult.value = false;
284
+ try
285
+ {
286
+ tmpResult.value = tmpInfo.changes;
287
+ }
288
+ catch (pErrorGettingRowcount)
289
+ {
290
+ _Fable.log.warn('Error getting affected rowcount during undelete query', { Body: tmpQueryBody, Parameters: pQuery.query.parameters });
291
+ }
292
+ tmpResult.executed = true;
293
+ return fCallback();
294
+ }
295
+ catch (pError)
296
+ {
297
+ tmpResult.error = pError;
298
+ tmpResult.value = false;
299
+ tmpResult.executed = true;
300
+ return fCallback();
301
+ }
233
302
  };
234
303
 
235
304
  var Count = function (pQuery, fCallback)
236
305
  {
237
306
  var tmpResult = pQuery.parameters.result;
238
307
 
239
- pQuery.setDialect('MySQL').buildCountQuery();
308
+ pQuery.setDialect('SQLite').buildCountQuery();
309
+
310
+ var tmpQueryBody = fixDateFunctions(pQuery.query.body);
240
311
 
241
312
  if (pQuery.logLevel > 0)
242
313
  {
243
- _Fable.log.trace(pQuery.query.body, pQuery.query.parameters);
314
+ _Fable.log.trace(tmpQueryBody, pQuery.query.parameters);
244
315
  }
245
316
 
246
- getDB().getConnection(function (pError, pDBConnection)
317
+ try
247
318
  {
248
- pDBConnection.query(
249
- pQuery.query.body,
250
- pQuery.query.parameters,
251
- // The SQLite library also returns the Fields as the third parameter
252
- function (pError, pRows)
253
- {
254
- pDBConnection.release();
255
- tmpResult.executed = true;
256
- tmpResult.error = pError;
257
- tmpResult.value = false;
258
- try
259
- {
260
- tmpResult.value = pRows[0].RowCount;
261
- }
262
- catch (pErrorGettingRowcount)
263
- {
264
- _Fable.log.warn('Error getting rowcount during count query', { Body: pQuery.query.body, Parameters: pQuery.query.parameters });
265
- }
266
- return fCallback();
267
- }
268
- );
269
- });
319
+ var tmpDB = getDB();
320
+ if (!tmpDB)
321
+ {
322
+ tmpResult.error = new Error('No SQLite database connection available.');
323
+ tmpResult.executed = true;
324
+ return fCallback();
325
+ }
326
+
327
+ var tmpStatement = tmpDB.prepare(tmpQueryBody);
328
+ var tmpRows = tmpStatement.all(pQuery.query.parameters);
329
+
330
+ tmpResult.executed = true;
331
+ tmpResult.error = null;
332
+ tmpResult.value = false;
333
+ try
334
+ {
335
+ tmpResult.value = tmpRows[0].RowCount;
336
+ }
337
+ catch (pErrorGettingRowcount)
338
+ {
339
+ _Fable.log.warn('Error getting rowcount during count query', { Body: tmpQueryBody, Parameters: pQuery.query.parameters });
340
+ }
341
+ return fCallback();
342
+ }
343
+ catch (pError)
344
+ {
345
+ tmpResult.error = pError;
346
+ tmpResult.value = false;
347
+ tmpResult.executed = true;
348
+ return fCallback();
349
+ }
270
350
  };
271
351
 
272
352
  var tmpNewProvider = (
@@ -0,0 +1,5 @@
1
+ SELECT
2
+ IDAnimal,
3
+ Type AS AnimalTypeCustom
4
+ FROM
5
+ FableTest