imodel-pg 0.3.1 → 0.3.2
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/index.d.mts +1 -1
- package/index.mjs +220 -7
- package/package.json +1 -1
package/index.d.mts
CHANGED
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* imodel v0.3.
|
|
2
|
+
* imodel v0.3.2
|
|
3
3
|
* (c) 2019-2025 undefined
|
|
4
4
|
* @license undefined
|
|
5
5
|
*/
|
|
@@ -934,6 +934,7 @@ WHERE ${Sql`AND`.glue(pKeys.map(f => Sql`${main.field(f)}=${tmp.field(f)}`), get
|
|
|
934
934
|
/** @import { Environment, IConnection } from 'imodel' */
|
|
935
935
|
/** @import { PgEnvTrans } from '../index.mjs' */
|
|
936
936
|
/** @import { DBIndex, DBTable, DBField } from 'imodel' */
|
|
937
|
+
const tableType = 'BASE TABLE';
|
|
937
938
|
/**
|
|
938
939
|
*
|
|
939
940
|
* @param {Environment<PgEnvTrans>} env
|
|
@@ -1065,7 +1066,7 @@ async function loadDefault(env, query, fields) {
|
|
|
1065
1066
|
return fields;
|
|
1066
1067
|
}
|
|
1067
1068
|
const sql = Sql`
|
|
1068
|
-
SELECT ${Sql`,`.glue(data.map((v, k) => Sql`${v} as ${Sql(`a${k}`)}`))}
|
|
1069
|
+
SELECT ${Sql`,`.glue(data.map((v, k) => Sql`${v.default} as ${Sql(`a${k}`)}`))}
|
|
1069
1070
|
`;
|
|
1070
1071
|
const values = await query(env, sql);
|
|
1071
1072
|
for (const [index, field] of data.entries()) {
|
|
@@ -1143,7 +1144,7 @@ async function loadBaseTables(env, schema, query, tables) {
|
|
|
1143
1144
|
WHERE
|
|
1144
1145
|
table_schema = ${schema}
|
|
1145
1146
|
AND
|
|
1146
|
-
table_type =
|
|
1147
|
+
table_type = ${tableType}
|
|
1147
1148
|
AND
|
|
1148
1149
|
table_name IN (${Sql`,`.glue(tables.map(t => Sql`${t}`))})`;
|
|
1149
1150
|
const rows = await query(env, sql);
|
|
@@ -1287,6 +1288,216 @@ async function createNewTable(env, query, {
|
|
|
1287
1288
|
await createIndex(env, query, table, index);
|
|
1288
1289
|
}
|
|
1289
1290
|
}
|
|
1291
|
+
/**
|
|
1292
|
+
*
|
|
1293
|
+
* @param {{fields: string[]; includes: string[]?; unique?: boolean; name: string[]}} oldIndex
|
|
1294
|
+
* @param {DBIndex} [newIndex]
|
|
1295
|
+
* @returns
|
|
1296
|
+
*/
|
|
1297
|
+
function indexIsEq({
|
|
1298
|
+
includes,
|
|
1299
|
+
name,
|
|
1300
|
+
unique
|
|
1301
|
+
}, newIndex) {
|
|
1302
|
+
if (!newIndex) {
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
if (name?.length !== 1) {
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
if (!includes) {
|
|
1309
|
+
return false;
|
|
1310
|
+
}
|
|
1311
|
+
if (Boolean(newIndex.unique) !== Boolean(unique)) {
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
const oldIncludes = new Set(includes);
|
|
1315
|
+
const newIncludes = new Set(newIndex.includes);
|
|
1316
|
+
if (oldIncludes.size !== newIncludes.size) {
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
for (const k of oldIncludes) {
|
|
1320
|
+
if (!newIncludes.has(k)) {
|
|
1321
|
+
return false;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
*
|
|
1328
|
+
* @param {DBIndex[]} [indexes]
|
|
1329
|
+
* @returns
|
|
1330
|
+
*/
|
|
1331
|
+
function buildOldIndexes(indexes) {
|
|
1332
|
+
/** @type {Record<string, {fields: string[]; includes: string[]?; unique?: boolean; name: string[]}>} */
|
|
1333
|
+
const indexMap = {};
|
|
1334
|
+
if (!indexes) {
|
|
1335
|
+
return indexMap;
|
|
1336
|
+
}
|
|
1337
|
+
for (const {
|
|
1338
|
+
fields,
|
|
1339
|
+
includes,
|
|
1340
|
+
unique,
|
|
1341
|
+
name
|
|
1342
|
+
} of indexes) {
|
|
1343
|
+
const keys = [];
|
|
1344
|
+
for (const f of fields) {
|
|
1345
|
+
if (keys.includes(f)) {
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
keys.push(f);
|
|
1349
|
+
}
|
|
1350
|
+
const key = keys.join('\n');
|
|
1351
|
+
const index = indexMap[key];
|
|
1352
|
+
if (!index) {
|
|
1353
|
+
indexMap[key] = {
|
|
1354
|
+
fields: keys,
|
|
1355
|
+
includes: includes || [],
|
|
1356
|
+
unique,
|
|
1357
|
+
name: name ? [name] : []
|
|
1358
|
+
};
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
index.includes = null;
|
|
1362
|
+
if (name) {
|
|
1363
|
+
index.name.push(name);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return indexMap;
|
|
1367
|
+
}
|
|
1368
|
+
function toTypeHad(type) {
|
|
1369
|
+
switch (type) {
|
|
1370
|
+
case 'ipv4':
|
|
1371
|
+
case 'ipv6':
|
|
1372
|
+
return 'ip';
|
|
1373
|
+
case 'ipnetv4':
|
|
1374
|
+
case 'ipnetv6':
|
|
1375
|
+
return 'ipnet';
|
|
1376
|
+
case 'object':
|
|
1377
|
+
return 'json';
|
|
1378
|
+
}
|
|
1379
|
+
return type;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
*
|
|
1383
|
+
* @param {Environment<PgEnvTrans>} env
|
|
1384
|
+
* @param {<T extends pg.QueryResultRow>(env: Environment<PgEnvTrans>, sql: Sql) => Promise<T[]>} query
|
|
1385
|
+
* @param {DBTable} newTable
|
|
1386
|
+
* @param {DBTable} oldTable
|
|
1387
|
+
*/
|
|
1388
|
+
async function changeTable(env, query, newTable, oldTable) {
|
|
1389
|
+
const {
|
|
1390
|
+
table
|
|
1391
|
+
} = newTable;
|
|
1392
|
+
const newIndexes = new Map(Object.entries(buildNewIndexes(newTable.indexes)));
|
|
1393
|
+
const oldIndexes = buildOldIndexes(oldTable.indexes);
|
|
1394
|
+
for (const [key, index] of Object.entries(oldIndexes)) {
|
|
1395
|
+
if (indexIsEq(index, newIndexes.get(key))) {
|
|
1396
|
+
newIndexes.delete(key);
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
for (const n of index.name || []) {
|
|
1400
|
+
const sql = Sql`DROP INDEX ${Sql.Id(n, 'index')}`;
|
|
1401
|
+
await query(env, sql);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
// 改名
|
|
1405
|
+
if (table !== oldTable.table) {
|
|
1406
|
+
const sql = Sql`
|
|
1407
|
+
ALTER TABLE ${Sql.Table(oldTable.table)}
|
|
1408
|
+
RENAME TO ${Sql.Table(table)}
|
|
1409
|
+
`;
|
|
1410
|
+
await query(env, sql);
|
|
1411
|
+
}
|
|
1412
|
+
const primary = Object.entries(newTable.fields).filter(([, v]) => v.primary).sort(([, {
|
|
1413
|
+
primary: a = 0
|
|
1414
|
+
}], [, {
|
|
1415
|
+
primary: b = 0
|
|
1416
|
+
}]) => a - b).map(([v]) => v);
|
|
1417
|
+
const oldPrimary = Object.entries(oldTable.fields).filter(([, v]) => v.primary).sort(([, {
|
|
1418
|
+
primary: a = 0
|
|
1419
|
+
}], [, {
|
|
1420
|
+
primary: b = 0
|
|
1421
|
+
}]) => a - b).map(([v]) => v);
|
|
1422
|
+
const prEq = oldPrimary.length === primary.length && primary.join('\n') === oldPrimary.join('\n');
|
|
1423
|
+
if (!prEq && oldPrimary.length) {
|
|
1424
|
+
let name = oldTable.primary;
|
|
1425
|
+
if (!name) {
|
|
1426
|
+
const sql = Sql`
|
|
1427
|
+
SELECT constraint_name
|
|
1428
|
+
FROM information_schema.table_constraints
|
|
1429
|
+
WHERE table_name = ${table} AND constraint_type = 'PRIMARY KEY'
|
|
1430
|
+
`;
|
|
1431
|
+
name = (await query(env, sql).then(v => v[0]?.constraint_name)) || '';
|
|
1432
|
+
}
|
|
1433
|
+
// @ts-ignore
|
|
1434
|
+
// eslint-disable-next-line max-len
|
|
1435
|
+
const sql = Sql`ALTER TABLE ${Sql.Table(table)} DROP CONSTRAINT ${Sql.Id(name)}`;
|
|
1436
|
+
await query(env, sql);
|
|
1437
|
+
}
|
|
1438
|
+
const oldFields = new Map(Object.entries(oldTable.fields));
|
|
1439
|
+
// 列处理
|
|
1440
|
+
/** @type {Sql[]} */
|
|
1441
|
+
const COLUMNs = [];
|
|
1442
|
+
for (const [fieldName, field] of Object.entries(newTable.fields)) {
|
|
1443
|
+
const old = oldFields.get(fieldName);
|
|
1444
|
+
oldFields.delete(fieldName);
|
|
1445
|
+
if (!old) {
|
|
1446
|
+
COLUMNs.push(add(fieldName, field.type, field, env));
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
const type = toTypeHad(field.type);
|
|
1450
|
+
const {
|
|
1451
|
+
size,
|
|
1452
|
+
scale,
|
|
1453
|
+
array,
|
|
1454
|
+
default: defaultValue = null,
|
|
1455
|
+
nullable
|
|
1456
|
+
} = field;
|
|
1457
|
+
if (type !== old.type || Number(array) !== Number(old.array)) ;
|
|
1458
|
+
const COLUMN = Sql`ALTER COLUMN ${Sql.Field(fieldName)}`;
|
|
1459
|
+
const newType = getType(type, {
|
|
1460
|
+
scale,
|
|
1461
|
+
size
|
|
1462
|
+
}, array);
|
|
1463
|
+
const oldType = getType(old.type, old, old.array);
|
|
1464
|
+
if (newType !== oldType) {
|
|
1465
|
+
COLUMNs.push(Sql`${COLUMN} TYPE ${Sql(newType)}`);
|
|
1466
|
+
// TODO: USING "description"::int2
|
|
1467
|
+
}
|
|
1468
|
+
if (Boolean(nullable) !== Boolean(old.nullable)) {
|
|
1469
|
+
COLUMNs.push(nullable ? Sql`${COLUMN} DROP NOT NULL` : Sql`${COLUMN} SET NOT NULL`);
|
|
1470
|
+
}
|
|
1471
|
+
if (defaultValue !== (old.default ?? null)) {
|
|
1472
|
+
COLUMNs.push(defaultValue !== null ? Sql`${COLUMN} SET DEFAULT ${defaultValue}` : Sql`${COLUMN} DROP DEFAULT`);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
{
|
|
1476
|
+
for (const [fieldName, {
|
|
1477
|
+
nullable
|
|
1478
|
+
}] of oldFields) {
|
|
1479
|
+
if (nullable) {
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
COLUMNs.push(Sql`ALTER COLUMN ${Sql.Field(fieldName)} DROP NOT NULL`);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
if (COLUMNs.length) {
|
|
1486
|
+
const sql = Sql`ALTER TABLE ${Sql.Table(table)} ${Sql`,`.glue(COLUMNs)}`;
|
|
1487
|
+
await query(env, sql);
|
|
1488
|
+
}
|
|
1489
|
+
if (!prEq && primary.length) {
|
|
1490
|
+
const sql = Sql`
|
|
1491
|
+
ALTER TABLE ${Sql.Table(table)}
|
|
1492
|
+
ADD PRIMARY KEY ${Sql`,`.glue(primary.map(f => Sql.Field(f)))}
|
|
1493
|
+
`;
|
|
1494
|
+
await query(env, sql);
|
|
1495
|
+
}
|
|
1496
|
+
// 重建索引
|
|
1497
|
+
for (const index of newIndexes.values()) {
|
|
1498
|
+
await createIndex(env, query, table, index);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1290
1501
|
/**
|
|
1291
1502
|
*
|
|
1292
1503
|
* @param {Environment<PgEnvTrans>} env
|
|
@@ -1299,12 +1510,14 @@ async function syncTables(env, schema, query, tables, del) {
|
|
|
1299
1510
|
const tableNames = tables.map(v => v.table);
|
|
1300
1511
|
const dbTables = await loadTables(env, schema, query, tableNames);
|
|
1301
1512
|
const tableMap = new Map(dbTables.map(v => [v.table, v]));
|
|
1302
|
-
|
|
1513
|
+
for (const table of tables) {
|
|
1303
1514
|
const old = tableMap.get(table.table);
|
|
1304
|
-
if (old)
|
|
1305
|
-
|
|
1515
|
+
if (old) {
|
|
1516
|
+
await changeTable(env, query, table, old);
|
|
1517
|
+
} else {
|
|
1518
|
+
await createNewTable(env, query, table);
|
|
1306
1519
|
}
|
|
1307
|
-
}
|
|
1520
|
+
}
|
|
1308
1521
|
}
|
|
1309
1522
|
|
|
1310
1523
|
/** @import { Environment, IConnection } from 'imodel' */
|