imean-cassandra-orm 3.1.0 → 3.2.0
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/dist/mod.cjs +824 -383
- package/dist/mod.d.cts +28 -28
- package/dist/mod.d.ts +28 -28
- package/dist/mod.js +824 -383
- package/package.json +68 -68
package/dist/mod.cjs
CHANGED
|
@@ -655,6 +655,20 @@ function shouldWrite(op, conf) {
|
|
|
655
655
|
return Array.isArray(conf) && conf.includes(op);
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
+
// src/indexes.ts
|
|
659
|
+
function createIndex(keyspace, tableName, fieldName, options) {
|
|
660
|
+
const indexName = `${tableName}_${String(fieldName).toLowerCase()}_sai_idx`;
|
|
661
|
+
const config = options !== true ? options : {
|
|
662
|
+
case_sensitive: false,
|
|
663
|
+
normalize: true,
|
|
664
|
+
ascii: true
|
|
665
|
+
};
|
|
666
|
+
const optionsString = JSON.stringify(config).replace(/"/g, "'");
|
|
667
|
+
return `CREATE INDEX IF NOT EXISTS ${indexName} ON ${keyspace}.${tableName} (${String(
|
|
668
|
+
fieldName
|
|
669
|
+
)}) USING 'sai' WITH OPTIONS = ${optionsString}`;
|
|
670
|
+
}
|
|
671
|
+
|
|
658
672
|
// src/helper.ts
|
|
659
673
|
function dropTable(keyspace, tableName) {
|
|
660
674
|
return `DROP TABLE IF EXISTS ${keyspace}.${tableName};`;
|
|
@@ -1194,28 +1208,10 @@ function buildSelectByIndexParams(query, fieldConfigs) {
|
|
|
1194
1208
|
function createIndexes(schema) {
|
|
1195
1209
|
const indexQueries = [];
|
|
1196
1210
|
if (schema.indexes) {
|
|
1197
|
-
Object.entries(schema.indexes).forEach(([fieldName,
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (typeof indexConfig === "object" && Object.keys(indexConfig).length > 0) {
|
|
1202
|
-
const options = [];
|
|
1203
|
-
Object.entries(indexConfig).forEach(([key, value]) => {
|
|
1204
|
-
if (value !== void 0 && value !== null) {
|
|
1205
|
-
if (typeof value === "string") {
|
|
1206
|
-
options.push(`'${key}': '${value}'`);
|
|
1207
|
-
} else {
|
|
1208
|
-
options.push(`'${key}': ${value}`);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
});
|
|
1212
|
-
if (options.length > 0) {
|
|
1213
|
-
indexQuery += ` WITH OPTIONS = { ${options.join(", ")} }`;
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
indexQuery += ";";
|
|
1217
|
-
indexQueries.push(indexQuery);
|
|
1218
|
-
}
|
|
1211
|
+
Object.entries(schema.indexes).forEach(([fieldName, options]) => {
|
|
1212
|
+
indexQueries.push(
|
|
1213
|
+
createIndex(schema.keyspace, schema.tableName, fieldName, options)
|
|
1214
|
+
);
|
|
1219
1215
|
});
|
|
1220
1216
|
}
|
|
1221
1217
|
return indexQueries;
|
|
@@ -1224,69 +1220,54 @@ function syncIndexes(schema, existingIndexes) {
|
|
|
1224
1220
|
const operations = [];
|
|
1225
1221
|
const existingIndexMap = /* @__PURE__ */ new Map();
|
|
1226
1222
|
existingIndexes.forEach((index) => {
|
|
1227
|
-
existingIndexMap.set(index.
|
|
1228
|
-
|
|
1223
|
+
existingIndexMap.set(index.options.target, {
|
|
1224
|
+
case_sensitive: Boolean(index.options.case_sensitive),
|
|
1225
|
+
normalize: Boolean(index.options.normalize),
|
|
1226
|
+
ascii: Boolean(index.options.ascii),
|
|
1227
|
+
similarity_function: index.options.similarity_function ?? void 0
|
|
1229
1228
|
});
|
|
1230
1229
|
});
|
|
1231
1230
|
const newIndexMap = /* @__PURE__ */ new Map();
|
|
1232
1231
|
if (schema.indexes) {
|
|
1233
|
-
Object.entries(schema.indexes).forEach(([fieldName
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
if (value !== void 0 && value !== null) {
|
|
1241
|
-
if (typeof value === "string") {
|
|
1242
|
-
optionStrings.push(`'${key}': '${value}'`);
|
|
1243
|
-
} else {
|
|
1244
|
-
optionStrings.push(`'${key}': ${value}`);
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
});
|
|
1248
|
-
if (optionStrings.length > 0) {
|
|
1249
|
-
options = `{ ${optionStrings.join(", ")} }`;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
newIndexMap.set(indexName, { fieldName, config: indexConfig, options });
|
|
1253
|
-
}
|
|
1232
|
+
Object.entries(schema.indexes).forEach(([fieldName]) => {
|
|
1233
|
+
const options = typeof schema.indexes?.[fieldName] === "boolean" ? {
|
|
1234
|
+
case_sensitive: false,
|
|
1235
|
+
normalize: true,
|
|
1236
|
+
ascii: true
|
|
1237
|
+
} : schema.indexes?.[fieldName];
|
|
1238
|
+
newIndexMap.set(fieldName, options);
|
|
1254
1239
|
});
|
|
1255
1240
|
}
|
|
1256
|
-
existingIndexMap.forEach((
|
|
1241
|
+
existingIndexMap.forEach((_, indexName) => {
|
|
1257
1242
|
if (!newIndexMap.has(indexName)) {
|
|
1258
1243
|
operations.push(`DROP INDEX IF EXISTS ${schema.keyspace}.${indexName};`);
|
|
1259
1244
|
}
|
|
1260
1245
|
});
|
|
1261
|
-
newIndexMap.forEach((
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1246
|
+
newIndexMap.forEach((_, fieldName) => {
|
|
1247
|
+
const options = newIndexMap.get(fieldName);
|
|
1248
|
+
const existingOptions = existingIndexMap.get(fieldName);
|
|
1249
|
+
if (!existingOptions) {
|
|
1250
|
+
let createQuery = createIndex(
|
|
1251
|
+
schema.keyspace,
|
|
1252
|
+
schema.tableName,
|
|
1253
|
+
fieldName,
|
|
1254
|
+
options
|
|
1255
|
+
);
|
|
1269
1256
|
operations.push(createQuery);
|
|
1270
1257
|
} else {
|
|
1271
|
-
|
|
1272
|
-
const newOptions = indexInfo.options;
|
|
1273
|
-
const normalizeOptions = (opts) => {
|
|
1274
|
-
if (!opts) return "";
|
|
1275
|
-
let normalized = opts.replace(/^"(.*)"$/, "$1").trim();
|
|
1276
|
-
normalized = normalized.replace(/\s+/g, " ");
|
|
1277
|
-
return normalized;
|
|
1278
|
-
};
|
|
1279
|
-
const normalizedExisting = normalizeOptions(existingOptions);
|
|
1280
|
-
const normalizedNew = normalizeOptions(newOptions);
|
|
1281
|
-
if (normalizedExisting !== normalizedNew) {
|
|
1258
|
+
if (JSON.stringify(existingOptions) !== JSON.stringify(options)) {
|
|
1282
1259
|
operations.push(
|
|
1283
|
-
|
|
1260
|
+
dropIndex(
|
|
1261
|
+
schema.keyspace,
|
|
1262
|
+
`${schema.tableName}_${fieldName.toLowerCase()}_sai_idx`
|
|
1263
|
+
)
|
|
1264
|
+
);
|
|
1265
|
+
let createQuery = createIndex(
|
|
1266
|
+
schema.keyspace,
|
|
1267
|
+
schema.tableName,
|
|
1268
|
+
fieldName,
|
|
1269
|
+
options
|
|
1284
1270
|
);
|
|
1285
|
-
let createQuery = `CREATE INDEX ${indexName} ON ${schema.keyspace}.${schema.tableName} (${indexInfo.fieldName}) USING 'sai'`;
|
|
1286
|
-
if (indexInfo.options) {
|
|
1287
|
-
createQuery += ` WITH OPTIONS = ${indexInfo.options}`;
|
|
1288
|
-
}
|
|
1289
|
-
createQuery += ";";
|
|
1290
1271
|
operations.push(createQuery);
|
|
1291
1272
|
}
|
|
1292
1273
|
}
|
|
@@ -1364,6 +1345,691 @@ var createTableSchema = (config) => {
|
|
|
1364
1345
|
return new TableSchema(config);
|
|
1365
1346
|
};
|
|
1366
1347
|
|
|
1348
|
+
// src/model-synchronizer.ts
|
|
1349
|
+
var ModelSynchronizer = class {
|
|
1350
|
+
constructor(model) {
|
|
1351
|
+
this.model = model;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* 检测表结构变更
|
|
1355
|
+
*/
|
|
1356
|
+
async detectChanges() {
|
|
1357
|
+
const existingTableInfo = await this.getExistingTableInfo();
|
|
1358
|
+
const schemaTableInfo = await this.convertSchemaToTableInfo();
|
|
1359
|
+
const addedFields = [];
|
|
1360
|
+
const modifiedFields = [];
|
|
1361
|
+
const deletedFields = [];
|
|
1362
|
+
const addedIndexes = [];
|
|
1363
|
+
const deletedIndexes = [];
|
|
1364
|
+
const rebuiltIndexes = [];
|
|
1365
|
+
for (const schemaColumn of schemaTableInfo.columns) {
|
|
1366
|
+
const existingColumn = existingTableInfo.columns.find(
|
|
1367
|
+
(c) => c.column_name.toLowerCase() === schemaColumn.column_name.toLowerCase()
|
|
1368
|
+
);
|
|
1369
|
+
if (!existingColumn) {
|
|
1370
|
+
addedFields.push({
|
|
1371
|
+
name: schemaColumn.column_name,
|
|
1372
|
+
type: schemaColumn.type,
|
|
1373
|
+
kind: schemaColumn.kind
|
|
1374
|
+
});
|
|
1375
|
+
} else {
|
|
1376
|
+
if (schemaColumn.type.toLowerCase() !== existingColumn.type.toLowerCase()) {
|
|
1377
|
+
modifiedFields.push({
|
|
1378
|
+
name: schemaColumn.column_name,
|
|
1379
|
+
oldType: existingColumn.type,
|
|
1380
|
+
newType: schemaColumn.type
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
if (schemaColumn.kind !== existingColumn.kind) {
|
|
1384
|
+
if (schemaColumn.column_name.toLowerCase() === existingColumn.column_name.toLowerCase()) {
|
|
1385
|
+
const kindChangedField = {
|
|
1386
|
+
name: existingColumn.column_name,
|
|
1387
|
+
type: existingColumn.type,
|
|
1388
|
+
kind: existingColumn.kind
|
|
1389
|
+
};
|
|
1390
|
+
if (!deletedFields.some(
|
|
1391
|
+
(f) => f.name.toLowerCase() === kindChangedField.name.toLowerCase()
|
|
1392
|
+
)) {
|
|
1393
|
+
deletedFields.push(kindChangedField);
|
|
1394
|
+
}
|
|
1395
|
+
const kindChangedFieldNew = {
|
|
1396
|
+
name: schemaColumn.column_name,
|
|
1397
|
+
type: schemaColumn.type,
|
|
1398
|
+
kind: schemaColumn.kind
|
|
1399
|
+
};
|
|
1400
|
+
if (!addedFields.some(
|
|
1401
|
+
(f) => f.name.toLowerCase() === kindChangedFieldNew.name.toLowerCase()
|
|
1402
|
+
)) {
|
|
1403
|
+
addedFields.push(kindChangedFieldNew);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
for (const existingColumn of existingTableInfo.columns) {
|
|
1410
|
+
const schemaColumn = schemaTableInfo.columns.find(
|
|
1411
|
+
(c) => c.column_name.toLowerCase() === existingColumn.column_name.toLowerCase()
|
|
1412
|
+
);
|
|
1413
|
+
if (!schemaColumn) {
|
|
1414
|
+
if (!deletedFields.some(
|
|
1415
|
+
(f) => f.name.toLowerCase() === existingColumn.column_name.toLowerCase()
|
|
1416
|
+
)) {
|
|
1417
|
+
deletedFields.push({
|
|
1418
|
+
name: existingColumn.column_name,
|
|
1419
|
+
type: existingColumn.type,
|
|
1420
|
+
kind: existingColumn.kind
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
for (const schemaIndex of schemaTableInfo.indexes) {
|
|
1426
|
+
const existingIndex = existingTableInfo.indexes.find(
|
|
1427
|
+
(i) => i.index_name.toLowerCase() === schemaIndex.index_name.toLowerCase()
|
|
1428
|
+
);
|
|
1429
|
+
if (!existingIndex) {
|
|
1430
|
+
addedIndexes.push({
|
|
1431
|
+
name: schemaIndex.index_name,
|
|
1432
|
+
options: schemaIndex.options
|
|
1433
|
+
});
|
|
1434
|
+
} else {
|
|
1435
|
+
if (!this.areIndexOptionsEqual(existingIndex.options, schemaIndex.options)) {
|
|
1436
|
+
rebuiltIndexes.push({
|
|
1437
|
+
name: schemaIndex.index_name,
|
|
1438
|
+
oldOptions: existingIndex.options,
|
|
1439
|
+
newOptions: schemaIndex.options
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
for (const existingIndex of existingTableInfo.indexes) {
|
|
1445
|
+
const schemaIndex = schemaTableInfo.indexes.find(
|
|
1446
|
+
(i) => i.index_name.toLowerCase() === existingIndex.index_name.toLowerCase()
|
|
1447
|
+
);
|
|
1448
|
+
if (!schemaIndex) {
|
|
1449
|
+
deletedIndexes.push({
|
|
1450
|
+
name: existingIndex.index_name,
|
|
1451
|
+
options: existingIndex.options
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
const hasChanges = addedFields.length > 0 || modifiedFields.length > 0 || deletedFields.length > 0 || addedIndexes.length > 0 || deletedIndexes.length > 0 || rebuiltIndexes.length > 0;
|
|
1456
|
+
return {
|
|
1457
|
+
addedFields,
|
|
1458
|
+
modifiedFields,
|
|
1459
|
+
deletedFields,
|
|
1460
|
+
addedIndexes,
|
|
1461
|
+
deletedIndexes,
|
|
1462
|
+
rebuiltIndexes,
|
|
1463
|
+
hasChanges
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* 打印变更信息(简洁列表格式)
|
|
1468
|
+
*/
|
|
1469
|
+
printChangesTable(changes) {
|
|
1470
|
+
if (!changes.hasChanges) {
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
console.log(`
|
|
1474
|
+
\u{1F4CA} \u8868\u7ED3\u6784\u53D8\u5316
|
|
1475
|
+
`);
|
|
1476
|
+
const stats = [];
|
|
1477
|
+
const addedRegularFields = changes.addedFields.filter(
|
|
1478
|
+
(f) => f.kind === "regular"
|
|
1479
|
+
).length;
|
|
1480
|
+
const addedPartitionKeys = changes.addedFields.filter(
|
|
1481
|
+
(f) => f.kind === "partition_key"
|
|
1482
|
+
).length;
|
|
1483
|
+
const addedClusteringKeys = changes.addedFields.filter(
|
|
1484
|
+
(f) => f.kind === "clustering_key"
|
|
1485
|
+
).length;
|
|
1486
|
+
const deletedRegularFields = changes.deletedFields.filter(
|
|
1487
|
+
(f) => f.kind === "regular"
|
|
1488
|
+
).length;
|
|
1489
|
+
const deletedPartitionKeys = changes.deletedFields.filter(
|
|
1490
|
+
(f) => f.kind === "partition_key"
|
|
1491
|
+
).length;
|
|
1492
|
+
const deletedClusteringKeys = changes.deletedFields.filter(
|
|
1493
|
+
(f) => f.kind === "clustering_key"
|
|
1494
|
+
).length;
|
|
1495
|
+
if (addedRegularFields > 0) stats.push(`\u65B0\u589E${addedRegularFields}\u5B57\u6BB5`);
|
|
1496
|
+
if (addedPartitionKeys > 0) stats.push(`\u65B0\u589E\u5206\u533A\u952E${addedPartitionKeys}`);
|
|
1497
|
+
if (addedClusteringKeys > 0) stats.push(`\u65B0\u589E\u805A\u7C7B\u952E${addedClusteringKeys}`);
|
|
1498
|
+
if (changes.modifiedFields.length > 0)
|
|
1499
|
+
stats.push(`\u4FEE\u6539${changes.modifiedFields.length}\u5B57\u6BB5`);
|
|
1500
|
+
if (deletedRegularFields > 0) stats.push(`\u5220\u9664${deletedRegularFields}\u5B57\u6BB5`);
|
|
1501
|
+
if (deletedPartitionKeys > 0)
|
|
1502
|
+
stats.push(`\u5220\u9664\u5206\u533A\u952E${deletedPartitionKeys}`);
|
|
1503
|
+
if (deletedClusteringKeys > 0)
|
|
1504
|
+
stats.push(`\u5220\u9664\u805A\u7C7B\u952E${deletedClusteringKeys}`);
|
|
1505
|
+
if (changes.addedIndexes.length > 0)
|
|
1506
|
+
stats.push(`\u65B0\u589E\u7D22\u5F15${changes.addedIndexes.length}`);
|
|
1507
|
+
if (changes.rebuiltIndexes.length > 0)
|
|
1508
|
+
stats.push(`\u91CD\u5EFA\u7D22\u5F15${changes.rebuiltIndexes.length}`);
|
|
1509
|
+
if (changes.deletedIndexes.length > 0)
|
|
1510
|
+
stats.push(`\u5220\u9664\u7D22\u5F15${changes.deletedIndexes.length}`);
|
|
1511
|
+
if (stats.length > 0) {
|
|
1512
|
+
console.log(` \u7EDF\u8BA1\uFF08${stats.join("\uFF0C")}\uFF09`);
|
|
1513
|
+
}
|
|
1514
|
+
changes.addedFields.forEach((field) => {
|
|
1515
|
+
console.log(` \u{1F7E2} \u65B0\u589E\u5B57\u6BB5 ${field.name} = ${field.type}`);
|
|
1516
|
+
});
|
|
1517
|
+
changes.modifiedFields.forEach((field) => {
|
|
1518
|
+
console.log(
|
|
1519
|
+
` \u{1F7E1} \u4FEE\u6539\u5B57\u6BB5 ${field.name} = ${field.oldType} \u2192 ${field.newType}`
|
|
1520
|
+
);
|
|
1521
|
+
});
|
|
1522
|
+
changes.deletedFields.forEach((field) => {
|
|
1523
|
+
console.log(` \u{1F534} \u5220\u9664\u5B57\u6BB5 ${field.name} = ${field.type}`);
|
|
1524
|
+
});
|
|
1525
|
+
changes.addedIndexes.forEach((index) => {
|
|
1526
|
+
const optionsStr = this.formatIndexOptions(index.options);
|
|
1527
|
+
console.log(
|
|
1528
|
+
` \u{1F7E2} \u65B0\u589E\u7D22\u5F15 ${index.name}${optionsStr ? ` (${optionsStr})` : ""}`
|
|
1529
|
+
);
|
|
1530
|
+
});
|
|
1531
|
+
changes.rebuiltIndexes.forEach((index) => {
|
|
1532
|
+
console.log(` \u{1F7E1} \u91CD\u5EFA\u7D22\u5F15 ${index.name}`);
|
|
1533
|
+
});
|
|
1534
|
+
changes.deletedIndexes.forEach((index) => {
|
|
1535
|
+
console.log(` \u{1F534} \u5220\u9664\u7D22\u5F15 ${index.name}`);
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* 打印表结构信息(简洁格式)
|
|
1540
|
+
*/
|
|
1541
|
+
printTableStructure(schema) {
|
|
1542
|
+
console.log(`
|
|
1543
|
+
\u{1F4CA} \u8868\u7ED3\u6784:
|
|
1544
|
+
`);
|
|
1545
|
+
Object.entries(schema.fields.shape).forEach(([fieldName, fieldSchema]) => {
|
|
1546
|
+
const type = convertZodToCassandraType(fieldSchema);
|
|
1547
|
+
const kind = schema.partitionKeyFields.includes(fieldName) ? "partition_key" : schema.clusteringKeyFields.some((ck) => ck.field === fieldName) ? "clustering_key" : "regular";
|
|
1548
|
+
const kindLabel = kind === "partition_key" ? " [\u5206\u533A\u952E]" : kind === "clustering_key" ? " [\u805A\u7C7B\u952E]" : "";
|
|
1549
|
+
console.log(` \u2022 ${fieldName} = ${type}${kindLabel}`);
|
|
1550
|
+
});
|
|
1551
|
+
if (schema.indexes && Object.keys(schema.indexes).length > 0) {
|
|
1552
|
+
console.log(`
|
|
1553
|
+
\u{1F4C7} \u7D22\u5F15:`);
|
|
1554
|
+
Object.entries(schema.indexes).forEach(([fieldName, options]) => {
|
|
1555
|
+
const indexName = `${schema.tableName}_${fieldName}_sai_idx`.toLowerCase();
|
|
1556
|
+
const optionsStr = this.formatIndexOptions(
|
|
1557
|
+
typeof options === "boolean" ? { case_sensitive: false, normalize: true, ascii: true } : options
|
|
1558
|
+
);
|
|
1559
|
+
console.log(
|
|
1560
|
+
` \u2022 ${indexName} (${fieldName})${optionsStr ? ` - ${optionsStr}` : ""}`
|
|
1561
|
+
);
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* 同步表结构(根据检测结果执行相应的变更)
|
|
1567
|
+
*/
|
|
1568
|
+
async sync(forceRecreate = false) {
|
|
1569
|
+
const { schema, client } = this.model;
|
|
1570
|
+
const tableName = `${schema.keyspace}.${schema.tableName}`;
|
|
1571
|
+
console.log(`
|
|
1572
|
+
\u{1F504} \u5F00\u59CB\u540C\u6B65\u8868\u7ED3\u6784: ${tableName}`);
|
|
1573
|
+
console.log("=".repeat(56));
|
|
1574
|
+
await client.execute(
|
|
1575
|
+
queryHelper.ensureKeyspace(schema.keyspace, schema.keyspaceConfig)
|
|
1576
|
+
);
|
|
1577
|
+
console.log(`\u2705 Keyspace ${schema.keyspace} \u5DF2\u786E\u4FDD\u5B58\u5728`);
|
|
1578
|
+
const changes = await this.detectChanges();
|
|
1579
|
+
const tableExists = await this.checkTableExists();
|
|
1580
|
+
if (tableExists) {
|
|
1581
|
+
this.printChangesTable(changes);
|
|
1582
|
+
}
|
|
1583
|
+
if (!tableExists) {
|
|
1584
|
+
console.log(`
|
|
1585
|
+
\u{1F4CB} \u8868\u4E0D\u5B58\u5728\uFF0C\u5C06\u521B\u5EFA\u65B0\u8868`);
|
|
1586
|
+
this.printTableStructure(schema);
|
|
1587
|
+
try {
|
|
1588
|
+
console.log(`
|
|
1589
|
+
\u{1F680} \u521B\u5EFA\u8868 ${tableName}`);
|
|
1590
|
+
await client.execute(queryHelper.createTable(schema));
|
|
1591
|
+
console.log(`\u2705 \u521B\u5EFA\u8868 ${tableName} \u6210\u529F`);
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1594
|
+
console.error(`\u274C \u521B\u5EFA\u8868 ${tableName} \u5931\u8D25: ${errorMessage}`);
|
|
1595
|
+
throw error;
|
|
1596
|
+
}
|
|
1597
|
+
if (schema.indexes && Object.keys(schema.indexes).length > 0) {
|
|
1598
|
+
const indexQueries = queryHelper.createIndexes(schema);
|
|
1599
|
+
const indexFields = Object.keys(schema.indexes);
|
|
1600
|
+
for (let i = 0; i < indexQueries.length; i++) {
|
|
1601
|
+
const indexQuery = indexQueries[i];
|
|
1602
|
+
const fieldName = indexFields[i];
|
|
1603
|
+
const indexName = `${schema.tableName}_${fieldName}_sai_idx`.toLowerCase();
|
|
1604
|
+
try {
|
|
1605
|
+
console.log(`\u{1F680} \u6DFB\u52A0\u7D22\u5F15 ${indexName}`);
|
|
1606
|
+
await client.execute(indexQuery);
|
|
1607
|
+
console.log(`\u2705 \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u6210\u529F`);
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1610
|
+
console.error(`\u274C \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u5931\u8D25: ${errorMessage}`);
|
|
1611
|
+
throw error;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
console.log(`
|
|
1616
|
+
\u2705 \u8868\u7ED3\u6784\u540C\u6B65\u5B8C\u6210: ${tableName}
|
|
1617
|
+
`);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (!changes.hasChanges) {
|
|
1621
|
+
console.log(`
|
|
1622
|
+
\u2705 \u8868\u7ED3\u6784\u5DF2\u662F\u6700\u65B0\u72B6\u6001\uFF0C\u65E0\u9700\u53D8\u66F4
|
|
1623
|
+
`);
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const needsRecreate = this.shouldRecreateTable(changes, forceRecreate);
|
|
1627
|
+
if (needsRecreate && !forceRecreate) {
|
|
1628
|
+
const reasons = [];
|
|
1629
|
+
const primaryKeyChanges = [
|
|
1630
|
+
...changes.deletedFields.filter(
|
|
1631
|
+
(f) => f.kind === "partition_key" || f.kind === "clustering_key"
|
|
1632
|
+
),
|
|
1633
|
+
...changes.addedFields.filter(
|
|
1634
|
+
(f) => f.kind === "partition_key" || f.kind === "clustering_key"
|
|
1635
|
+
)
|
|
1636
|
+
];
|
|
1637
|
+
if (primaryKeyChanges.length > 0) {
|
|
1638
|
+
const partitionKeyChanges = primaryKeyChanges.filter(
|
|
1639
|
+
(f) => f.kind === "partition_key"
|
|
1640
|
+
);
|
|
1641
|
+
const clusteringKeyChanges = primaryKeyChanges.filter(
|
|
1642
|
+
(f) => f.kind === "clustering_key"
|
|
1643
|
+
);
|
|
1644
|
+
if (partitionKeyChanges.length > 0) {
|
|
1645
|
+
reasons.push(`\u4E3B\u952E\u53D8\u66F4: \u5206\u533A\u952E\u6570\u91CF\u4E0D\u4E00\u81F4`);
|
|
1646
|
+
} else if (clusteringKeyChanges.length > 0) {
|
|
1647
|
+
reasons.push(`\u4E3B\u952E\u53D8\u66F4: \u805A\u7C7B\u952E\u914D\u7F6E\u4E0D\u4E00\u81F4`);
|
|
1648
|
+
} else {
|
|
1649
|
+
const deletedKeys = primaryKeyChanges.map(
|
|
1650
|
+
(f) => `${f.name} (${f.kind === "partition_key" ? "\u5206\u533A\u952E" : "\u805A\u7C7B\u952E"})`
|
|
1651
|
+
);
|
|
1652
|
+
reasons.push(`\u4E3B\u952E\u53D8\u66F4: ${deletedKeys.join(", ")}`);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
const incompatibleTypeChanges = changes.modifiedFields.filter(
|
|
1656
|
+
(f) => !this.isTypeCompatible(f.oldType, f.newType)
|
|
1657
|
+
);
|
|
1658
|
+
if (incompatibleTypeChanges.length > 0) {
|
|
1659
|
+
const typeChangeDetails = incompatibleTypeChanges.map(
|
|
1660
|
+
(f) => `${f.name} (${f.oldType} \u2192 ${f.newType})`
|
|
1661
|
+
);
|
|
1662
|
+
reasons.push(`\u5B57\u6BB5\u7C7B\u578B\u53D8\u66F4: ${typeChangeDetails.map((d) => `\u5B57\u6BB5\u7C7B\u578B\u4E0D\u517C\u5BB9: ${d.split("(")[0]}`).join(", ")}`);
|
|
1663
|
+
}
|
|
1664
|
+
const errorMessage = `\u8868 ${tableName} \u7ED3\u6784\u5B58\u5728\u4E0D\u53EF\u53D8\u66F4\u7684\u4FEE\u6539\uFF0C\u9700\u8981\u4F7F\u7528 forceRecreate=true \u6765\u91CD\u5EFA\u8868\u3002
|
|
1665
|
+
\u4E0D\u53EF\u53D8\u66F4\u7684\u4FEE\u6539\u5305\u62EC\uFF1A
|
|
1666
|
+
${reasons.map((r) => ` - ${r}`).join("\n")}
|
|
1667
|
+
\u5176\u4ED6\u53D8\u66F4\uFF1A
|
|
1668
|
+
- \u65B0\u589E\u5B57\u6BB5: ${changes.addedFields.filter((f) => f.kind === "regular").map((f) => f.name).join(", ") || "\u65E0"}
|
|
1669
|
+
- \u5220\u9664\u5B57\u6BB5: ${changes.deletedFields.filter((f) => f.kind === "regular").map((f) => f.name).join(", ") || "\u65E0"}
|
|
1670
|
+
- \u65B0\u589E\u7D22\u5F15: ${changes.addedIndexes.map((i) => i.name).join(", ") || "\u65E0"}
|
|
1671
|
+
- \u5220\u9664\u7D22\u5F15: ${changes.deletedIndexes.map((i) => i.name).join(", ") || "\u65E0"}
|
|
1672
|
+
- \u91CD\u5EFA\u7D22\u5F15: ${changes.rebuiltIndexes.map((i) => i.name).join(", ") || "\u65E0"}`;
|
|
1673
|
+
console.error(`
|
|
1674
|
+
\u274C ${errorMessage}
|
|
1675
|
+
`);
|
|
1676
|
+
throw new Error(errorMessage);
|
|
1677
|
+
}
|
|
1678
|
+
if (needsRecreate) {
|
|
1679
|
+
console.log(`
|
|
1680
|
+
\u{1F4CB} \u8868\u5DF2\u7ECF\u5B58\u5728\uFF0C\u9700\u8981\u91CD\u5EFA\u8868`);
|
|
1681
|
+
try {
|
|
1682
|
+
console.log(`
|
|
1683
|
+
\u{1F680} \u5220\u9664\u8868 ${tableName}`);
|
|
1684
|
+
await client.execute(
|
|
1685
|
+
queryHelper.dropTable(schema.keyspace, schema.tableName)
|
|
1686
|
+
);
|
|
1687
|
+
console.log(`\u2705 \u5220\u9664\u8868 ${tableName} \u6210\u529F`);
|
|
1688
|
+
} catch (error) {
|
|
1689
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1690
|
+
console.error(`\u274C \u5220\u9664\u8868 ${tableName} \u5931\u8D25: ${errorMessage}`);
|
|
1691
|
+
throw error;
|
|
1692
|
+
}
|
|
1693
|
+
try {
|
|
1694
|
+
console.log(`\u{1F680} \u521B\u5EFA\u8868 ${tableName}`);
|
|
1695
|
+
await client.execute(queryHelper.createTable(schema));
|
|
1696
|
+
console.log(`\u2705 \u521B\u5EFA\u8868 ${tableName} \u6210\u529F`);
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1699
|
+
console.error(`\u274C \u521B\u5EFA\u8868 ${tableName} \u5931\u8D25: ${errorMessage}`);
|
|
1700
|
+
throw error;
|
|
1701
|
+
}
|
|
1702
|
+
if (schema.indexes && Object.keys(schema.indexes).length > 0) {
|
|
1703
|
+
const indexQueries = queryHelper.createIndexes(schema);
|
|
1704
|
+
const indexFields = Object.keys(schema.indexes);
|
|
1705
|
+
for (let i = 0; i < indexQueries.length; i++) {
|
|
1706
|
+
const indexQuery = indexQueries[i];
|
|
1707
|
+
const fieldName = indexFields[i];
|
|
1708
|
+
const indexName = `${schema.tableName}_${fieldName}_sai_idx`.toLowerCase();
|
|
1709
|
+
try {
|
|
1710
|
+
console.log(`\u{1F680} \u6DFB\u52A0\u7D22\u5F15 ${indexName}`);
|
|
1711
|
+
await client.execute(indexQuery);
|
|
1712
|
+
console.log(`\u2705 \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u6210\u529F`);
|
|
1713
|
+
} catch (error) {
|
|
1714
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1715
|
+
console.error(`\u274C \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u5931\u8D25: ${errorMessage}`);
|
|
1716
|
+
throw error;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
console.log(`\u2705 \u4FEE\u6539\u8868 ${tableName} \u6210\u529F`);
|
|
1721
|
+
console.log(`
|
|
1722
|
+
\u2705 \u8868\u7ED3\u6784\u540C\u6B65\u5B8C\u6210: ${tableName}
|
|
1723
|
+
`);
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
console.log(`
|
|
1727
|
+
\u{1F4CB} \u8868\u5DF2\u7ECF\u5B58\u5728\uFF0C\u5F00\u59CB\u66F4\u65B0`);
|
|
1728
|
+
if (changes.addedFields.length > 0) {
|
|
1729
|
+
const regularFields = changes.addedFields.filter(
|
|
1730
|
+
(f) => f.kind === "regular"
|
|
1731
|
+
);
|
|
1732
|
+
if (regularFields.length > 0) {
|
|
1733
|
+
try {
|
|
1734
|
+
regularFields.forEach((f) => {
|
|
1735
|
+
console.log(`\u{1F680} \u6DFB\u52A0\u5B57\u6BB5 ${f.name}`);
|
|
1736
|
+
});
|
|
1737
|
+
await client.execute(
|
|
1738
|
+
queryHelper.addColumns(
|
|
1739
|
+
schema,
|
|
1740
|
+
regularFields.map((f) => f.name)
|
|
1741
|
+
)
|
|
1742
|
+
);
|
|
1743
|
+
regularFields.forEach((f) => {
|
|
1744
|
+
console.log(`\u2705 \u6DFB\u52A0\u5B57\u6BB5 ${f.name} \u6210\u529F`);
|
|
1745
|
+
});
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1748
|
+
console.error(`\u274C \u6DFB\u52A0\u5B57\u6BB5\u5931\u8D25: ${errorMessage}`);
|
|
1749
|
+
throw error;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
if (changes.deletedFields.length > 0) {
|
|
1754
|
+
const regularFields = changes.deletedFields.filter(
|
|
1755
|
+
(f) => f.kind === "regular"
|
|
1756
|
+
);
|
|
1757
|
+
if (regularFields.length > 0) {
|
|
1758
|
+
try {
|
|
1759
|
+
regularFields.forEach((f) => {
|
|
1760
|
+
console.log(`\u{1F680} \u5220\u9664\u5B57\u6BB5 ${f.name}`);
|
|
1761
|
+
});
|
|
1762
|
+
await client.execute(
|
|
1763
|
+
queryHelper.dropColumns(
|
|
1764
|
+
schema,
|
|
1765
|
+
regularFields.map((f) => f.name)
|
|
1766
|
+
)
|
|
1767
|
+
);
|
|
1768
|
+
regularFields.forEach((f) => {
|
|
1769
|
+
console.log(`\u2705 \u5220\u9664\u5B57\u6BB5 ${f.name} \u6210\u529F`);
|
|
1770
|
+
});
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1773
|
+
console.error(`\u274C \u5220\u9664\u5B57\u6BB5\u5931\u8D25: ${errorMessage}`);
|
|
1774
|
+
throw error;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
if (changes.modifiedFields.length > 0) {
|
|
1779
|
+
const compatibleChanges = changes.modifiedFields.filter(
|
|
1780
|
+
(f) => this.isTypeCompatible(f.oldType, f.newType)
|
|
1781
|
+
);
|
|
1782
|
+
const incompatibleChanges = changes.modifiedFields.filter(
|
|
1783
|
+
(f) => !this.isTypeCompatible(f.oldType, f.newType)
|
|
1784
|
+
);
|
|
1785
|
+
if (incompatibleChanges.length > 0) {
|
|
1786
|
+
incompatibleChanges.forEach((f) => {
|
|
1787
|
+
console.log(`\u{1F680} \u4FEE\u6539\u5B57\u6BB5 ${f.name} (${f.oldType} \u2192 ${f.newType})`);
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
if (compatibleChanges.length > 0) {
|
|
1791
|
+
compatibleChanges.forEach((f) => {
|
|
1792
|
+
console.log(`\u{1F680} \u4FEE\u6539\u5B57\u6BB5 ${f.name} (${f.oldType} \u2192 ${f.newType})`);
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
if (changes.deletedIndexes.length > 0) {
|
|
1797
|
+
for (const deletedIndex of changes.deletedIndexes) {
|
|
1798
|
+
try {
|
|
1799
|
+
console.log(`\u{1F680} \u5220\u9664\u7D22\u5F15 ${deletedIndex.name}`);
|
|
1800
|
+
await client.execute(
|
|
1801
|
+
queryHelper.dropIndex(schema.keyspace, deletedIndex.name)
|
|
1802
|
+
);
|
|
1803
|
+
console.log(`\u2705 \u5220\u9664\u7D22\u5F15 ${deletedIndex.name} \u6210\u529F`);
|
|
1804
|
+
} catch (error) {
|
|
1805
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1806
|
+
console.error(
|
|
1807
|
+
`\u274C \u5220\u9664\u7D22\u5F15 ${deletedIndex.name} \u5931\u8D25: ${errorMessage}`
|
|
1808
|
+
);
|
|
1809
|
+
throw error;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (changes.rebuiltIndexes.length > 0) {
|
|
1814
|
+
for (const rebuiltIndex of changes.rebuiltIndexes) {
|
|
1815
|
+
try {
|
|
1816
|
+
console.log(`\u{1F680} \u91CD\u5EFA\u7D22\u5F15 ${rebuiltIndex.name}`);
|
|
1817
|
+
await client.execute(
|
|
1818
|
+
queryHelper.dropIndex(schema.keyspace, rebuiltIndex.name)
|
|
1819
|
+
);
|
|
1820
|
+
console.log(`\u2705 \u5220\u9664\u65E7\u7D22\u5F15 ${rebuiltIndex.name} \u6210\u529F`);
|
|
1821
|
+
} catch (error) {
|
|
1822
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1823
|
+
console.error(
|
|
1824
|
+
`\u274C \u5220\u9664\u65E7\u7D22\u5F15 ${rebuiltIndex.name} \u5931\u8D25: ${errorMessage}`
|
|
1825
|
+
);
|
|
1826
|
+
throw error;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
if (changes.addedIndexes.length > 0 || changes.rebuiltIndexes.length > 0) {
|
|
1831
|
+
if (schema.indexes) {
|
|
1832
|
+
const indexQueries = queryHelper.createIndexes(schema);
|
|
1833
|
+
const indexFields = Object.keys(schema.indexes);
|
|
1834
|
+
for (let i = 0; i < indexQueries.length; i++) {
|
|
1835
|
+
const indexQuery = indexQueries[i];
|
|
1836
|
+
const fieldName = indexFields[i];
|
|
1837
|
+
const indexName = `${schema.tableName}_${fieldName}_sai_idx`.toLowerCase();
|
|
1838
|
+
try {
|
|
1839
|
+
console.log(`\u{1F680} \u6DFB\u52A0\u7D22\u5F15 ${indexName}`);
|
|
1840
|
+
await client.execute(indexQuery);
|
|
1841
|
+
console.log(`\u2705 \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u6210\u529F`);
|
|
1842
|
+
} catch (error) {
|
|
1843
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1844
|
+
console.error(`\u274C \u6DFB\u52A0\u7D22\u5F15 ${indexName} \u5931\u8D25: ${errorMessage}`);
|
|
1845
|
+
throw error;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
console.log(`\u2705 \u4FEE\u6539\u8868 ${tableName} \u6210\u529F`);
|
|
1851
|
+
console.log(`
|
|
1852
|
+
\u2705 \u8868\u7ED3\u6784\u540C\u6B65\u5B8C\u6210: ${tableName}
|
|
1853
|
+
`);
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* 判断是否需要重建表
|
|
1857
|
+
*/
|
|
1858
|
+
shouldRecreateTable(changes, forceRecreate) {
|
|
1859
|
+
if (forceRecreate && changes.hasChanges) {
|
|
1860
|
+
return true;
|
|
1861
|
+
}
|
|
1862
|
+
const hasPrimaryKeyChanges = changes.deletedFields.some(
|
|
1863
|
+
(f) => f.kind === "partition_key" || f.kind === "clustering_key"
|
|
1864
|
+
) || changes.addedFields.some(
|
|
1865
|
+
(f) => f.kind === "partition_key" || f.kind === "clustering_key"
|
|
1866
|
+
);
|
|
1867
|
+
if (hasPrimaryKeyChanges) {
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
const hasIncompatibleTypeChanges = changes.modifiedFields.some(
|
|
1871
|
+
(f) => !this.isTypeCompatible(f.oldType, f.newType)
|
|
1872
|
+
);
|
|
1873
|
+
if (hasIncompatibleTypeChanges) {
|
|
1874
|
+
return true;
|
|
1875
|
+
}
|
|
1876
|
+
return false;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* 检查类型是否兼容
|
|
1880
|
+
* 基于 Cassandra 5.0 的类型兼容性规则
|
|
1881
|
+
*/
|
|
1882
|
+
isTypeCompatible(existingType, newType) {
|
|
1883
|
+
const normalizeType = (type) => type.toLowerCase().trim();
|
|
1884
|
+
const existing = normalizeType(existingType);
|
|
1885
|
+
const newT = normalizeType(newType);
|
|
1886
|
+
if (existing === newT) {
|
|
1887
|
+
return true;
|
|
1888
|
+
}
|
|
1889
|
+
const compatibleChanges = {
|
|
1890
|
+
// 文本类型之间的兼容性
|
|
1891
|
+
text: ["varchar", "ascii"],
|
|
1892
|
+
varchar: ["text", "ascii"],
|
|
1893
|
+
ascii: ["text", "varchar"],
|
|
1894
|
+
// 整数类型之间的兼容性
|
|
1895
|
+
int: ["bigint", "smallint", "tinyint", "varint"],
|
|
1896
|
+
bigint: ["int", "smallint", "tinyint", "varint"],
|
|
1897
|
+
smallint: ["int", "bigint", "tinyint", "varint"],
|
|
1898
|
+
tinyint: ["int", "bigint", "smallint", "varint"],
|
|
1899
|
+
varint: ["int", "bigint", "smallint", "tinyint"],
|
|
1900
|
+
// 浮点数类型之间的兼容性
|
|
1901
|
+
float: ["double"],
|
|
1902
|
+
double: ["float"],
|
|
1903
|
+
// 时间类型之间的兼容性
|
|
1904
|
+
timestamp: ["timeuuid"],
|
|
1905
|
+
// 某些场景下可以转换
|
|
1906
|
+
// UUID 类型
|
|
1907
|
+
uuid: ["timeuuid"],
|
|
1908
|
+
// 某些场景下可以转换
|
|
1909
|
+
timeuuid: ["uuid"]
|
|
1910
|
+
// 某些场景下可以转换
|
|
1911
|
+
};
|
|
1912
|
+
if (compatibleChanges[existing] && compatibleChanges[existing].includes(newT)) {
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1915
|
+
if (compatibleChanges[newT] && compatibleChanges[newT].includes(existing)) {
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1918
|
+
const existingBase = existing.split("<")[0];
|
|
1919
|
+
const newBase = newT.split("<")[0];
|
|
1920
|
+
if (existingBase === newBase && (existingBase === "list" || existingBase === "set" || existingBase === "map")) {
|
|
1921
|
+
return false;
|
|
1922
|
+
}
|
|
1923
|
+
return false;
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* 检查表是否存在
|
|
1927
|
+
*/
|
|
1928
|
+
async checkTableExists() {
|
|
1929
|
+
const { schema, client } = this.model;
|
|
1930
|
+
try {
|
|
1931
|
+
const result = await client.execute(queryHelper.tableMetadata(), [
|
|
1932
|
+
schema.keyspace,
|
|
1933
|
+
schema.tableName
|
|
1934
|
+
]);
|
|
1935
|
+
return result.rows.length > 0;
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
return false;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* 获取现有表信息
|
|
1942
|
+
*/
|
|
1943
|
+
async getExistingTableInfo() {
|
|
1944
|
+
const { schema, client } = this.model;
|
|
1945
|
+
const tableColumnsRows = await client.execute(queryHelper.tableMetadata(), [
|
|
1946
|
+
schema.keyspace,
|
|
1947
|
+
schema.tableName
|
|
1948
|
+
]);
|
|
1949
|
+
const columns = tableColumnsRows.rows.map((col) => ({
|
|
1950
|
+
...col,
|
|
1951
|
+
// 标准化 kind 值:Cassandra 可能返回 "clustering" 而不是 "clustering_key"
|
|
1952
|
+
kind: col.kind === "clustering" ? "clustering_key" : col.kind
|
|
1953
|
+
}));
|
|
1954
|
+
const indexesRows = await client.execute(queryHelper.getTableIndexes(), [
|
|
1955
|
+
schema.keyspace,
|
|
1956
|
+
schema.tableName
|
|
1957
|
+
]);
|
|
1958
|
+
const indexes = indexesRows.rows;
|
|
1959
|
+
return { columns, indexes };
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* 将 Schema 转换为表信息
|
|
1963
|
+
*/
|
|
1964
|
+
async convertSchemaToTableInfo() {
|
|
1965
|
+
const { schema } = this.model;
|
|
1966
|
+
const columns = Object.keys(schema.fields.shape).map((field) => {
|
|
1967
|
+
const kind = schema.partitionKeyFields.includes(field) ? "partition_key" : schema.clusteringKeyFields.some((ck) => ck.field === field) ? "clustering_key" : "regular";
|
|
1968
|
+
return {
|
|
1969
|
+
column_name: field,
|
|
1970
|
+
type: convertZodToCassandraType(schema.fields.shape[field]),
|
|
1971
|
+
kind
|
|
1972
|
+
};
|
|
1973
|
+
});
|
|
1974
|
+
const indexes = Object.keys(schema.indexes ?? {}).map((index) => {
|
|
1975
|
+
const options = schema.indexes?.[index];
|
|
1976
|
+
const indexName = `${schema.tableName}_${index}_sai_idx`.toLowerCase();
|
|
1977
|
+
const indexOptions = typeof options === "boolean" ? { case_sensitive: false, normalize: true, ascii: true } : options;
|
|
1978
|
+
return {
|
|
1979
|
+
index_name: indexName,
|
|
1980
|
+
options: indexOptions
|
|
1981
|
+
};
|
|
1982
|
+
});
|
|
1983
|
+
return { columns, indexes };
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* 比较索引选项是否相等
|
|
1987
|
+
*/
|
|
1988
|
+
areIndexOptionsEqual(options1, options2) {
|
|
1989
|
+
const normalizeOptions = (opts) => {
|
|
1990
|
+
if (!opts || typeof opts !== "object") {
|
|
1991
|
+
return { case_sensitive: false, normalize: true, ascii: true };
|
|
1992
|
+
}
|
|
1993
|
+
const toBool = (val) => {
|
|
1994
|
+
if (typeof val === "boolean") return val;
|
|
1995
|
+
if (typeof val === "string") return val.toLowerCase() === "true";
|
|
1996
|
+
return Boolean(val);
|
|
1997
|
+
};
|
|
1998
|
+
return {
|
|
1999
|
+
case_sensitive: toBool(opts.case_sensitive ?? false),
|
|
2000
|
+
normalize: toBool(opts.normalize ?? true),
|
|
2001
|
+
ascii: toBool(opts.ascii ?? true),
|
|
2002
|
+
similarity_function: opts.similarity_function ?? void 0
|
|
2003
|
+
};
|
|
2004
|
+
};
|
|
2005
|
+
const norm1 = normalizeOptions(options1);
|
|
2006
|
+
const norm2 = normalizeOptions(options2);
|
|
2007
|
+
return norm1.case_sensitive === norm2.case_sensitive && norm1.normalize === norm2.normalize && norm1.ascii === norm2.ascii && norm1.similarity_function === norm2.similarity_function;
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* 格式化索引选项为字符串
|
|
2011
|
+
*/
|
|
2012
|
+
formatIndexOptions(options) {
|
|
2013
|
+
if (!options || typeof options !== "object") {
|
|
2014
|
+
return "";
|
|
2015
|
+
}
|
|
2016
|
+
const parts = [];
|
|
2017
|
+
if (options.case_sensitive !== void 0) {
|
|
2018
|
+
parts.push(`case_sensitive: ${options.case_sensitive}`);
|
|
2019
|
+
}
|
|
2020
|
+
if (options.normalize !== void 0) {
|
|
2021
|
+
parts.push(`normalize: ${options.normalize}`);
|
|
2022
|
+
}
|
|
2023
|
+
if (options.ascii !== void 0) {
|
|
2024
|
+
parts.push(`ascii: ${options.ascii}`);
|
|
2025
|
+
}
|
|
2026
|
+
if (options.similarity_function) {
|
|
2027
|
+
parts.push(`similarity: ${options.similarity_function}`);
|
|
2028
|
+
}
|
|
2029
|
+
return parts.length > 0 ? parts.join(", ") : "";
|
|
2030
|
+
}
|
|
2031
|
+
};
|
|
2032
|
+
|
|
1367
2033
|
// src/model.ts
|
|
1368
2034
|
var Model = class _Model {
|
|
1369
2035
|
schema;
|
|
@@ -1527,332 +2193,107 @@ var Model = class _Model {
|
|
|
1527
2193
|
this.schema.fields
|
|
1528
2194
|
);
|
|
1529
2195
|
}
|
|
1530
|
-
// 同步 Schema
|
|
2196
|
+
// 同步 Schema(使用新的 ModelSynchronizer)
|
|
1531
2197
|
async syncSchema(force = false) {
|
|
1532
2198
|
try {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
);
|
|
1540
|
-
return operations.join("\n");
|
|
1541
|
-
} catch (error) {
|
|
1542
|
-
console.error(
|
|
1543
|
-
`\u274C \u8868 ${this.schema.keyspace}.${this.schema.tableName} \u540C\u6B65\u5931\u8D25:`,
|
|
1544
|
-
error
|
|
1545
|
-
);
|
|
1546
|
-
throw error;
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
// 同步表结构
|
|
1550
|
-
async syncTableSchema(forceRecreate = false) {
|
|
1551
|
-
const operations = [];
|
|
1552
|
-
try {
|
|
1553
|
-
console.log(
|
|
1554
|
-
` \u{1F4CB} \u8868\u7ED3\u6784: ${this.schema.keyspace}.${this.schema.tableName}`
|
|
1555
|
-
);
|
|
1556
|
-
console.log(` \u{1F511} \u5206\u533A\u952E: [${this.schema.partitionKey.join(", ")}]`);
|
|
1557
|
-
if (this.schema.clusteringKey) {
|
|
1558
|
-
console.log(
|
|
1559
|
-
` \u{1F4CA} \u805A\u7C7B\u952E: ${Object.entries(this.schema.clusteringKey).map(([k, v]) => `${k}:${v}`).join(", ")}`
|
|
1560
|
-
);
|
|
1561
|
-
}
|
|
1562
|
-
console.log(
|
|
1563
|
-
` \u{1F4DD} \u5B57\u6BB5\u6570\u91CF: ${Object.keys(this.schema.fields.shape).length}`
|
|
1564
|
-
);
|
|
1565
|
-
await this.client.execute(
|
|
1566
|
-
queryHelper.ensureKeyspace(
|
|
1567
|
-
this.schema.keyspace,
|
|
1568
|
-
this.schema.keyspaceConfig
|
|
1569
|
-
)
|
|
1570
|
-
);
|
|
1571
|
-
const tableExists = await this.checkTableExists();
|
|
2199
|
+
const synchronizer = new ModelSynchronizer({
|
|
2200
|
+
schema: this.schema,
|
|
2201
|
+
client: this.client
|
|
2202
|
+
});
|
|
2203
|
+
const changes = await synchronizer.detectChanges();
|
|
2204
|
+
const operations = [];
|
|
2205
|
+
const tableExists = await synchronizer.checkTableExists();
|
|
1572
2206
|
if (!tableExists) {
|
|
1573
2207
|
operations.push(queryHelper.createTable(this.schema));
|
|
1574
|
-
|
|
1575
|
-
|
|
2208
|
+
if (this.schema.indexes && Object.keys(this.schema.indexes).length > 0) {
|
|
2209
|
+
operations.push(...queryHelper.createIndexes(this.schema));
|
|
2210
|
+
}
|
|
2211
|
+
} else if (!changes.hasChanges) {
|
|
2212
|
+
await synchronizer.sync(force);
|
|
2213
|
+
return "";
|
|
1576
2214
|
} else {
|
|
1577
|
-
const
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
console.log("\u6240\u6709\u53D8\u66F4:", tableChanges.changes);
|
|
2215
|
+
const needsRecreate = synchronizer.shouldRecreateTable(
|
|
2216
|
+
changes,
|
|
2217
|
+
force
|
|
2218
|
+
);
|
|
2219
|
+
if (needsRecreate) {
|
|
1583
2220
|
operations.push(
|
|
1584
|
-
|
|
2221
|
+
`DROP TABLE IF EXISTS ${this.schema.keyspace}.${this.schema.tableName};`
|
|
1585
2222
|
);
|
|
1586
2223
|
operations.push(queryHelper.createTable(this.schema));
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
console.log("\u68C0\u6D4B\u5230\u53EF\u53D8\u66F4\u7684\u8868\u7ED3\u6784\u4FEE\u6539\uFF0C\u5C06\u4F7F\u7528 ALTER TABLE");
|
|
1591
|
-
console.log("\u53D8\u66F4\u8BE6\u60C5:", tableChanges.changes);
|
|
1592
|
-
operations.push(...tableChanges.operations);
|
|
1593
|
-
const indexOperations = await this.syncIndexes();
|
|
1594
|
-
operations.push(...indexOperations);
|
|
1595
|
-
} else if (!forceRecreate && hasUnalterableChanges) {
|
|
1596
|
-
const reasons = tableChanges.unalterableReasons.join("; ");
|
|
1597
|
-
throw new Error(
|
|
1598
|
-
`\u8868 ${this.schema.keyspace}.${this.schema.tableName} \u7ED3\u6784\u5B58\u5728\u4E0D\u53EF\u53D8\u66F4\u7684\u4FEE\u6539\uFF0C\u9700\u8981\u4F7F\u7528 forceRecreate=true \u6765\u91CD\u5EFA\u8868\u3002
|
|
1599
|
-
\u4E0D\u53EF\u53D8\u66F4\u7684\u4FEE\u6539\u5305\u62EC\uFF1A
|
|
1600
|
-
${reasons}
|
|
1601
|
-
\u5176\u4ED6\u53D8\u66F4\uFF1A${tableChanges.changes.join("; ")}`
|
|
1602
|
-
);
|
|
2224
|
+
if (this.schema.indexes && Object.keys(this.schema.indexes).length > 0) {
|
|
2225
|
+
operations.push(...queryHelper.createIndexes(this.schema));
|
|
2226
|
+
}
|
|
1603
2227
|
} else {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
return operations;
|
|
1615
|
-
} catch (error) {
|
|
1616
|
-
console.error("\u540C\u6B65\u8868\u7ED3\u6784\u65F6\u53D1\u751F\u9519\u8BEF:", error);
|
|
1617
|
-
throw error;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
// 检查表是否存在
|
|
1621
|
-
async checkTableExists() {
|
|
1622
|
-
try {
|
|
1623
|
-
const result = await this.client.execute(queryHelper.tableMetadata(), [
|
|
1624
|
-
this.schema.keyspace,
|
|
1625
|
-
this.schema.tableName
|
|
1626
|
-
]);
|
|
1627
|
-
return result.rows.length > 0;
|
|
1628
|
-
} catch (error) {
|
|
1629
|
-
return false;
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
// 分析表结构变更
|
|
1633
|
-
async analyzeTableChanges() {
|
|
1634
|
-
const operations = [];
|
|
1635
|
-
const changes = [];
|
|
1636
|
-
const unalterableReasons = [];
|
|
1637
|
-
try {
|
|
1638
|
-
const tableMetadata2 = await this.client.execute(queryHelper.tableMetadata(), [
|
|
1639
|
-
this.schema.keyspace,
|
|
1640
|
-
this.schema.tableName
|
|
1641
|
-
]).then((res) => res.rows);
|
|
1642
|
-
const schemaFields = Object.keys(this.schema.fields.shape);
|
|
1643
|
-
const newFields = this.getNewFields(schemaFields, tableMetadata2);
|
|
1644
|
-
const deletedFields = this.getDeletedFields(schemaFields, tableMetadata2);
|
|
1645
|
-
if (newFields.length > 0) {
|
|
1646
|
-
changes.push(`\u65B0\u589E\u5B57\u6BB5: ${newFields.join(", ")}`);
|
|
1647
|
-
operations.push(queryHelper.addColumns(this.schema, newFields));
|
|
1648
|
-
}
|
|
1649
|
-
if (deletedFields.length > 0) {
|
|
1650
|
-
changes.push(`\u5220\u9664\u5B57\u6BB5: ${deletedFields.join(", ")}`);
|
|
1651
|
-
operations.push(queryHelper.dropColumns(this.schema, deletedFields));
|
|
1652
|
-
}
|
|
1653
|
-
const primaryKeyChanges = await this.checkPrimaryKeyChanges();
|
|
1654
|
-
if (primaryKeyChanges.hasChanges) {
|
|
1655
|
-
unalterableReasons.push(
|
|
1656
|
-
`\u4E3B\u952E\u53D8\u66F4: ${primaryKeyChanges.reasons.join(", ")}`
|
|
1657
|
-
);
|
|
1658
|
-
operations.push(
|
|
1659
|
-
queryHelper.dropTable(this.schema.keyspace, this.schema.tableName)
|
|
1660
|
-
);
|
|
1661
|
-
operations.push(queryHelper.createTable(this.schema));
|
|
1662
|
-
const indexQueries = queryHelper.createIndexes(this.schema);
|
|
1663
|
-
operations.push(...indexQueries);
|
|
1664
|
-
}
|
|
1665
|
-
const typeChanges = await this.checkFieldTypeChanges(tableMetadata2);
|
|
1666
|
-
if (typeChanges.hasChanges) {
|
|
1667
|
-
unalterableReasons.push(
|
|
1668
|
-
`\u5B57\u6BB5\u7C7B\u578B\u53D8\u66F4: ${typeChanges.reasons.join(", ")}`
|
|
1669
|
-
);
|
|
1670
|
-
operations.push(
|
|
1671
|
-
queryHelper.dropTable(this.schema.keyspace, this.schema.tableName)
|
|
1672
|
-
);
|
|
1673
|
-
operations.push(queryHelper.createTable(this.schema));
|
|
1674
|
-
const indexQueries = queryHelper.createIndexes(this.schema);
|
|
1675
|
-
operations.push(...indexQueries);
|
|
1676
|
-
}
|
|
1677
|
-
const indexOperations = await this.syncIndexes();
|
|
1678
|
-
operations.push(...indexOperations);
|
|
1679
|
-
return { operations, changes, unalterableReasons };
|
|
1680
|
-
} catch (error) {
|
|
1681
|
-
throw new Error(
|
|
1682
|
-
`\u5206\u6790\u8868 ${this.schema.keyspace}.${this.schema.tableName} \u7ED3\u6784\u53D8\u66F4\u5931\u8D25: ${error}`
|
|
1683
|
-
);
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
// 智能同步索引
|
|
1687
|
-
async syncIndexes() {
|
|
1688
|
-
try {
|
|
1689
|
-
const existingIndexes = await this.client.execute(queryHelper.getTableIndexes(), [
|
|
1690
|
-
this.schema.keyspace,
|
|
1691
|
-
this.schema.tableName
|
|
1692
|
-
]).then((res) => res.rows);
|
|
1693
|
-
return queryHelper.syncIndexes(this.schema, existingIndexes);
|
|
1694
|
-
} catch (error) {
|
|
1695
|
-
console.error("\u540C\u6B65\u7D22\u5F15\u65F6\u53D1\u751F\u9519\u8BEF:", error);
|
|
1696
|
-
return [];
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
// 获取新字段
|
|
1700
|
-
getNewFields(schemaFields, tableMetadata2) {
|
|
1701
|
-
const existingFields = tableMetadata2.map(
|
|
1702
|
-
(t) => t.column_name.toLowerCase()
|
|
1703
|
-
);
|
|
1704
|
-
return schemaFields.filter(
|
|
1705
|
-
(field) => !existingFields.includes(field.toLowerCase())
|
|
1706
|
-
);
|
|
1707
|
-
}
|
|
1708
|
-
// 获取删除的字段
|
|
1709
|
-
getDeletedFields(schemaFields, tableMetadata2) {
|
|
1710
|
-
const schemaFieldSet = new Set(schemaFields.map((f) => f.toLowerCase()));
|
|
1711
|
-
return tableMetadata2.filter((t) => !schemaFieldSet.has(t.column_name.toLowerCase())).map((t) => t.column_name);
|
|
1712
|
-
}
|
|
1713
|
-
// 检查主键变更
|
|
1714
|
-
async checkPrimaryKeyChanges() {
|
|
1715
|
-
try {
|
|
1716
|
-
const partitionKeyResult = await this.client.execute(
|
|
1717
|
-
"SELECT column_name, type FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ? AND kind = 'partition_key' ALLOW FILTERING",
|
|
1718
|
-
[this.schema.keyspace, this.schema.tableName]
|
|
1719
|
-
);
|
|
1720
|
-
const clusteringKeyResult = await this.client.execute(
|
|
1721
|
-
"SELECT column_name, type FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ? AND kind = 'clustering' ALLOW FILTERING",
|
|
1722
|
-
[this.schema.keyspace, this.schema.tableName]
|
|
1723
|
-
);
|
|
1724
|
-
if (partitionKeyResult.rows.length !== this.schema.partitionKey.length) {
|
|
1725
|
-
return { hasChanges: true, reasons: ["\u5206\u533A\u952E\u6570\u91CF\u4E0D\u4E00\u81F4"] };
|
|
1726
|
-
}
|
|
1727
|
-
const existingPartitionKeys = new Set(
|
|
1728
|
-
partitionKeyResult.rows.map((row) => row.column_name.toLowerCase())
|
|
1729
|
-
);
|
|
1730
|
-
const schemaPartitionKeys = new Set(
|
|
1731
|
-
this.schema.partitionKey.map((key) => String(key).toLowerCase())
|
|
1732
|
-
);
|
|
1733
|
-
if (existingPartitionKeys.size !== schemaPartitionKeys.size) {
|
|
1734
|
-
return { hasChanges: true, reasons: ["\u5206\u533A\u952E\u5B57\u6BB5\u540D\u4E0D\u4E00\u81F4"] };
|
|
1735
|
-
}
|
|
1736
|
-
for (const key of existingPartitionKeys) {
|
|
1737
|
-
if (!schemaPartitionKeys.has(key)) {
|
|
1738
|
-
return { hasChanges: true, reasons: ["\u5206\u533A\u952E\u5B57\u6BB5\u540D\u4E0D\u4E00\u81F4"] };
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
if (this.schema.clusteringKey) {
|
|
1742
|
-
const existingClusteringKeys = new Set(
|
|
1743
|
-
clusteringKeyResult.rows.map(
|
|
1744
|
-
(row) => row.column_name.toLowerCase()
|
|
1745
|
-
)
|
|
1746
|
-
);
|
|
1747
|
-
const schemaClusteringKeys = new Set(
|
|
1748
|
-
Object.keys(this.schema.clusteringKey).map((key) => key.toLowerCase())
|
|
1749
|
-
);
|
|
1750
|
-
if (existingClusteringKeys.size !== schemaClusteringKeys.size) {
|
|
1751
|
-
return { hasChanges: true, reasons: ["\u805A\u7C7B\u952E\u914D\u7F6E\u4E0D\u4E00\u81F4"] };
|
|
1752
|
-
}
|
|
1753
|
-
for (const key of existingClusteringKeys) {
|
|
1754
|
-
if (!schemaClusteringKeys.has(key)) {
|
|
1755
|
-
return { hasChanges: true, reasons: ["\u805A\u7C7B\u952E\u914D\u7F6E\u4E0D\u4E00\u81F4"] };
|
|
2228
|
+
const regularAddedFields = changes.addedFields.filter(
|
|
2229
|
+
(f) => f.kind === "regular"
|
|
2230
|
+
);
|
|
2231
|
+
if (regularAddedFields.length > 0) {
|
|
2232
|
+
operations.push(
|
|
2233
|
+
queryHelper.addColumns(
|
|
2234
|
+
this.schema,
|
|
2235
|
+
regularAddedFields.map((f) => f.name)
|
|
2236
|
+
)
|
|
2237
|
+
);
|
|
1756
2238
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
return { hasChanges: true, reasons: ["\u805A\u7C7B\u952E\u914D\u7F6E\u4E0D\u4E00\u81F4"] };
|
|
1760
|
-
}
|
|
1761
|
-
return { hasChanges: false, reasons: [] };
|
|
1762
|
-
} catch (error) {
|
|
1763
|
-
console.error("\u68C0\u67E5\u4E3B\u952E\u53D8\u66F4\u65F6\u53D1\u751F\u9519\u8BEF:", error);
|
|
1764
|
-
return { hasChanges: false, reasons: [] };
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
// 检查字段类型变更
|
|
1768
|
-
async checkFieldTypeChanges(tableMetadata2) {
|
|
1769
|
-
try {
|
|
1770
|
-
for (const field of Object.keys(this.schema.fields.shape)) {
|
|
1771
|
-
const existingField = tableMetadata2.find(
|
|
1772
|
-
(t) => t.column_name.toLowerCase() === field.toLowerCase()
|
|
1773
|
-
);
|
|
1774
|
-
if (existingField) {
|
|
1775
|
-
const schemaType = this.getCassandraType(
|
|
1776
|
-
this.schema.fields.shape[field]
|
|
2239
|
+
const regularDeletedFields = changes.deletedFields.filter(
|
|
2240
|
+
(f) => f.kind === "regular"
|
|
1777
2241
|
);
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2242
|
+
if (regularDeletedFields.length > 0) {
|
|
2243
|
+
operations.push(
|
|
2244
|
+
queryHelper.dropColumns(
|
|
2245
|
+
this.schema,
|
|
2246
|
+
regularDeletedFields.map((f) => f.name)
|
|
2247
|
+
)
|
|
2248
|
+
);
|
|
2249
|
+
}
|
|
2250
|
+
for (const deletedIndex of changes.deletedIndexes) {
|
|
2251
|
+
operations.push(
|
|
2252
|
+
queryHelper.dropIndex(this.schema.keyspace, deletedIndex.name)
|
|
2253
|
+
);
|
|
2254
|
+
}
|
|
2255
|
+
for (const rebuiltIndex of changes.rebuiltIndexes) {
|
|
2256
|
+
operations.push(
|
|
2257
|
+
queryHelper.dropIndex(this.schema.keyspace, rebuiltIndex.name)
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2260
|
+
for (const addedIndex of changes.addedIndexes) {
|
|
2261
|
+
const fieldName = addedIndex.name.replace(`${this.schema.tableName}_`, "").replace("_sai_idx", "");
|
|
2262
|
+
const indexOptions = this.schema.indexes?.[fieldName];
|
|
2263
|
+
if (indexOptions !== void 0) {
|
|
2264
|
+
const indexQuery = createIndex(
|
|
2265
|
+
this.schema.keyspace,
|
|
2266
|
+
this.schema.tableName,
|
|
2267
|
+
fieldName,
|
|
2268
|
+
typeof indexOptions === "boolean" ? true : indexOptions
|
|
2269
|
+
);
|
|
2270
|
+
operations.push(indexQuery);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
for (const rebuiltIndex of changes.rebuiltIndexes) {
|
|
2274
|
+
const fieldName = rebuiltIndex.name.replace(`${this.schema.tableName}_`, "").replace("_sai_idx", "");
|
|
2275
|
+
const indexOptions = this.schema.indexes?.[fieldName];
|
|
2276
|
+
if (indexOptions !== void 0) {
|
|
2277
|
+
const indexQuery = createIndex(
|
|
2278
|
+
this.schema.keyspace,
|
|
2279
|
+
this.schema.tableName,
|
|
2280
|
+
fieldName,
|
|
2281
|
+
typeof indexOptions === "boolean" ? true : indexOptions
|
|
2282
|
+
);
|
|
2283
|
+
operations.push(indexQuery);
|
|
2284
|
+
}
|
|
1786
2285
|
}
|
|
1787
2286
|
}
|
|
1788
2287
|
}
|
|
1789
|
-
|
|
2288
|
+
await synchronizer.sync(force);
|
|
2289
|
+
return operations.join("\n");
|
|
1790
2290
|
} catch (error) {
|
|
1791
|
-
console.error(
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
getCassandraType(zodType) {
|
|
1797
|
-
let baseType = zodType;
|
|
1798
|
-
while (baseType._def && (baseType._def.typeName === "ZodOptional" || baseType._def.typeName === "ZodNullable" || baseType._def.typeName === "ZodDefault")) {
|
|
1799
|
-
baseType = baseType._def.innerType;
|
|
1800
|
-
}
|
|
1801
|
-
if (baseType._cassandraType) {
|
|
1802
|
-
return baseType._cassandraType;
|
|
1803
|
-
}
|
|
1804
|
-
if (baseType._def.typeName === "ZodString") {
|
|
1805
|
-
if (baseType._def.checks && baseType._def.checks.some((c) => c.kind === "uuid")) {
|
|
1806
|
-
return "uuid";
|
|
1807
|
-
}
|
|
1808
|
-
return "text";
|
|
1809
|
-
} else if (baseType._def.typeName === "ZodNumber") {
|
|
1810
|
-
return "double";
|
|
1811
|
-
} else if (baseType._def.typeName === "ZodBoolean") {
|
|
1812
|
-
return "boolean";
|
|
1813
|
-
} else if (baseType._def.typeName === "ZodDate") {
|
|
1814
|
-
return "timestamp";
|
|
1815
|
-
} else if (baseType._def.typeName === "ZodArray") {
|
|
1816
|
-
return "list";
|
|
1817
|
-
} else if (baseType._def.typeName === "ZodSet") {
|
|
1818
|
-
return "set";
|
|
1819
|
-
} else if (baseType._def.typeName === "ZodRecord") {
|
|
1820
|
-
return "map";
|
|
1821
|
-
}
|
|
1822
|
-
return "text";
|
|
1823
|
-
}
|
|
1824
|
-
// 检查类型是否兼容
|
|
1825
|
-
isTypeCompatible(existingType, newType) {
|
|
1826
|
-
if (existingType === newType) {
|
|
1827
|
-
return true;
|
|
1828
|
-
}
|
|
1829
|
-
const compatibleChanges = {
|
|
1830
|
-
text: ["varchar", "ascii"],
|
|
1831
|
-
// text 可以兼容 varchar 和 ascii
|
|
1832
|
-
varchar: ["text", "ascii"],
|
|
1833
|
-
// varchar 可以兼容 text 和 ascii
|
|
1834
|
-
ascii: ["text", "varchar"],
|
|
1835
|
-
// ascii 可以兼容 text 和 varchar
|
|
1836
|
-
int: ["bigint", "smallint", "tinyint"],
|
|
1837
|
-
// int 可以兼容其他整数类型
|
|
1838
|
-
bigint: ["int", "smallint", "tinyint"],
|
|
1839
|
-
// bigint 可以兼容其他整数类型
|
|
1840
|
-
smallint: ["int", "bigint", "tinyint"],
|
|
1841
|
-
// smallint 可以兼容其他整数类型
|
|
1842
|
-
tinyint: ["int", "bigint", "smallint"],
|
|
1843
|
-
// tinyint 可以兼容其他整数类型
|
|
1844
|
-
float: ["double"],
|
|
1845
|
-
// float 可以兼容 double
|
|
1846
|
-
double: ["float"]
|
|
1847
|
-
// double 可以兼容 float
|
|
1848
|
-
};
|
|
1849
|
-
if (compatibleChanges[existingType] && compatibleChanges[existingType].includes(newType)) {
|
|
1850
|
-
return true;
|
|
1851
|
-
}
|
|
1852
|
-
if (compatibleChanges[newType] && compatibleChanges[newType].includes(existingType)) {
|
|
1853
|
-
return true;
|
|
2291
|
+
console.error(
|
|
2292
|
+
`\u274C \u8868 ${this.schema.keyspace}.${this.schema.tableName} \u540C\u6B65\u5931\u8D25:`,
|
|
2293
|
+
error
|
|
2294
|
+
);
|
|
2295
|
+
throw error;
|
|
1854
2296
|
}
|
|
1855
|
-
return false;
|
|
1856
2297
|
}
|
|
1857
2298
|
// 构建插入查询
|
|
1858
2299
|
buildInsertQuery(data, options) {
|
|
@@ -1900,7 +2341,7 @@ function createModel(schema, client) {
|
|
|
1900
2341
|
var uuid = () => cassandraDriver.types.Uuid.random().toString();
|
|
1901
2342
|
|
|
1902
2343
|
// src/client.ts
|
|
1903
|
-
var
|
|
2344
|
+
var Client3 = class {
|
|
1904
2345
|
constructor(options) {
|
|
1905
2346
|
this.options = options;
|
|
1906
2347
|
this.cassandraClient = new cassandraDriver.Client({ ...options });
|
|
@@ -2069,7 +2510,7 @@ var Types = {
|
|
|
2069
2510
|
)
|
|
2070
2511
|
};
|
|
2071
2512
|
|
|
2072
|
-
exports.Client =
|
|
2513
|
+
exports.Client = Client3;
|
|
2073
2514
|
exports.Model = Model;
|
|
2074
2515
|
exports.TableSchema = TableSchema;
|
|
2075
2516
|
exports.Types = Types;
|