fix(xugu): enable safe row editing without primary keys
This commit is contained in:
1 parent
39b2d2a6ac
commit
3e75be87eb
17 files changed
+236
-50
No files matched your search
@@ -64,6 +64,21 @@ describe("editable query hidden primary keys", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("supports Xugu's unqualified ROWID expression for keyless base tables", () => {
|
||||
expect(
|
||||
buildQueryWithHiddenPrimaryKeys({
|
||||
sql: "SELECT * FROM APP.USERS t WHERE t.ACTIVE = 1",
|
||||
databaseType: "xugu",
|
||||
primaryKeys: ["__DBX_ROWID"],
|
||||
existingResultNames: ["ID", "NAME"],
|
||||
sourceExpressions: { __DBX_ROWID: "ROWID" },
|
||||
}),
|
||||
).toEqual({
|
||||
sql: 'SELECT *, ROWID AS "__DBX_PK_0" FROM APP.USERS t WHERE t.ACTIVE = 1',
|
||||
projections: [{ sourceName: "__DBX_ROWID", alias: "__DBX_PK_0" }],
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves an Oracle FOR UPDATE clause when appending a hidden row key", () => {
|
||||
expect(
|
||||
buildQueryWithHiddenPrimaryKeys({
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
DBX_NEO4J_ELEMENT_ID_COLUMN,
|
||||
DBX_ROWID_COLUMN,
|
||||
DBX_TDENGINE_TBNAME_COLUMN,
|
||||
canInsertTableRows,
|
||||
canDeleteExistingTdengineRows,
|
||||
canEditExistingTableRows,
|
||||
canUseKeylessRowPredicate,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
isTdengineExistingRowReadonlyColumn,
|
||||
isTableDataEditable,
|
||||
supportsDataGridTransaction,
|
||||
shouldIncludeSyntheticRowId,
|
||||
usesSyntheticRowIdKey,
|
||||
} from "@/lib/table/tableEditing";
|
||||
import type { ColumnInfo, IndexInfo } from "@/types/database";
|
||||
@@ -46,6 +48,29 @@ describe("tableEditing", () => {
|
||||
expect(editablePrimaryKeys("oceanbase-oracle", [column("ID", true), column("NAME")], "TABLE")).toEqual(["ID"]);
|
||||
});
|
||||
|
||||
it("uses Xugu ROWID for ordinary, partitioned, and temporary tables but not views", () => {
|
||||
const columns = [column("ID"), column("VALUE")];
|
||||
expect(editablePrimaryKeys("xugu", columns, "TABLE")).toEqual([DBX_ROWID_COLUMN]);
|
||||
expect(editablePrimaryKeys("xugu", columns, "PARTITIONED TABLE")).toEqual([DBX_ROWID_COLUMN]);
|
||||
expect(editablePrimaryKeys("xugu", columns, "TEMPORARY TABLE")).toEqual([DBX_ROWID_COLUMN]);
|
||||
expect(editablePrimaryKeys("xugu", columns, "VIEW")).toEqual([]);
|
||||
expect(usesSyntheticRowIdKey("xugu", [DBX_ROWID_COLUMN], "TABLE")).toBe(true);
|
||||
expect(usesSyntheticRowIdKey("xugu", [DBX_ROWID_COLUMN], "PARTITIONED TABLE")).toBe(true);
|
||||
expect(usesSyntheticRowIdKey("xugu", [DBX_ROWID_COLUMN], "TEMPORARY TABLE")).toBe(true);
|
||||
expect(usesSyntheticRowIdKey("xugu", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
|
||||
expect(isTableDataEditable("xugu", [DBX_ROWID_COLUMN], "TABLE")).toBe(true);
|
||||
expect(isTableDataEditable("xugu", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
|
||||
expect(canInsertTableRows("xugu")).toBe(true);
|
||||
});
|
||||
|
||||
it("includes Xugu ROWID while cold table metadata has no declared primary keys", () => {
|
||||
expect(shouldIncludeSyntheticRowId("xugu", [], "TABLE")).toBe(true);
|
||||
expect(shouldIncludeSyntheticRowId("xugu", [], "PARTITIONED TABLE")).toBe(true);
|
||||
expect(shouldIncludeSyntheticRowId("xugu", [], "TEMPORARY TABLE")).toBe(true);
|
||||
expect(shouldIncludeSyntheticRowId("xugu", [], "VIEW")).toBe(false);
|
||||
expect(shouldIncludeSyntheticRowId("oracle", [], "TABLE")).toBe(false);
|
||||
});
|
||||
|
||||
it("treats view data tabs as readonly", () => {
|
||||
expect(isTableDataEditable("oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
import { isSchemaAware, usesTreeSchemaMode } from "@/lib/database/databaseFeatureSupport";
|
||||
|
||||
export type SyntheticEditKey = "oracle-rowid" | "neo4j-element-id";
|
||||
export type SyntheticEditKey = "oracle-rowid" | "xugu-rowid" | "neo4j-element-id";
|
||||
|
||||
export interface TableDataCapability {
|
||||
insert: boolean;
|
||||
@@ -158,6 +158,20 @@ const DATABASE_CAPABILITY_OVERRIDES: Partial<Record<DatabaseType, Partial<Databa
|
||||
oracle: {
|
||||
syntheticKey: "oracle-rowid",
|
||||
},
|
||||
xugu: {
|
||||
// Xugu exposes a stable ROWID pseudo-column for base, partitioned and
|
||||
// temporary tables. Keep this capability scoped to Xugu instead of
|
||||
// broadening Oracle-compatible behavior for other drivers.
|
||||
syntheticKey: "xugu-rowid",
|
||||
tableData: {
|
||||
insert: true,
|
||||
updateRequiresPrimaryKey: false,
|
||||
deleteRequiresPrimaryKey: false,
|
||||
keylessRowPredicate: true,
|
||||
requiresTransactionalTableForExistingRows: false,
|
||||
transaction: true,
|
||||
},
|
||||
},
|
||||
"oceanbase-oracle": {
|
||||
syntheticKey: "oracle-rowid",
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ export function editablePrimaryKeys(databaseType: DatabaseType | undefined, colu
|
||||
if (isViewTableType(tableType)) return primaryKeys;
|
||||
if (databaseType === "tdengine" && primaryKeys.length > 0 && isTdengineStableTableType(tableType)) return [DBX_TDENGINE_TBNAME_COLUMN, ...primaryKeys];
|
||||
const syntheticKey = getDatabaseCapability(databaseType).syntheticKey;
|
||||
if (syntheticKey === "oracle-rowid" && primaryKeys.length === 0) return [DBX_ROWID_COLUMN];
|
||||
if ((syntheticKey === "oracle-rowid" || syntheticKey === "xugu-rowid") && primaryKeys.length === 0) return [DBX_ROWID_COLUMN];
|
||||
if (syntheticKey === "neo4j-element-id" && primaryKeys.length === 0) return [DBX_NEO4J_ELEMENT_ID_COLUMN];
|
||||
return primaryKeys;
|
||||
}
|
||||
@@ -93,12 +93,23 @@ export function hiveTablePropertiesIndicateTransactional(result: { rows: readonl
|
||||
export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[], tableType?: string): boolean {
|
||||
if (isViewTableType(tableType)) return false;
|
||||
const syntheticKey = getDatabaseCapability(databaseType).syntheticKey;
|
||||
return primaryKeys.length === 1 && ((syntheticKey === "oracle-rowid" && primaryKeys[0].toUpperCase() === DBX_ROWID_COLUMN) || (syntheticKey === "neo4j-element-id" && primaryKeys[0] === DBX_NEO4J_ELEMENT_ID_COLUMN));
|
||||
return primaryKeys.length === 1 && (((syntheticKey === "oracle-rowid" || syntheticKey === "xugu-rowid") && primaryKeys[0].toUpperCase() === DBX_ROWID_COLUMN) || (syntheticKey === "neo4j-element-id" && primaryKeys[0] === DBX_NEO4J_ELEMENT_ID_COLUMN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Table-data tabs may start before metadata has populated declared primary keys.
|
||||
* Xugu base/partitioned/temp tables can still be addressed safely with ROWID,
|
||||
* so request the hidden projection during that cold-cache window as well.
|
||||
*/
|
||||
export function shouldIncludeSyntheticRowId(databaseType: DatabaseType | undefined, primaryKeys: string[], tableType?: string): boolean {
|
||||
if (isViewTableType(tableType)) return false;
|
||||
if (usesSyntheticRowIdKey(databaseType, primaryKeys, tableType)) return true;
|
||||
return databaseType === "xugu" && primaryKeys.length === 0;
|
||||
}
|
||||
|
||||
export function isHiddenGridColumn(databaseType: DatabaseType | undefined, column: string, primaryKeys: string[], tableType?: string): boolean {
|
||||
if (databaseType === "neo4j" && column === DBX_NEO4J_ELEMENT_ID_COLUMN) return true;
|
||||
return usesSyntheticRowIdKey(databaseType, primaryKeys, tableType) && column.toUpperCase() === DBX_ROWID_COLUMN;
|
||||
return shouldIncludeSyntheticRowId(databaseType, primaryKeys, tableType) && column.toUpperCase() === DBX_ROWID_COLUMN;
|
||||
}
|
||||
|
||||
export function isTdengineExistingRowReadonlyColumn(databaseType: DatabaseType | undefined, column: string, columns: ColumnInfo[]): boolean {
|
||||
|
||||
Reference in new issue
Block a user