neopg 2.2.6 → 2.2.8
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/ModelChain.js +10 -2
- package/lib/SchemaSync.js +87 -10
- package/package.json +1 -1
- package/test/test-db.js +26 -3
package/lib/ModelChain.js
CHANGED
|
@@ -198,8 +198,16 @@ class ModelChain {
|
|
|
198
198
|
const v = arg1[k]
|
|
199
199
|
if (v === undefined) continue
|
|
200
200
|
if (v === null) this._conditions.push(this.sql`${this.sql(k)} IS NULL`)
|
|
201
|
-
else if (Array.isArray(v))
|
|
202
|
-
|
|
201
|
+
else if (Array.isArray(v)) {
|
|
202
|
+
//this._conditions.push(this.sql`${this.sql(k)} IN ${this.sql(v)}`)
|
|
203
|
+
if (v.length === 0) {
|
|
204
|
+
// 性能优化:空数组直接在应用层生成 FALSE,不触发数据库复杂的数组运算
|
|
205
|
+
this._conditions.push(this.sql`FALSE`)
|
|
206
|
+
} else {
|
|
207
|
+
// 在 postgres.js 中,${v} 会被转换为 Postgres 数组变量 $1
|
|
208
|
+
this._conditions.push(this.sql`${this.sql(k)} = ANY(${v})`)
|
|
209
|
+
}
|
|
210
|
+
} else this._conditions.push(this.sql`${this.sql(k)} = ${v}`)
|
|
203
211
|
}
|
|
204
212
|
return this
|
|
205
213
|
}
|
package/lib/SchemaSync.js
CHANGED
|
@@ -102,9 +102,9 @@ class SchemaSync {
|
|
|
102
102
|
for (const c of cols) inf[c.column_name] = c;
|
|
103
103
|
|
|
104
104
|
await this.syncColumn(sql, def, inf, curTableName, debug, force, dropNotExistCol);
|
|
105
|
+
await this.autoRemoveIndex(sql, def, schema, tableName, debug);
|
|
105
106
|
await this.syncIndex(sql, def, schema, curTableName, debug);
|
|
106
107
|
await this.syncUnique(sql, def, schema, curTableName, debug);
|
|
107
|
-
await this.autoRemoveIndex(sql, def, schema, tableName, debug);
|
|
108
108
|
await this.syncReferences(sql, def, schema, curTableName, schemaOid, debug, ctx, options);
|
|
109
109
|
|
|
110
110
|
if (debug) console.log(` - 表结构同步完成 (${tableName}) - `);
|
|
@@ -290,6 +290,33 @@ class SchemaSync {
|
|
|
290
290
|
const indices = def.index || [];
|
|
291
291
|
if (!Array.isArray(indices)) return;
|
|
292
292
|
|
|
293
|
+
let gen_real_index = indname => {
|
|
294
|
+
let real_indexname
|
|
295
|
+
if (Array.isArray(indname)) {
|
|
296
|
+
real_indexname = indname.sort((a,b) => a.localeCompare(b)).join('_');
|
|
297
|
+
} else if (typeof indname === 'string') {
|
|
298
|
+
if (indname.indexOf(',') >= 0) {
|
|
299
|
+
real_indexname = indname.split(',').map(s=>s.trim()).filter(s=>s)
|
|
300
|
+
.sort((a,b)=>a.localeCompare(b))
|
|
301
|
+
.join('_');
|
|
302
|
+
} else {
|
|
303
|
+
real_indexname = indname.trim();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return real_indexname;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let unique_indexs = (def.unique || []).map(a => gen_real_index(a));
|
|
311
|
+
|
|
312
|
+
let check_in_unique = (indname) => {
|
|
313
|
+
for (let a of unique_indexs) {
|
|
314
|
+
if (a === gen_real_index(indname)) return true;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
293
320
|
for (const indname of indices) {
|
|
294
321
|
const removeIndex = def.removeIndex || [];
|
|
295
322
|
if (removeIndex.includes(indname)) continue;
|
|
@@ -299,7 +326,21 @@ class SchemaSync {
|
|
|
299
326
|
continue;
|
|
300
327
|
}
|
|
301
328
|
|
|
302
|
-
|
|
329
|
+
if (!Array.isArray(indname) && typeof indname !== 'string') {
|
|
330
|
+
debug && console.error(`Index ${indname} 不是字符串或数组,跳过。`);
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (check_in_unique(indname)) {
|
|
335
|
+
debug && console.error(`Index ${indname} 是唯一索引,不会创建index索引,跳过。`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let idxCols = Array.isArray(indname)
|
|
340
|
+
? indname.sort((a,b) => a.localeCompare(b))
|
|
341
|
+
: indname.split(',').map(s=>s.trim()).filter(s=>s)
|
|
342
|
+
.sort((a,b) => a.localeCompare(b));
|
|
343
|
+
|
|
303
344
|
const idxNamePart = idxCols.join('_');
|
|
304
345
|
const targetIdxName = `${def.tableName}_${idxNamePart}_idx`;
|
|
305
346
|
|
|
@@ -339,7 +380,14 @@ class SchemaSync {
|
|
|
339
380
|
for (const indname of uniques) {
|
|
340
381
|
if (!this._checkColumnsExist(indname, def)) continue;
|
|
341
382
|
|
|
342
|
-
|
|
383
|
+
if (!Array.isArray(indname) && typeof indname !== 'string') {
|
|
384
|
+
debug && console.error(`Index ${indname} 不是字符串或数组,跳过。`);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let idxCols = Array.isArray(indname)
|
|
389
|
+
? indname.sort((a,b)=>a.localeCompare(b))
|
|
390
|
+
: indname.split(',').map(s=>s.trim()).filter(s=>s).sort((a,b)=>a.localeCompare(b));
|
|
343
391
|
|
|
344
392
|
// 监测是否等于主键
|
|
345
393
|
if (pkSet.size > 0 && idxCols.length === pkSet.size) {
|
|
@@ -368,7 +416,7 @@ class SchemaSync {
|
|
|
368
416
|
|
|
369
417
|
static async autoRemoveIndex(sql, def, schema, tableName, debug) {
|
|
370
418
|
const allIdx = await sql`
|
|
371
|
-
SELECT
|
|
419
|
+
SELECT * FROM pg_indexes
|
|
372
420
|
WHERE tablename = ${tableName}
|
|
373
421
|
AND schemaname = ${schema}
|
|
374
422
|
AND indexname != ${tableName + '_pkey'}
|
|
@@ -377,22 +425,51 @@ class SchemaSync {
|
|
|
377
425
|
if (allIdx.length === 0) return;
|
|
378
426
|
|
|
379
427
|
const currentIdxNames = allIdx.map(i => i.indexname);
|
|
428
|
+
let indexTable = Object.create(null);
|
|
429
|
+
for (let a of allIdx) {
|
|
430
|
+
indexTable[a.indexname] = a;
|
|
431
|
+
}
|
|
380
432
|
|
|
381
433
|
const indices = def.index || [];
|
|
382
434
|
const uniques = def.unique || [];
|
|
383
|
-
|
|
384
|
-
const keepSet = new Set();
|
|
385
|
-
const makeName = (n) => `${tableName}_${Array.isArray(n) ? n.map(x=>x.trim()).join('_') : n.split(',').map(x=>x.trim()).filter(x=>x).join('_')}_idx`;
|
|
386
435
|
|
|
387
|
-
|
|
388
|
-
|
|
436
|
+
const makeName = (n) => `${tableName}_${Array.isArray(n)
|
|
437
|
+
? n.map(x=>x.trim()).sort((a,b) => a.localeCompare(b)).join('_')
|
|
438
|
+
: n.split(',').map(x=>x.trim()).filter(x=>x).sort((a,b) => a.localeCompare(b)).join('_')}_idx`;
|
|
439
|
+
|
|
440
|
+
let allIndex = Object.create(null);
|
|
441
|
+
for (let k of indices) {
|
|
442
|
+
allIndex[makeName(k)] = k;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let allUnique = Object.create(null);
|
|
446
|
+
for (let k of uniques) {
|
|
447
|
+
allUnique[makeName(k)] = k;
|
|
448
|
+
}
|
|
389
449
|
|
|
390
450
|
for (const idxName of currentIdxNames) {
|
|
391
|
-
if (!
|
|
451
|
+
if (!allIndex[idxName] && !allUnique[idxName]) {
|
|
392
452
|
if (debug) console.log('Auto removing index:', idxName);
|
|
393
453
|
await sql.unsafe(`DROP INDEX ${schema}.${idxName}`);
|
|
394
454
|
}
|
|
395
455
|
}
|
|
456
|
+
|
|
457
|
+
for (let k in allIndex) {
|
|
458
|
+
if (!indexTable[k]) continue;
|
|
459
|
+
|
|
460
|
+
if (indexTable[k].indexdef.toLowerCase().indexOf('create index') !== 0) {
|
|
461
|
+
debug && console.log('Auto remove index with inconsistent type:', k);
|
|
462
|
+
await sql.unsafe(`DROP INDEX ${schema}.${k};`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
for (let k in allUnique) {
|
|
467
|
+
if (!indexTable[k]) continue;
|
|
468
|
+
if (indexTable[k].indexdef.toLowerCase().indexOf('create unique') !== 0) {
|
|
469
|
+
debug && console.log('Auto remove index with inconsistent type:', k);
|
|
470
|
+
await sql.unsafe(`DROP INDEX ${schema}.${k};`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
396
473
|
}
|
|
397
474
|
|
|
398
475
|
// --- 外键同步 ---
|
package/package.json
CHANGED
package/test/test-db.js
CHANGED
|
@@ -130,6 +130,11 @@ const User = {
|
|
|
130
130
|
type: dataTypes.POINT,
|
|
131
131
|
default: '(0,0)',
|
|
132
132
|
indexType: 'GiST'
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
role: {
|
|
136
|
+
type: dataTypes.STRING(16),
|
|
137
|
+
default: 'user'
|
|
133
138
|
}
|
|
134
139
|
},
|
|
135
140
|
|
|
@@ -213,6 +218,7 @@ db.add(User)
|
|
|
213
218
|
console.log('test has', db.has('User'))
|
|
214
219
|
|
|
215
220
|
await db.model('ShopOrder').where('1=1').delete()
|
|
221
|
+
|
|
216
222
|
await db.model('ShopOrder').insert([
|
|
217
223
|
{
|
|
218
224
|
name: 'topbit',
|
|
@@ -222,6 +228,10 @@ db.add(User)
|
|
|
222
228
|
{
|
|
223
229
|
name: 'neopg',
|
|
224
230
|
order_no: Math.random().toString(16)
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'hydra-thread',
|
|
234
|
+
order_no: Math.random().toString(16)
|
|
225
235
|
}
|
|
226
236
|
])
|
|
227
237
|
|
|
@@ -240,20 +250,23 @@ db.add(User)
|
|
|
240
250
|
username: 'Neo',
|
|
241
251
|
email: '123@w.com',
|
|
242
252
|
sex: 1,
|
|
243
|
-
level: Math.floor((Math.random() * 105))
|
|
253
|
+
level: Math.floor((Math.random() * 105)),
|
|
254
|
+
role: 'user'
|
|
244
255
|
},
|
|
245
256
|
{
|
|
246
257
|
username: 'PG',
|
|
247
258
|
email: '1234@w.com',
|
|
248
259
|
sex: 2,
|
|
249
|
-
level: Math.floor((Math.random() * 100))
|
|
260
|
+
level: Math.floor((Math.random() * 100)),
|
|
261
|
+
role: 'db'
|
|
250
262
|
},
|
|
251
263
|
|
|
252
264
|
{
|
|
253
265
|
username: 'NPG',
|
|
254
266
|
email: '1235@w.com',
|
|
255
267
|
sex: 3,
|
|
256
|
-
level: 3
|
|
268
|
+
level: 3,
|
|
269
|
+
role: 'test'
|
|
257
270
|
}
|
|
258
271
|
])
|
|
259
272
|
)
|
|
@@ -294,6 +307,16 @@ db.add(User)
|
|
|
294
307
|
await tx.model('User').group('level').select(tx.sql`level, MAX(username) as username`).findAndCount()
|
|
295
308
|
)
|
|
296
309
|
|
|
310
|
+
console.log(
|
|
311
|
+
'test for any (role in ...)',
|
|
312
|
+
await tx.model('User').where({role: ['test', 'db']}).find()
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
console.log(
|
|
316
|
+
'test for any (empty)',
|
|
317
|
+
await tx.model('User').where({role: []}).find()
|
|
318
|
+
)
|
|
319
|
+
|
|
297
320
|
console.log(
|
|
298
321
|
'test avg',
|
|
299
322
|
await tx.model('User').avg('level')
|