utilitas 1995.2.11 → 1995.2.13

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/dbio.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- assertSet, ensureArray, ensureString, log as _log, need,
2
+ assertSet, ensureArray, ensureString, log as _log, need, throwError,
3
3
  } from './utilitas.mjs';
4
4
 
5
5
  import { isPrimary } from './callosum.mjs';
@@ -7,21 +7,27 @@ import { isPrimary } from './callosum.mjs';
7
7
  const _NEED = ['mysql2', 'pg'];
8
8
  const [mysqlDefaultPort, postgresqlDefaultPort] = [3306, 5432];
9
9
  const [mysqlQuote, postgresqlQuote] = ['`', ''];
10
+ const IFSC = 'information_schema';
10
11
  const orders = { '+': 'ASC', '-': 'DESC' };
12
+ const [INSERT, UPDATE, RETURNING] = ['INSERT', 'UPDATE', 'RETURNING'];
13
+ const RETURNING_ALL = ` RETURNING *`;
11
14
  const [fieldId, fieldAny] = ['id', '*'];
12
15
  const fieldCount = `COUNT(${fieldAny})`;
16
+ const direct = { direct: true };
13
17
  const comparison = ['=', '>', '<', '<>', '!='];
14
18
  const fieldNoQuote = [fieldAny, fieldCount];
15
- const log = (content) => _log(content, import.meta.url);
16
- const defaultKey = (options) => options && options.key ? options.key : fieldId;
19
+ const log = content => _log(content, import.meta.url);
20
+ const defaultKey = options => options && options.key ? options.key : fieldId;
17
21
  const queryOne = async (...args) => (await query(...args))[0];
18
22
  const assertForce = op => assert(op?.force, "Option 'force' is required.", 500);
19
23
  const [MYSQL, POSTGRESQL] = ['MYSQL', 'POSTGRESQL'];
20
24
  const quote = name => `${bracket}${name}${bracket}`;
21
25
  const join = elements => elements.join(', ');
26
+ const assembleDelete = table => `DELETE FROM ${quote(assertTable(table))}`;
27
+ const execByQuery = async (...a) => await rawQuery(...distillArguments(...a));
22
28
 
23
- let provider, pool, actExecute, bracket, actDuplicate, sqlShowTable,
24
- defaultPlaceholder, sqlShowIndex, fieldCountResult;
29
+ let provider, pool, actExecute, bracket, sqlShowTables, placeholder,
30
+ sqlShowIndexes, fieldCountResult, sqlDesc, doublePlaceholder;
25
31
 
26
32
  const init = async (options) => {
27
33
  if (options) {
@@ -34,14 +40,17 @@ const init = async (options) => {
34
40
  const mysql = await need('mysql2/promise');
35
41
  delete options.provider;
36
42
  [
37
- provider, pool, port, actExecute, bracket, actDuplicate,
38
- sqlShowTable, defaultPlaceholder, sqlShowIndex,
39
- fieldCountResult,
43
+ provider, pool, port, actExecute, bracket, sqlShowTables,
44
+ placeholder, fieldCountResult,
40
45
  ] = [
41
46
  MYSQL, mysql.createPool(options), mysqlDefaultPort,
42
- 'execute', mysqlQuote, 'ON DUPLICATE KEY', 'SHOW TABLES',
43
- '?', 'SHOW INDEXES FROM ??', fieldCount,
47
+ 'execute', mysqlQuote, 'SHOW TABLES', '?', fieldCount,
44
48
  ];
49
+ doublePlaceholder = `${placeholder}${placeholder}`;
50
+ [sqlShowIndexes, sqlDesc] = [
51
+ `SHOW INDEXES FROM ${doublePlaceholder}`,
52
+ `DESC ${doublePlaceholder}`,
53
+ ];
45
54
  break;
46
55
  case 'PG':
47
56
  case 'POSTGRE':
@@ -50,16 +59,17 @@ const init = async (options) => {
50
59
  // https://node-postgres.com/apis/pool
51
60
  const { Pool } = await need('pg');
52
61
  [
53
- provider, pool, port, actExecute, bracket, actDuplicate,
54
- sqlShowTable, defaultPlaceholder, sqlShowIndex,
62
+ provider, pool, port, actExecute, bracket, placeholder,
55
63
  fieldCountResult,
56
64
  ] = [
57
65
  POSTGRESQL, new Pool(options), postgresqlDefaultPort,
58
- 'query', postgresqlQuote, 'ON CONFLICT DO',
59
- "SELECT * FROM information_schema.tables WHERE table_schema = 'public'",
60
- '$1', 'SELECT * FROM pg_indexes WHERE tablename = $1',
61
- 'count',
66
+ 'query', postgresqlQuote, '$1', 'count',
62
67
  ];
68
+ [sqlShowTables, sqlShowIndexes, sqlDesc] = [
69
+ [`${IFSC}.tables`, 'table_schema', "'public'", direct],
70
+ ['pg_indexes', 'tablename', ''],
71
+ [`${IFSC}.COLUMNS`, 'TABLE_NAME', ''],
72
+ ].map(args => assembleQueryByKeyValue(...args));
63
73
  break;
64
74
  default:
65
75
  assert(
@@ -134,25 +144,29 @@ const execute = async (...args) => {
134
144
  }
135
145
  };
136
146
 
137
- const assertTable = (table, message, status) =>
147
+ const assertTable = (table, message, status) => {
138
148
  assert(table, message || 'Table is required.', status || 500);
149
+ return table;
150
+ };
139
151
 
140
152
  const assertKeyValue = (key, value) => {
141
153
  assert(key, 'Key is required.', 500);
142
154
  assertSet(value, 'Value is required.', 500);
143
155
  };
144
156
 
157
+ const assembleQueryByKeyValue = (table, key, value, options) =>
158
+ assembleQuery(table) + assembleKeyValue(key, value, options);
159
+
145
160
  // const assertTableKeyValue = (table, key, value) => {
146
161
  // assertTable(table);
147
162
  // assertKeyValue(key, value);
148
163
  // };
149
164
 
150
165
  const assembleQuery = (table, options) => {
151
- options = options || {};
152
166
  assertTable(table);
153
167
  const fields = [];
154
- ensureArray(options.fields).map((field) => {
155
- fields.push(fieldNoQuote.includes(field) || options.noQuote
168
+ ensureArray(options?.fields).map((field) => {
169
+ fields.push(fieldNoQuote.includes(field) || options?.noQuote
156
170
  ? field : quote(field));
157
171
  });
158
172
  if (!fields.length) { fields.push(fieldAny); }
@@ -160,73 +174,94 @@ const assembleQuery = (table, options) => {
160
174
  };
161
175
 
162
176
  const rawAssembleKeyValue = (key, value, options) => {
163
- options = options || {};
164
177
  assertKeyValue(key, value);
165
- let express = `${getComparison(value) || '='} ${defaultPlaceholder}`;
178
+ let _placeholder;
179
+ switch (provider) {
180
+ case MYSQL:
181
+ _placeholder = placeholder;
182
+ break;
183
+ case POSTGRESQL:
184
+ _placeholder = `$${options?.placeholderIndex || 1}`;
185
+ }
186
+ const val = options?.direct ? value : _placeholder;
187
+ let express = `${getComparison(value) || '='} ${val}`;
166
188
  if (Array.isArray(value)) {
167
189
  assert(value.length, 'Invalid array value.', 500);
168
- express = `IN (${defaultPlaceholder})`;
169
- } else if (value === null) { express = `IS ${defaultPlaceholder}`; }
170
- return `${options.prefix || ''}${quote(key)} ${express}`;
190
+ express = `IN (${val})`;
191
+ } else if (value === null) { express = `IS ${val}`; }
192
+ return `${options?.prefix || ''}${quote(key)} ${express}`;
171
193
  };
172
194
 
173
- const assembleKeyValue = (key, value, options) => {
174
- options = options || {};
175
- options.prefix = ' WHERE ';
176
- return rawAssembleKeyValue(key, value, options);
177
- };
195
+ const assembleKeyValue = (key, value, options) => rawAssembleKeyValue(
196
+ key, value, { ...options || {}, prefix: ' WHERE ' }
197
+ );
178
198
 
179
199
  const pushValue = (values, item) => values.push(
180
200
  Object.isObject(item) || Array.isArray(item) ? JSON.stringify(item) : item
181
201
  );
182
202
 
183
203
  const assembleSet = (data, options) => {
184
- options = options || {};
185
- const [isArray, result] = [options.asArray || Array.isArray(data), []];
204
+ const [isArray, result] = [options?.asArray || Array.isArray(data), []];
186
205
  ensureArray(data).map((item) => {
187
206
  assert(Object.keys(item).length, 'Fields are required.', 500);
188
- let [keys, vals, values, dupSql] = [[], [], [], []];
207
+ let [pairs, keys, vals, values, dupSql, i, sql]
208
+ = [[], [], [], [], [], 0, ''];
189
209
  for (let k in item) {
190
210
  keys.push(k);
191
211
  switch (provider) {
192
- case MYSQL: vals.push('?'); break;
193
- case POSTGRESQL: vals.push(`$${vals.length + 1}`); break;
212
+ case MYSQL:
213
+ vals.push('?');
214
+ pairs.push(`${quote(k)} = ?`);
215
+ break;
216
+ case POSTGRESQL:
217
+ vals.push(`$${++i}`);
218
+ pairs.push(`${quote(k)} = $${i}`);
194
219
  }
195
220
  pushValue(values, item[k]);
196
221
  }
197
- if (options.upsert) {
222
+ if (options?.upsert) {
198
223
  for (let k in item) {
199
- dupSql.push(`${quote(k)} = ?`);
224
+ switch (provider) {
225
+ case MYSQL: dupSql.push(`${quote(k)} = ?`); break;
226
+ case POSTGRESQL: dupSql.push(`${quote(k)} = $${++i}`); break;
227
+ }
200
228
  pushValue(values, item[k]);
201
229
  }
202
- dupSql = ` ${actDuplicate} UPDATE ${join(dupSql)}`;
230
+ switch (provider) {
231
+ case MYSQL:
232
+ dupSql = ` ON DUPLICATE KEY UPDATE ${join(dupSql)}`;
233
+ break;
234
+ case POSTGRESQL:
235
+ const target = options?.key ? ` (${options?.key})` : '';
236
+ dupSql = ` ON CONFLICT${target} DO UPDATE SET ${join(dupSql)}`;
237
+ break;
238
+ }
203
239
  } else { dupSql = ''; }
204
- const sql = `${options.prefix || ''}`
205
- + `(${join(keys.map(quote))}) VALUES (${join(vals)})${dupSql}`
206
- + `${options.subfix || ''}`
240
+ switch (options?.action) {
241
+ case INSERT:
242
+ sql = `(${join(keys.map(quote))}) VALUES (${join(vals)})`;
243
+ break;
244
+ case UPDATE:
245
+ sql = `SET ${pairs.join(', ')}`;
246
+ break;
247
+ default:
248
+ throwError(`Invalid action: '${options?.action}'.`, 400);
249
+ }
250
+ sql = `${options?.prefix || ''}${sql}${dupSql}${options?.subfix || ''}`;
207
251
  result.push({ sql, values, object: item });
208
252
  });
209
253
  return isArray ? result : result[0];
210
254
  };
211
255
 
212
- const assembleInsert = (table, data, options) => {
213
- options = options || {};
214
- assertTable(table);
215
- options.prefix = `INSERT INTO ${quote(table)} `;
216
- return assembleSet(data, options);
217
- };
218
-
219
- const assembleUpdate = (table, data, options) => {
220
- options = options || {};
221
- assertTable(table);
222
- options.prefix = `UPDATE ${quote(table)} `;
223
- return assembleSet(data, options);
224
- };
256
+ const assembleInsert = (table, data, options) => assembleSet(data, {
257
+ ...options || {}, action: INSERT,
258
+ prefix: `INSERT INTO ${quote(assertTable(table))} `,
259
+ });
225
260
 
226
- const assembleDelete = (table) => {
227
- assertTable(table);
228
- return `DELETE FROM ${quote(table)}`;
229
- };
261
+ const assembleUpdate = (table, data, options) => assembleSet(data, {
262
+ ...options || {}, action: UPDATE,
263
+ prefix: `UPDATE ${quote(assertTable(table))} `,
264
+ });
230
265
 
231
266
  const assembleTail = (options) => {
232
267
  let sort = [];
@@ -240,7 +275,7 @@ const assembleTail = (options) => {
240
275
  };
241
276
 
242
277
  const tables = async (options) => {
243
- const resp = await query(sqlShowTable);
278
+ const resp = await query(sqlShowTables);
244
279
  return options?.raw ? resp : resp.map(x => {
245
280
  switch (provider) {
246
281
  case MYSQL: return Object.values(x)[0];
@@ -251,15 +286,18 @@ const tables = async (options) => {
251
286
 
252
287
  const desc = async (table, options) => {
253
288
  assertTable(table);
254
- const [resp, result] = [await query('DESC ??', [table]), {}];
289
+ const [resp, result] = [await query(sqlDesc, [table]), {}];
255
290
  if (options?.raw) { return resp; }
256
- resp.map(x => result[x.Field] = x);
291
+ switch (provider) {
292
+ case MYSQL: resp.map(x => result[x.Field] = x); break;
293
+ case POSTGRESQL: resp.map(x => result[x.column_name] = x); break;
294
+ }
257
295
  return result;
258
296
  };
259
297
 
260
298
  const indexes = async (table, options) => {
261
299
  assertTable(table);
262
- const [resp, result] = [await query(sqlShowIndex, [table]), {}];
300
+ const [resp, result] = [await query(sqlShowIndexes, [table]), {}];
263
301
  if (options?.raw) { return resp; }
264
302
  switch (provider) {
265
303
  case MYSQL: resp.map(x => result[x.Key_name] = x); break;
@@ -271,36 +309,37 @@ const indexes = async (table, options) => {
271
309
  const drop = async (table, options) => {
272
310
  assertTable(table);
273
311
  assertForce(options);
274
- return await query('DROP TABLE IF EXISTS ??', [table]);
312
+ const act = {
313
+ [MYSQL]: [query, [`DROP TABLE IF EXISTS ${doublePlaceholder}`, [table]]],
314
+ [POSTGRESQL]: [execute, [`DROP TABLE IF EXISTS ${table}`]],
315
+ }[provider]
316
+ return await act[0](...act[1]);
275
317
  };
276
318
 
277
319
  const queryAll = (table, options) =>
278
320
  query(`${assembleQuery(table, options)}${assembleTail(options)}`);
279
321
 
280
322
  const queryByKeyValue = async (table, key, value, options) => {
281
- options = options || {};
282
323
  const sql = assembleQuery(table, options)
283
324
  + assembleKeyValue(key, value)
284
325
  + assembleTail(options);
285
326
  const resp = await query(sql, [value]);
286
- return options.unique ? (resp && resp.length ? resp[0] : null) : resp;
327
+ return options?.unique ? (resp && resp.length ? resp[0] : null) : resp;
287
328
  };
288
329
 
289
- const queryById = async (table, id, options) => {
290
- options = options || {};
291
- options.unique = !Array.isArray(id);
292
- return await queryByKeyValue(table, defaultKey(options), id, options);
293
- };
330
+ const queryById = async (table, id, options) => await queryByKeyValue(
331
+ table, defaultKey(options), id,
332
+ { ...options || {}, unique: !Array.isArray(id) }
333
+ );
294
334
 
295
335
  const insert = async (table, fields, options) => {
296
- options = options || {};
297
336
  let [isArray, key, ids, error, result]
298
337
  = [Array.isArray(fields), defaultKey(options), [], [], []];
299
- const subfix = provider === POSTGRESQL ? (options.skipEcho ? (
300
- options.key ? ` RETURNING ${key}` : ''
301
- ) : ` RETURNING *`) : '';
338
+ const subfix = provider === POSTGRESQL ? (options?.skipEcho ? (
339
+ options?.key ? ` ${RETURNING} ${key}` : ''
340
+ ) : RETURNING_ALL) : '';
302
341
  for (let item of assembleInsert(
303
- table, fields, { ...options, asArray: true, subfix })
342
+ table, fields, { ...options || {}, asArray: true, subfix })
304
343
  ) {
305
344
  try {
306
345
  const resp = await execute(item.sql, item.values);
@@ -314,7 +353,7 @@ const insert = async (table, fields, options) => {
314
353
  ids.push(resp.insertId);
315
354
  } catch (err) { error.push(err); }
316
355
  }
317
- if (!options.skipEcho && ids.length) {
356
+ if (!options?.skipEcho && ids.length) {
318
357
  switch (provider) {
319
358
  case MYSQL:
320
359
  result = await queryById(table, ids, options);
@@ -345,13 +384,18 @@ const countByKeyValue = async (table, key, value) => {
345
384
  };
346
385
 
347
386
  const updateByKeyValue = async (table, key, value, fields, options) => {
348
- options = options || {};
349
387
  assertTable(table);
350
- let { sql, values } = assembleUpdate(table, fields);
351
- sql += assembleKeyValue(key, value) + assembleTail(options);
388
+ const dfKey = defaultKey(options);
389
+ const subfix = assembleKeyValue(key, value, {
390
+ placeholderIndex: Object.keys(fields).length + 1,
391
+ }) + (provider === POSTGRESQL ? (options?.skipEcho ? (
392
+ options?.key ? ` ${RETURNING} ${dfKey}` : ''
393
+ ) : RETURNING_ALL) : '');
394
+ let { sql, values } = assembleUpdate(table, fields, { subfix });
395
+ sql += assembleTail(options);
352
396
  const resp = await query(sql, [...values, value]);
353
- return options.skipEcho
354
- ? resp : await queryByKeyValue(table, key, value, options);
397
+ return !options?.skipEcho && provider === MYSQL
398
+ ? await queryByKeyValue(table, key, value, options) : resp;
355
399
  };
356
400
 
357
401
  const updateById = async (table, id, fields, options) => {
@@ -365,11 +409,13 @@ const deleteByKeyValue = async (table, key, value, options) => {
365
409
  const sql = assembleDelete(table)
366
410
  + assembleKeyValue(key, value)
367
411
  + assembleTail(options);
368
- return await query(sql, [value]);
412
+ return await {
413
+ [MYSQL]: query, [POSTGRESQL]: execByQuery,
414
+ }[provider](sql, [value]);
369
415
  };
370
416
 
371
- const deleteById = (table, id, options) =>
372
- deleteByKeyValue(table, defaultKey(options), id);
417
+ const deleteById = async (table, id, options) =>
418
+ await deleteByKeyValue(table, defaultKey(options), id);
373
419
 
374
420
  const deleteAll = async (table, options) => {
375
421
  assertForce(options);
package/lib/manifest.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  const manifest = {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "1995.2.11",
4
+ "version": "1995.2.13",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "1995.2.11",
4
+ "version": "1995.2.13",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",