@saltcorn/sql 0.5.3 → 0.5.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/table-provider.js +133 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/sql",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Actions and views based on SQL",
5
5
  "main": "index.js",
6
6
  "dependencies": {
package/table-provider.js CHANGED
@@ -163,22 +163,7 @@ const sqlEscapeObject = (o) => {
163
163
  return r;
164
164
  };
165
165
 
166
- const runQuery = async (cfg, where, opts) => {
167
- const sqlTmpl = cfg?.sql || "";
168
- const template = _.template(sqlTmpl || "", {
169
- evaluate: /\{\{#(.+?)\}\}/g,
170
- interpolate: /\{\{([^#].+?)\}\}/g,
171
- });
172
-
173
- const qctx = {};
174
-
175
- if (opts?.forUser) qctx.user = sqlEscapeObject(opts.forUser);
176
- else if (where?.forUser)
177
- qctx.user = sqlEscapeObject(where.forUser); //workaround legacy bug
178
- else qctx.user = null;
179
-
180
- const sql = template(qctx);
181
-
166
+ const getSqlQuery = (sql, cfg, where, opts) => {
182
167
  const is_sqlite = db.isSQLite;
183
168
  const opt = {
184
169
  database: is_sqlite ? "SQLite" : "PostgreSQL",
@@ -189,12 +174,21 @@ const runQuery = async (cfg, where, opts) => {
189
174
  sqlQ = sql;
190
175
  } else {
191
176
  const { ast } = parser.parse(sql, opt);
192
-
177
+ /*console.log(
178
+ JSON.stringify(
179
+ parser.parse(
180
+ `select * from "users" where "email" ILIKE concat('%',cast($1 as text),'%')`,
181
+ opt
182
+ ).ast,
183
+ null,
184
+ 2
185
+ )
186
+ );*/
193
187
  const colNames = new Set((cfg?.columns || []).map((c) => c.name));
194
188
  let phIndex = 1;
195
189
  //console.log(ast[0].columns);
196
- for (const k of Object.keys(where)) {
197
- if (!colNames.has(k)) continue;
190
+ const proc_k_where = (k, wherek) => {
191
+ if (!colNames.has(k)) return;
198
192
  const sqlCol =
199
193
  ast[0].columns == "*"
200
194
  ? {
@@ -240,14 +234,66 @@ const runQuery = async (cfg, where, opts) => {
240
234
  column: db.sqlsanitize(k),
241
235
  };
242
236
  }
237
+
243
238
  const newClause = {
244
239
  type: "binary_expr",
245
- operator: where[k]?.ilike && !sqlAggrCol ? "ILIKE" : "=",
240
+ operator:
241
+ wherek?.ilike && !sqlAggrCol
242
+ ? "ILIKE"
243
+ : wherek?.gt && !sqlAggrCol
244
+ ? wherek.equal
245
+ ? ">="
246
+ : ">"
247
+ : wherek?.lt && !sqlAggrCol
248
+ ? wherek.equal
249
+ ? "<="
250
+ : "<"
251
+ : "=",
246
252
  left,
247
- right: { type: "number", value: "$" + phIndex },
253
+ right:
254
+ wherek?.ilike && !sqlAggrCol
255
+ ? {
256
+ type: "function",
257
+ name: {
258
+ name: [
259
+ {
260
+ type: "default",
261
+ value: "concat",
262
+ },
263
+ ],
264
+ },
265
+ args: {
266
+ type: "expr_list",
267
+ value: [
268
+ {
269
+ type: "single_quote_string",
270
+ value: "%",
271
+ },
272
+ {
273
+ type: "cast",
274
+ keyword: "cast",
275
+ expr: { type: "number", value: "$" + phIndex },
276
+ symbol: "as",
277
+ target: [
278
+ {
279
+ dataType: "TEXT",
280
+ },
281
+ ],
282
+ },
283
+
284
+ {
285
+ type: "single_quote_string",
286
+ value: "%",
287
+ },
288
+ ],
289
+ },
290
+ }
291
+ : { type: "number", value: "$" + phIndex },
248
292
  };
249
293
  phIndex += 1;
250
- phValues.push(where[k]?.ilike ? where[k]?.ilike : where[k]);
294
+ phValues.push(
295
+ wherek?.ilike || wherek?.gt || wherek?.lt || wherek
296
+ );
251
297
  if (!sqlAggrCol) {
252
298
  if (!ast[0].where) ast[0].where = newClause;
253
299
  else {
@@ -269,6 +315,10 @@ const runQuery = async (cfg, where, opts) => {
269
315
  };
270
316
  }
271
317
  }
318
+ };
319
+ for (const k of Object.keys(where)) {
320
+ if (Array.isArray(where[k])) where[k].forEach((w) => proc_k_where(k, w));
321
+ else proc_k_where(k, where[k]);
272
322
  }
273
323
  if (where?.limit && where?.offset) {
274
324
  ast[0].limit = {
@@ -346,6 +396,28 @@ const runQuery = async (cfg, where, opts) => {
346
396
  }
347
397
  sqlQ = parser.sqlify(ast, opt);
348
398
  }
399
+ return { sqlQ, phValues };
400
+ };
401
+
402
+ const runQuery = async (cfg, where, opts) => {
403
+ const sqlTmpl = cfg?.sql || "";
404
+ const template = _.template(sqlTmpl || "", {
405
+ evaluate: /\{\{#(.+?)\}\}/g,
406
+ interpolate: /\{\{([^#].+?)\}\}/g,
407
+ });
408
+
409
+ const qctx = {};
410
+
411
+ if (opts?.forUser) qctx.user = sqlEscapeObject(opts.forUser);
412
+ else if (where?.forUser)
413
+ qctx.user = sqlEscapeObject(where.forUser); //workaround legacy bug
414
+ else qctx.user = null;
415
+
416
+ const sql = template(qctx);
417
+ const is_sqlite = db.isSQLite;
418
+
419
+ const { sqlQ, phValues } = getSqlQuery(sql, cfg, where, opts);
420
+
349
421
  const client = is_sqlite ? db : await db.getClient();
350
422
  await client.query(`BEGIN;`);
351
423
  if (!is_sqlite) {
@@ -353,7 +425,7 @@ const runQuery = async (cfg, where, opts) => {
353
425
  await client.query(`SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`);
354
426
  }
355
427
 
356
- //console.log({ sqlQ, phValues, opts });
428
+ //console.trace({ sqlQ, phValues, opts });
357
429
  const qres = await client.query(sqlQ, phValues);
358
430
  qres.query = sqlQ;
359
431
  await client.query(`ROLLBACK;`);
@@ -362,6 +434,41 @@ const runQuery = async (cfg, where, opts) => {
362
434
  return qres;
363
435
  };
364
436
 
437
+ const countRows = async (cfg, where, opts) => {
438
+ const sqlTmpl = cfg?.sql || "";
439
+ const template = _.template(sqlTmpl || "", {
440
+ evaluate: /\{\{#(.+?)\}\}/g,
441
+ interpolate: /\{\{([^#].+?)\}\}/g,
442
+ });
443
+
444
+ const qctx = {};
445
+
446
+ if (opts?.forUser) qctx.user = sqlEscapeObject(opts.forUser);
447
+ else if (where?.forUser)
448
+ qctx.user = sqlEscapeObject(where.forUser); //workaround legacy bug
449
+ else qctx.user = null;
450
+
451
+ const sql = template(qctx);
452
+ const is_sqlite = db.isSQLite;
453
+
454
+ const { sqlQ, phValues } = getSqlQuery(sql, cfg, where, opts);
455
+
456
+ const client = is_sqlite ? db : await db.getClient();
457
+ await client.query(`BEGIN;`);
458
+ if (!is_sqlite) {
459
+ await client.query(`SET LOCAL search_path TO "${db.getTenantSchema()}";`);
460
+ await client.query(`SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`);
461
+ }
462
+
463
+ //console.trace({ sqlQ, phValues, opts });
464
+ const qres = await client.query(`select count(*) from (${sqlQ})`, phValues);
465
+ qres.query = sqlQ;
466
+ await client.query(`ROLLBACK;`);
467
+
468
+ if (!is_sqlite) client.release(true);
469
+ return qres.rows[0].count;
470
+ };
471
+
365
472
  module.exports = {
366
473
  "SQL query": {
367
474
  configuration_workflow,
@@ -372,6 +479,9 @@ module.exports = {
372
479
  const qres = await runQuery(cfg, where, opts);
373
480
  return qres.rows;
374
481
  },
482
+ countRows: async (where, opts) => {
483
+ return await countRows(cfg, where, opts);
484
+ },
375
485
  };
376
486
  },
377
487
  },