fix(oracle): skip rowid for view data queries
This commit is contained in:
parent
7e7c4944e9
commit
223ecbec20
|
|
@ -1341,6 +1341,7 @@ async function handleQuickOpenSelect(item: any) {
|
|||
database: item.database,
|
||||
schema: item.schema,
|
||||
tableName: item.objectName || item.tableName,
|
||||
tableType: item.type === "view" ? "VIEW" : item.type === "materialized_view" ? "MATERIALIZED_VIEW" : "TABLE",
|
||||
});
|
||||
} else if (item.type === "procedure" || item.type === "function" || item.type === "sequence" || item.type === "package" || item.type === "package-body") {
|
||||
// Open the object source in a source tab
|
||||
|
|
@ -1857,6 +1858,7 @@ onUnmounted(() => {
|
|||
database: activeTab.database,
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: target.tableType,
|
||||
})
|
||||
"
|
||||
@object-schema-change="(schema) => activeTab && queryStore.updateSchema(activeTab.id, schema)"
|
||||
|
|
|
|||
|
|
@ -2461,7 +2461,7 @@ const persistedColumnOrderKeys = ref<string[]>([]);
|
|||
const displayableColumnIndexes = computed(() =>
|
||||
props.result.columns
|
||||
.map((column, index) => ({ column, index }))
|
||||
.filter(({ column }) => !isHiddenGridColumn(props.databaseType, column, props.tableMeta?.primaryKeys ?? []))
|
||||
.filter(({ column }) => !isHiddenGridColumn(props.databaseType, column, props.tableMeta?.primaryKeys ?? [], props.tableMeta?.tableType))
|
||||
.map(({ index }) => index),
|
||||
);
|
||||
const goToColumnItems = computed(() =>
|
||||
|
|
@ -5004,7 +5004,7 @@ async function applyOrderBySearch() {
|
|||
orderBy: orderByClause,
|
||||
limit: pageSize.value,
|
||||
whereInput: currentWhereInput(),
|
||||
includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys),
|
||||
includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys, tableMeta.tableType),
|
||||
});
|
||||
await props.onExecuteSql(sql);
|
||||
} catch (e: any) {
|
||||
|
|
@ -5035,7 +5035,7 @@ async function applyWhereFilter() {
|
|||
orderBy: orderByInput.value.trim() || (sortCol.value ? `${queryColumnRef(sortCol.value)} ${sortDir.value.toUpperCase()}` : undefined),
|
||||
limit: pageSize.value,
|
||||
whereInput,
|
||||
includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys),
|
||||
includeRowId: usesSyntheticRowIdKey(resolvedDatabaseType.value, tableMeta.primaryKeys, tableMeta.tableType),
|
||||
});
|
||||
await props.onExecuteSql(sql);
|
||||
} catch (e: any) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ const emit = defineEmits<{
|
|||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
columnName?: string;
|
||||
},
|
||||
];
|
||||
|
|
@ -60,6 +61,7 @@ const emit = defineEmits<{
|
|||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
whereInput?: string;
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ const emit = defineEmits<{
|
|||
viewTableData: [tableName: string];
|
||||
viewTableDdl: [tableName: string];
|
||||
editTableStructure: [tableName: string];
|
||||
openObjectTable: [target: { tableName: string; schema?: string }];
|
||||
openObjectTable: [target: { tableName: string; schema?: string; tableType?: string }];
|
||||
objectSchemaChange: [schema: string | undefined];
|
||||
structureEditorSaved: [commentChanged: boolean];
|
||||
structureEditorClose: [];
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const emit = defineEmits<{
|
|||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
columnName?: string;
|
||||
},
|
||||
];
|
||||
|
|
@ -268,6 +269,7 @@ function openItemTarget(item: FieldLineageItem) {
|
|||
database: props.prefillDatabase,
|
||||
schema: item.schema || props.prefillSchema,
|
||||
tableName: item.table,
|
||||
tableType: item.kind === "viewReference" ? "VIEW" : "TABLE",
|
||||
columnName: item.column || props.prefillColumn,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
openTable: [target: { tableName: string; schema?: string }];
|
||||
openTable: [target: { tableName: string; schema?: string; tableType?: string }];
|
||||
schemaChange: [schema: string | undefined];
|
||||
}>();
|
||||
|
||||
|
|
@ -661,7 +661,7 @@ async function saveFileContent(content: string, defaultFileName: string, filterN
|
|||
}
|
||||
|
||||
function openViewData(row: ObjectBrowserRow) {
|
||||
emit("openTable", { tableName: row.name, schema: row.schema });
|
||||
emit("openTable", { tableName: row.name, schema: row.schema, tableType: row.type });
|
||||
}
|
||||
|
||||
function openStructureEditor(row: ObjectBrowserRow) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const emit = defineEmits<{
|
|||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
whereInput?: string;
|
||||
},
|
||||
];
|
||||
|
|
@ -45,6 +46,7 @@ type SearchResultItem = {
|
|||
id: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
matchedColumns: string[];
|
||||
preview: string;
|
||||
whereInput: string;
|
||||
|
|
@ -223,6 +225,7 @@ async function searchTable(task: SearchTableTask, databaseType: DatabaseType, cu
|
|||
id: `${tableLabel}:${rowIndex}:${results.value.length}`,
|
||||
schema: task.schema,
|
||||
tableName: task.table.name,
|
||||
tableType: task.table.table_type,
|
||||
matchedColumns,
|
||||
preview: rowPreview(result.columns, row, matchedColumns),
|
||||
whereInput,
|
||||
|
|
@ -257,6 +260,7 @@ function openResult(item: SearchResultItem) {
|
|||
database: props.prefillDatabase,
|
||||
schema: item.schema,
|
||||
tableName: item.tableName,
|
||||
tableType: item.tableType,
|
||||
whereInput: item.whereInput,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1283,7 +1283,7 @@ async function openData() {
|
|||
|
||||
const columns = cachedTableMeta?.columns ?? [];
|
||||
const primaryKeys = cachedTableMeta?.primaryKeys ?? [];
|
||||
const includeRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys);
|
||||
const includeRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys, tableType);
|
||||
const sql = await buildTableSelectSql({
|
||||
databaseType: effectiveDbType,
|
||||
schema: tableSchema,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
|
|||
const effectiveDbType = effectiveDatabaseTypeForConnection(config);
|
||||
const tableMeta = tableMetaForDataTab(tab);
|
||||
const primaryKeys = tab.tableMeta ? tab.tableMeta.primaryKeys : (tableMeta?.primaryKeys ?? []);
|
||||
const useRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys);
|
||||
const useRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys, tableMeta?.tableType);
|
||||
return buildTableSelectSql({
|
||||
databaseType: effectiveDbType,
|
||||
schema: tableMeta?.schema,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type NavigationTarget = {
|
|||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
columnName?: string;
|
||||
whereInput?: string;
|
||||
};
|
||||
|
|
@ -55,13 +56,15 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
if (!config) throw new Error("Connection config not found");
|
||||
const effectiveDbType = effectiveDatabaseTypeForConnection(config);
|
||||
const querySchema = metadataSchemaForConnection(config, target.database, target.schema);
|
||||
const targetTableType = target.tableType ?? "TABLE";
|
||||
if (config.db_type === "neo4j") {
|
||||
const columns = await api.getColumns(target.connectionId, target.database, querySchema, target.tableName);
|
||||
const primaryKeys = editableRowIdentifierColumns(effectiveDbType, columns);
|
||||
const primaryKeys = editableRowIdentifierColumns(effectiveDbType, columns, undefined, targetTableType);
|
||||
const sql = await buildTableSelectSql({
|
||||
databaseType: effectiveDbType,
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: targetTableType,
|
||||
columns: columns.map((column) => column.name),
|
||||
primaryKeys,
|
||||
whereInput: target.whereInput,
|
||||
|
|
@ -71,7 +74,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
queryStore.setTableMeta(tabId, {
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: "TABLE",
|
||||
tableType: targetTableType,
|
||||
columns,
|
||||
primaryKeys,
|
||||
});
|
||||
|
|
@ -82,6 +85,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
databaseType: effectiveDbType,
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: targetTableType,
|
||||
whereInput: target.whereInput,
|
||||
limit: pageLimit,
|
||||
});
|
||||
|
|
@ -89,7 +93,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
queryStore.setTableMeta(tabId, {
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: "TABLE",
|
||||
tableType: targetTableType,
|
||||
columns: [],
|
||||
primaryKeys: [],
|
||||
});
|
||||
|
|
@ -106,6 +110,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
databaseType: effectiveDbType,
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: targetTableType,
|
||||
whereInput: target.whereInput,
|
||||
limit: 0,
|
||||
});
|
||||
|
|
@ -115,12 +120,12 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
try {
|
||||
const columns = await api.getColumns(target.connectionId, target.database, querySchema, target.tableName);
|
||||
const indexes = await api.listIndexes(target.connectionId, target.database, querySchema, target.tableName).catch(() => []);
|
||||
const primaryKeys = editableRowIdentifierColumns(effectiveDbType, columns, indexes);
|
||||
const useRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys);
|
||||
const primaryKeys = editableRowIdentifierColumns(effectiveDbType, columns, indexes, targetTableType);
|
||||
const useRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys, targetTableType);
|
||||
queryStore.setTableMeta(tabId, {
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: "TABLE",
|
||||
tableType: targetTableType,
|
||||
columns,
|
||||
primaryKeys,
|
||||
});
|
||||
|
|
@ -129,6 +134,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab
|
|||
databaseType: effectiveDbType,
|
||||
schema: target.schema,
|
||||
tableName: target.tableName,
|
||||
tableType: targetTableType,
|
||||
whereInput: target.whereInput,
|
||||
primaryKeys,
|
||||
columns: columns.map((column) => column.name),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { DBX_ROWID_COLUMN, canEditExistingTableRows, canUseKeylessRowPredicate, editablePrimaryKeys, editableRowIdentifierColumns, isClickHouseExistingRowReadonlyColumn, isTableDataEditable, supportsDataGridTransaction } from "@/lib/table/tableEditing";
|
||||
import { DBX_ROWID_COLUMN, canEditExistingTableRows, canUseKeylessRowPredicate, editablePrimaryKeys, editableRowIdentifierColumns, isClickHouseExistingRowReadonlyColumn, isTableDataEditable, supportsDataGridTransaction, usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
|
||||
import type { ColumnInfo, IndexInfo } from "@/types/database";
|
||||
|
||||
function column(name: string, isPrimaryKey = false): ColumnInfo {
|
||||
|
|
@ -33,6 +33,11 @@ describe("tableEditing", () => {
|
|||
expect(isTableDataEditable("oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
|
||||
});
|
||||
|
||||
it("does not include Oracle ROWID for view data tabs", () => {
|
||||
expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
|
||||
expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW")).toBe(false);
|
||||
});
|
||||
|
||||
it("allows keyless row predicates only for databases that support them", () => {
|
||||
expect(canUseKeylessRowPredicate("postgres", [])).toBe(true);
|
||||
expect(canUseKeylessRowPredicate("mysql", [])).toBe(true);
|
||||
|
|
|
|||
|
|
@ -72,13 +72,14 @@ export function hiveTablePropertiesIndicateTransactional(result: { rows: readonl
|
|||
});
|
||||
}
|
||||
|
||||
export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[]): boolean {
|
||||
export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[], tableType?: string): boolean {
|
||||
if (isViewTableType(tableType)) return false;
|
||||
return primaryKeys.length === 1 && ((databaseType === "oracle" && primaryKeys[0].toUpperCase() === DBX_ROWID_COLUMN) || (databaseType === "neo4j" && primaryKeys[0] === DBX_NEO4J_ELEMENT_ID_COLUMN));
|
||||
}
|
||||
|
||||
export function isHiddenGridColumn(databaseType: DatabaseType | undefined, column: string, primaryKeys: string[]): boolean {
|
||||
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) && column.toUpperCase() === DBX_ROWID_COLUMN;
|
||||
return usesSyntheticRowIdKey(databaseType, primaryKeys, tableType) && column.toUpperCase() === DBX_ROWID_COLUMN;
|
||||
}
|
||||
|
||||
export function isTdengineExistingRowReadonlyColumn(databaseType: DatabaseType | undefined, column: string, columns: ColumnInfo[]): boolean {
|
||||
|
|
|
|||
|
|
@ -1687,10 +1687,12 @@ export const useQueryStore = defineStore("query", () => {
|
|||
elapsed: elapsed?.(),
|
||||
});
|
||||
const indexes = await api.listIndexes(tab.connectionId, tab.database, metadataSchema, metadataTableName).catch(() => []);
|
||||
const primaryKeys = editableRowIdentifierColumns(dbType as DatabaseType, columns, indexes);
|
||||
const tableType = tab.tableMeta?.tableType;
|
||||
const primaryKeys = editableRowIdentifierColumns(dbType as DatabaseType, columns, indexes, tableType);
|
||||
const tableMeta = {
|
||||
schema: metadataSchema || undefined,
|
||||
tableName: metadataTableName,
|
||||
tableType,
|
||||
columns,
|
||||
primaryKeys,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,8 +29,13 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
};
|
||||
let order_by = options.order_by.as_deref().filter(|order| !order.trim().is_empty()).or(default_order_by.as_deref());
|
||||
let order = order_by.map(|order_by| format!(" ORDER BY {order_by}")).unwrap_or_default();
|
||||
// Oracle join views can raise ORA-01445 when ROWID is selected; keep the
|
||||
// synthetic ROWID fallback scoped to base-table reads.
|
||||
let include_oracle_row_id = options.include_row_id
|
||||
&& database_type == Some(DatabaseType::Oracle)
|
||||
&& !is_view_table_type(options.table_type.as_deref());
|
||||
|
||||
let select_columns = if options.include_row_id && database_type == Some(DatabaseType::Oracle) {
|
||||
let select_columns = if include_oracle_row_id {
|
||||
format!("ROWIDTOCHAR(t.ROWID) AS \"{DBX_ROWID_COLUMN}\", t.*")
|
||||
} else {
|
||||
build_select_columns(
|
||||
|
|
@ -40,7 +45,7 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
)
|
||||
};
|
||||
let rownum_select_columns = quoted_table_columns_or_star(database_type, &options.columns);
|
||||
let page_select_columns = if options.include_row_id && database_type == Some(DatabaseType::Oracle) {
|
||||
let page_select_columns = if include_oracle_row_id {
|
||||
if options.columns.is_empty() {
|
||||
"*".to_string()
|
||||
} else {
|
||||
|
|
@ -49,11 +54,8 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
} else {
|
||||
rownum_select_columns.clone()
|
||||
};
|
||||
let table_alias = if options.include_row_id && database_type.is_some_and(uses_fetch_first) {
|
||||
format!("{table} t")
|
||||
} else {
|
||||
table
|
||||
};
|
||||
let table_alias =
|
||||
if include_oracle_row_id && database_type.is_some_and(uses_fetch_first) { format!("{table} t") } else { table };
|
||||
|
||||
match table_pagination_strategy(database_type) {
|
||||
TablePaginationStrategy::IrisTop => {
|
||||
|
|
@ -88,11 +90,8 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
)
|
||||
}
|
||||
TablePaginationStrategy::Rownum => {
|
||||
let rownum_inner_select_columns = if options.include_row_id && database_type == Some(DatabaseType::Oracle) {
|
||||
&select_columns
|
||||
} else {
|
||||
&rownum_select_columns
|
||||
};
|
||||
let rownum_inner_select_columns =
|
||||
if include_oracle_row_id { &select_columns } else { &rownum_select_columns };
|
||||
build_rownum_table_select_sql(
|
||||
&table_alias,
|
||||
&where_clause,
|
||||
|
|
@ -136,6 +135,10 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
}
|
||||
}
|
||||
|
||||
fn is_view_table_type(table_type: Option<&str>) -> bool {
|
||||
table_type.is_some_and(|value| value.to_ascii_uppercase().contains("VIEW"))
|
||||
}
|
||||
|
||||
pub fn build_table_select_sql(options: TableSelectSqlOptions<'_>) -> String {
|
||||
let database_type = options.database_type;
|
||||
let table = qualified_table_name(database_type, options.schema, options.table_name);
|
||||
|
|
|
|||
|
|
@ -699,6 +699,23 @@ fn builds_oracle_and_neo4j_table_data_queries() {
|
|||
}),
|
||||
"SELECT \"__DBX_ROWID\", \"ID\", \"NAME\" FROM (SELECT ROWIDTOCHAR(t.ROWID) AS \"__DBX_ROWID\", t.* FROM \"DBXTEST\".\"DBX_LOAD_TABLE_006\" t) WHERE ROWNUM <= 100"
|
||||
);
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Oracle),
|
||||
schema: Some("DBXTEST".to_string()),
|
||||
table_name: "DBX_JOIN_VIEW".to_string(),
|
||||
table_type: Some("VIEW".to_string()),
|
||||
primary_keys: vec![DBX_ROWID_COLUMN.to_string()],
|
||||
columns: vec!["ID".to_string(), "NAME".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
order_by: None,
|
||||
limit: Some(100),
|
||||
offset: None,
|
||||
where_input: None,
|
||||
include_row_id: true,
|
||||
}),
|
||||
"SELECT \"ID\", \"NAME\" FROM (SELECT \"ID\", \"NAME\" FROM \"DBXTEST\".\"DBX_JOIN_VIEW\") WHERE ROWNUM <= 100"
|
||||
);
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Neo4j),
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ test("keeps TDengine existing row identity and tag columns read-only", () => {
|
|||
test("detects the synthetic Oracle ROWID key case", () => {
|
||||
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN]), true);
|
||||
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN.toLowerCase()]), true);
|
||||
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "VIEW"), false);
|
||||
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW"), false);
|
||||
assert.equal(usesSyntheticRowIdKey("postgres", [DBX_ROWID_COLUMN]), false);
|
||||
assert.equal(usesSyntheticRowIdKey("oracle", ["ID"]), false);
|
||||
assert.equal(usesSyntheticRowIdKey("neo4j", [DBX_NEO4J_ELEMENT_ID_COLUMN]), true);
|
||||
|
|
@ -141,6 +143,7 @@ test("detects the synthetic Oracle ROWID key case", () => {
|
|||
|
||||
test("hides only the synthetic Oracle ROWID grid column", () => {
|
||||
assert.equal(isHiddenGridColumn("oracle", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN]), true);
|
||||
assert.equal(isHiddenGridColumn("oracle", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN], "VIEW"), false);
|
||||
assert.equal(isHiddenGridColumn("oracle", "ROWID", [DBX_ROWID_COLUMN]), false);
|
||||
assert.equal(isHiddenGridColumn("mysql", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN]), false);
|
||||
assert.equal(isHiddenGridColumn("neo4j", DBX_NEO4J_ELEMENT_ID_COLUMN, [DBX_NEO4J_ELEMENT_ID_COLUMN]), true);
|
||||
|
|
|
|||
Loading…
Reference in New Issue