fix(tdengine): generate data for stable tables
This commit is contained in:
parent
34b7814c5c
commit
98bf3d1dfd
|
|
@ -71,6 +71,14 @@ function colKey(schema: string, table: string, column: string) {
|
|||
return `${schema}.${table}.${column}`;
|
||||
}
|
||||
|
||||
function tableInfo(schema: string, table: string): TableInfo | undefined {
|
||||
return schemaTables[schema]?.find((item) => item.name === table);
|
||||
}
|
||||
|
||||
function isTdengineTagColumn(column: ColumnInfo): boolean {
|
||||
return (column.extra ?? "").toUpperCase().includes("TAG") || (column.comment ?? "").toUpperCase() === "TAG";
|
||||
}
|
||||
|
||||
// Derived state for template
|
||||
const activeCfg = computed(() => {
|
||||
const k = panelTableKey.value;
|
||||
|
|
@ -109,11 +117,13 @@ async function loadSchemas() {
|
|||
try {
|
||||
const tables = await api.listTables(cid, db, targetSchema);
|
||||
schemaTables[targetSchema] = tables;
|
||||
if (tables.some((t: { name: string }) => t.name === props.prefillTable)) {
|
||||
const prefillTableInfo = tables.find((table) => table.name === props.prefillTable);
|
||||
if (prefillTableInfo) {
|
||||
const cols = await api.getColumns(cid, db, targetSchema, props.prefillTable);
|
||||
const key = tableKey(targetSchema, props.prefillTable);
|
||||
configs[key] = {
|
||||
tableName: props.prefillTable,
|
||||
tableType: prefillTableInfo.table_type,
|
||||
schema: targetSchema,
|
||||
database: db,
|
||||
rowCount: 1000,
|
||||
|
|
@ -138,6 +148,7 @@ async function loadSchemas() {
|
|||
gKey,
|
||||
),
|
||||
isAutoIncrement: isAI,
|
||||
isTag: isTdengineTagColumn(c),
|
||||
columnDefault: c.column_default,
|
||||
};
|
||||
}),
|
||||
|
|
@ -210,6 +221,7 @@ async function loadColumns(schema: string, table: string) {
|
|||
const cols = await api.getColumns(props.prefillConnectionId, props.prefillDatabase, schema, table);
|
||||
const cfg: TableGenerateConfig = {
|
||||
tableName: table,
|
||||
tableType: tableInfo(schema, table)?.table_type,
|
||||
schema,
|
||||
database: props.prefillDatabase,
|
||||
rowCount: 1000,
|
||||
|
|
@ -234,6 +246,7 @@ async function loadColumns(schema: string, table: string) {
|
|||
gKey,
|
||||
),
|
||||
isAutoIncrement: isAI,
|
||||
isTag: isTdengineTagColumn(c),
|
||||
columnDefault: c.column_default,
|
||||
};
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -204,11 +204,13 @@ export interface ColumnGenerateConfig {
|
|||
generatorCategoryLabel?: string;
|
||||
generatorParams?: GeneratorParams;
|
||||
isAutoIncrement?: boolean;
|
||||
isTag?: boolean;
|
||||
columnDefault?: string | null;
|
||||
}
|
||||
|
||||
export interface TableGenerateConfig {
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
schema: string;
|
||||
database: string;
|
||||
rowCount: number;
|
||||
|
|
@ -1791,20 +1793,53 @@ function buildOracleInsertStatements(targetTable: string, columnList: string, va
|
|||
return statements;
|
||||
}
|
||||
|
||||
function isTdengineStableGenerate(config: TableGenerateConfig, databaseType?: DatabaseType): boolean {
|
||||
if (databaseType !== "tdengine") return false;
|
||||
const tableType = config.tableType?.trim().toUpperCase();
|
||||
return tableType === "STABLE" || tableType === "SUPER TABLE" || tableType === "SUPERTABLE" || (!tableType && config.columns.some((column) => column.isTag));
|
||||
}
|
||||
|
||||
function generateTdengineChildTableName(): string {
|
||||
const randomSuffix = Math.random().toString(36).slice(2, 8).padEnd(6, "0");
|
||||
return `dbx_gen_${Date.now().toString(36)}_${randomSuffix}`;
|
||||
}
|
||||
|
||||
export function generateTableData(config: TableGenerateConfig, databaseType?: DatabaseType): GenerateResult {
|
||||
const colNames = config.columns.map((c) => c.columnName);
|
||||
const isTdengineStable = isTdengineStableGenerate(config, databaseType);
|
||||
const hasTbnameColumn = config.columns.some((column) => column.columnName.toLowerCase() === "tbname");
|
||||
const shouldAddTbname = isTdengineStable && !hasTbnameColumn;
|
||||
const tdengineChildTableName = shouldAddTbname ? generateTdengineChildTableName() : null;
|
||||
const tagValues = new Map<string, unknown>();
|
||||
const colNames = shouldAddTbname ? ["tbname", ...config.columns.map((c) => c.columnName)] : config.columns.map((c) => c.columnName);
|
||||
const rows: unknown[][] = [];
|
||||
|
||||
for (let i = 0; i < config.rowCount; i++) {
|
||||
const row = config.columns.map((col) => generateValue(col.columnName, col.dataType, col.generatorKey, i, col.generatorParams, col.isAutoIncrement ? null : col.columnDefault));
|
||||
rows.push(row);
|
||||
const row = config.columns.map((col) => {
|
||||
if (isTdengineStable && col.isTag && tagValues.has(col.columnName)) {
|
||||
return tagValues.get(col.columnName);
|
||||
}
|
||||
const value = generateValue(col.columnName, col.dataType, col.generatorKey, i, col.generatorParams, col.isAutoIncrement ? null : col.columnDefault);
|
||||
if (isTdengineStable && col.isTag) {
|
||||
tagValues.set(col.columnName, value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
rows.push(shouldAddTbname ? [tdengineChildTableName, ...row] : row);
|
||||
}
|
||||
|
||||
const quotedCols = config.columns.map((c) => quoteTableIdentifier(databaseType, c.columnName));
|
||||
const quotedCols = colNames.map((column) => quoteTableIdentifier(databaseType, column));
|
||||
const targetTable = qualifiedTableName({ databaseType, schema: config.schema, tableName: config.tableName, database: config.database });
|
||||
const columnList = quotedCols.join(", ");
|
||||
const insertPrefix = `INSERT INTO ${targetTable} (${columnList}) VALUES`;
|
||||
const valueRows = rows.map((row) => `(${row.map((value, index) => formatGeneratedValue(value, databaseType, config.columns[index]?.dataType)).join(", ")})`);
|
||||
const valueRows = rows.map(
|
||||
(row) =>
|
||||
`(${row
|
||||
.map((value, index) => {
|
||||
const configIndex = shouldAddTbname ? index - 1 : index;
|
||||
return formatGeneratedValue(value, databaseType, configIndex >= 0 ? config.columns[configIndex]?.dataType : undefined);
|
||||
})
|
||||
.join(", ")})`,
|
||||
);
|
||||
const statements = databaseType === "oracle" ? buildOracleInsertStatements(targetTable, columnList, valueRows) : supportsGeneratedMultiRowValues(databaseType) ? [`${insertPrefix}\n${valueRows.join(",\n")};`] : valueRows.map((values) => `${insertPrefix} ${values};`);
|
||||
const sql = statements.join("\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -165,3 +165,79 @@ test("batches large Oracle data generation statements", () => {
|
|||
assert.equal(result.statements[0].match(/\n INTO /g)?.length, 100);
|
||||
assert.equal(result.statements[1].match(/\n INTO /g)?.length, 1);
|
||||
});
|
||||
|
||||
test("generates TDengine stable rows with one child table identity and stable tag values", () => {
|
||||
const result = generateTableData(
|
||||
{
|
||||
tableName: "sensor_data",
|
||||
tableType: "STABLE",
|
||||
schema: "dbx_issue4512",
|
||||
database: "dbx_issue4512",
|
||||
rowCount: 2,
|
||||
columns: [
|
||||
{
|
||||
columnName: "ts",
|
||||
dataType: "TIMESTAMP",
|
||||
rowCount: 2,
|
||||
generatorKey: "sequence",
|
||||
generatorParams: { startValue: 1, increment: 1 },
|
||||
},
|
||||
{
|
||||
columnName: "temperature",
|
||||
dataType: "FLOAT",
|
||||
rowCount: 2,
|
||||
generatorKey: "sequence",
|
||||
generatorParams: { startValue: 20, increment: 1 },
|
||||
},
|
||||
{
|
||||
columnName: "device_id",
|
||||
dataType: "BINARY(64)",
|
||||
rowCount: 2,
|
||||
generatorKey: "sequence",
|
||||
generatorParams: { startValue: 100, increment: 1 },
|
||||
isTag: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
"tdengine",
|
||||
);
|
||||
|
||||
assert.deepEqual(result.columns, ["tbname", "ts", "temperature", "device_id"]);
|
||||
assert.match(String(result.rows[0][0]), /^dbx_gen_[a-z0-9]+_[a-z0-9]+$/);
|
||||
assert.equal(result.rows[1][0], result.rows[0][0]);
|
||||
assert.deepEqual(
|
||||
result.rows.map((row) => row.slice(1)),
|
||||
[
|
||||
[1, 20, 100],
|
||||
[2, 21, 100],
|
||||
],
|
||||
);
|
||||
assert.match(result.sql, /^INSERT INTO `sensor_data` \(`tbname`, `ts`, `temperature`, `device_id`\) VALUES\n/);
|
||||
assert.equal(result.sql.match(/'dbx_gen_[a-z0-9]+_[a-z0-9]+'/g)?.length, 2);
|
||||
});
|
||||
|
||||
test("keeps ordinary TDengine table generation unchanged", () => {
|
||||
const result = generateTableData(
|
||||
{
|
||||
tableName: "sensor_data_001",
|
||||
tableType: "TABLE",
|
||||
schema: "dbx_issue4512",
|
||||
database: "dbx_issue4512",
|
||||
rowCount: 1,
|
||||
columns: [
|
||||
{
|
||||
columnName: "ts",
|
||||
dataType: "TIMESTAMP",
|
||||
rowCount: 1,
|
||||
generatorKey: "sequence",
|
||||
generatorParams: { startValue: 1, increment: 1 },
|
||||
},
|
||||
],
|
||||
},
|
||||
"tdengine",
|
||||
);
|
||||
|
||||
assert.deepEqual(result.columns, ["ts"]);
|
||||
assert.deepEqual(result.rows, [[1]]);
|
||||
assert.doesNotMatch(result.sql, /tbname/);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue