fix(oceanbase): use rowid for keyless Oracle rows

This commit is contained in:
t8y2 committed 2026-07-15 13:00:38 +08:00
1 parent 86a6846b10
commit 002dbbf602
12 files changed
+234 -17

No files matched your search

@@ -10,7 +10,7 @@ import { useHistoryStore } from "@/stores/historyStore";
import { useProductionSafetyStore } from "@/stores/productionSafetyStore"; import { useProductionSafetyStore } from "@/stores/productionSafetyStore";
import { assessProductionSql, productionContextForDatabase } from "@/lib/database/productionSafety"; import { assessProductionSql, productionContextForDatabase } from "@/lib/database/productionSafety";
import type { ColumnInfo, DatabaseType } from "@/types/database"; import type { ColumnInfo, DatabaseType } from "@/types/database";
import { DBX_NEO4J_ELEMENT_ID_COLUMN, DBX_ROWID_COLUMN } from "@/lib/table/tableEditing"; import { DBX_NEO4J_ELEMENT_ID_COLUMN, usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect"; import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
interface RowItem { interface RowItem {
@@ -862,7 +862,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
} }
function shouldClearClonedColumn(columnName: string, columnInfo: ColumnInfo | undefined): boolean { function shouldClearClonedColumn(columnName: string, columnInfo: ColumnInfo | undefined): boolean {
if (resolvedDatabaseType.value === "oracle" && columnName.toUpperCase() === DBX_ROWID_COLUMN) return true; if (usesSyntheticRowIdKey(resolvedDatabaseType.value, [columnName])) return true;
if (resolvedDatabaseType.value === "neo4j" && columnName === DBX_NEO4J_ELEMENT_ID_COLUMN) return true; if (resolvedDatabaseType.value === "neo4j" && columnName === DBX_NEO4J_ELEMENT_ID_COLUMN) return true;
const extra = columnInfo?.extra ?? ""; const extra = columnInfo?.extra ?? "";
const columnDefault = columnInfo?.column_default ?? ""; const columnDefault = columnInfo?.column_default ?? "";
@@ -15,7 +15,7 @@ import { expandNestedJsonStringsForCopy } from "@/lib/common/jsonCopyValue";
import { buildMongoCopyDocumentFromOriginal, buildMongoCopyInsertDocument, formatMongoShellLiteral, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues"; import { buildMongoCopyDocumentFromOriginal, buildMongoCopyInsertDocument, formatMongoShellLiteral, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues";
import type { DatabaseType, QueryResult } from "@/types/database"; import type { DatabaseType, QueryResult } from "@/types/database";
import type { QueryResultExportRequest } from "@/lib/backend/api"; import type { QueryResultExportRequest } from "@/lib/backend/api";
import { DBX_ROWID_COLUMN } from "@/lib/table/tableEditing"; import { usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
import { buildXlsxSqlWorksheet } from "@/lib/export/xlsxSqlSheet"; import { buildXlsxSqlWorksheet } from "@/lib/export/xlsxSqlSheet";
/** /**
@@ -1385,7 +1385,7 @@ function effectiveColumns(sourceColumns: Array<string | undefined> | undefined,
} }
function isCopyInsertOmittedColumn(databaseType: DatabaseType | undefined, column: string, tableMeta: DataGridTableMeta | undefined): boolean { function isCopyInsertOmittedColumn(databaseType: DatabaseType | undefined, column: string, tableMeta: DataGridTableMeta | undefined): boolean {
if (databaseType === "oracle" && column.toUpperCase() === DBX_ROWID_COLUMN) return true; if (usesSyntheticRowIdKey(databaseType, [column])) return true;
const columnInfo = tableMeta?.columns?.find((item) => normalizeColumnName(item.name) === normalizeColumnName(column)); const columnInfo = tableMeta?.columns?.find((item) => normalizeColumnName(item.name) === normalizeColumnName(column));
const normalizedType = columnInfo?.data_type.trim().replace(/^"|"$/g, "").toLowerCase(); const normalizedType = columnInfo?.data_type.trim().replace(/^"|"$/g, "").toLowerCase();
if (databaseType === "postgres" && (normalizedType === "tsvector" || normalizedType?.endsWith(".tsvector"))) return true; if (databaseType === "postgres" && (normalizedType === "tsvector" || normalizedType?.endsWith(".tsvector"))) return true;
+5 -1
View File
@@ -38,9 +38,11 @@ function index(columns: string[], isUnique = true, filter: string | null = null)
} }
describe("tableEditing", () => { describe("tableEditing", () => {
it("does not synthesize Oracle ROWID for views", () => { it("synthesizes ROWID only for Oracle-compatible base tables", () => {
expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "VIEW")).toEqual([]); expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "VIEW")).toEqual([]);
expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "TABLE")).toEqual([DBX_ROWID_COLUMN]); expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "TABLE")).toEqual([DBX_ROWID_COLUMN]);
expect(editablePrimaryKeys("oceanbase-oracle", [column("ID"), column("NAME")], "TABLE")).toEqual([DBX_ROWID_COLUMN]);
expect(editablePrimaryKeys("oceanbase-oracle", [column("ID", true), column("NAME")], "TABLE")).toEqual(["ID"]);
}); });
it("treats view data tabs as readonly", () => { it("treats view data tabs as readonly", () => {
@@ -50,6 +52,8 @@ describe("tableEditing", () => {
it("does not include Oracle ROWID for view data tabs", () => { 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], "VIEW")).toBe(false);
expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW")).toBe(false); expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW")).toBe(false);
expect(usesSyntheticRowIdKey("oceanbase-oracle", [DBX_ROWID_COLUMN], "TABLE")).toBe(true);
expect(usesSyntheticRowIdKey("oceanbase-oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
}); });
it("allows keyless row predicates only for databases that support them", () => { it("allows keyless row predicates only for databases that support them", () => {
@@ -124,6 +124,9 @@ const DATABASE_CAPABILITY_OVERRIDES: Partial<Record<DatabaseType, Partial<Databa
oracle: { oracle: {
syntheticKey: "oracle-rowid", syntheticKey: "oracle-rowid",
}, },
"oceanbase-oracle": {
syntheticKey: "oracle-rowid",
},
trino: { trino: {
tableData: { tableData: {
insert: true, insert: true,
+2 -1
View File
@@ -91,7 +91,8 @@ export function hiveTablePropertiesIndicateTransactional(result: { rows: readonl
export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[], tableType?: string): boolean { export function usesSyntheticRowIdKey(databaseType: DatabaseType | undefined, primaryKeys: string[], tableType?: string): boolean {
if (isViewTableType(tableType)) return false; 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)); 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));
} }
export function isHiddenGridColumn(databaseType: DatabaseType | undefined, column: string, primaryKeys: string[], tableType?: string): boolean { export function isHiddenGridColumn(databaseType: DatabaseType | undefined, column: string, primaryKeys: string[], tableType?: string): boolean {
+167 -3
View File
@@ -16,8 +16,8 @@ use data_grid_tdengine_sql::{
use crate::models::connection::DatabaseType; use crate::models::connection::DatabaseType;
use crate::sql_dialect::{ use crate::sql_dialect::{
firebird_rows_clause, quote_table_identifier, table_pagination_strategy, uses_single_row_insert_statements, firebird_rows_clause, quote_table_identifier, table_pagination_strategy, uses_oracle_row_id,
TablePaginationStrategy, uses_single_row_insert_statements, TablePaginationStrategy,
}; };
use crate::transfer::{format_ch_array_sql_literal, format_pg_array_sql_literal}; use crate::transfer::{format_ch_array_sql_literal, format_pg_array_sql_literal};
@@ -828,6 +828,9 @@ fn validate_data_grid_save(options: &DataGridSaveStatementOptions) -> Option<Str
if let Some(error) = validate_tdengine_existing_rows(options) { if let Some(error) = validate_tdengine_existing_rows(options) {
return Some(error); return Some(error);
} }
if let Some(error) = validate_oracle_keyless_lob_predicate(options) {
return Some(error);
}
let not_null_columns: Vec<String> = options let not_null_columns: Vec<String> = options
.table_meta .table_meta
@@ -878,6 +881,24 @@ fn validate_data_grid_save(options: &DataGridSaveStatementOptions) -> Option<Str
None None
} }
fn validate_oracle_keyless_lob_predicate(options: &DataGridSaveStatementOptions) -> Option<String> {
if !uses_oracle_row_id(options.database_type)
|| !options.table_meta.primary_keys.is_empty()
|| (options.dirty_rows.is_empty() && options.deleted_rows.is_empty())
{
return None;
}
let has_lob_column =
options.table_meta.columns.as_deref().unwrap_or(&[]).iter().any(|column| is_oracle_lob_type(&column.data_type));
if !has_lob_column {
return None;
}
// LOB equality is unsupported in Oracle-compatible SQL. Refuse unsafe
// keyless writes instead of dropping LOB predicates and risking extra rows.
Some("Cannot safely update or delete this Oracle-compatible row because the table has LOB columns but no primary key or ROWID identifier.".to_string())
}
fn validate_clickhouse_mutable_updates(options: &DataGridSaveStatementOptions) -> Option<String> { fn validate_clickhouse_mutable_updates(options: &DataGridSaveStatementOptions) -> Option<String> {
if options.database_type != Some(DatabaseType::ClickHouse) || options.dirty_rows.is_empty() { if options.database_type != Some(DatabaseType::ClickHouse) || options.dirty_rows.is_empty() {
return None; return None;
@@ -1961,8 +1982,16 @@ fn is_textual_column_type(data_type: &str) -> bool {
|| lower.starts_with("national character varying") || lower.starts_with("national character varying")
} }
fn is_oracle_lob_type(data_type: &str) -> bool {
let lower = data_type.trim().trim_matches('"').to_ascii_lowercase();
let base = lower.split(['(', ':', ' ']).next().unwrap_or("");
matches!(base, "blob" | "clob" | "nclob" | "bfile" | "lob")
|| lower.starts_with("binary large object")
|| lower.starts_with("character large object")
}
fn is_oracle_row_id(database_type: Option<DatabaseType>, name: Option<&str>) -> bool { fn is_oracle_row_id(database_type: Option<DatabaseType>, name: Option<&str>) -> bool {
database_type == Some(DatabaseType::Oracle) && name.is_some_and(|name| name.eq_ignore_ascii_case(DBX_ROWID_COLUMN)) uses_oracle_row_id(database_type) && name.is_some_and(|name| name.eq_ignore_ascii_case(DBX_ROWID_COLUMN))
} }
pub(crate) fn is_neo4j_element_id(database_type: Option<DatabaseType>, name: Option<&str>) -> bool { pub(crate) fn is_neo4j_element_id(database_type: Option<DatabaseType>, name: Option<&str>) -> bool {
@@ -3370,6 +3399,141 @@ mod tests {
); );
} }
#[test]
fn prepares_oceanbase_oracle_lob_deletes_with_synthetic_rowid() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::OceanbaseOracle),
table_meta: DataGridTableMeta {
catalog: None,
database: None,
schema: Some("APP".to_string()),
table_name: "DATA_REPORT_SUB_TASK".to_string(),
primary_keys: vec![DBX_ROWID_COLUMN.to_string()],
columns: Some(vec![
column(DBX_ROWID_COLUMN, "VARCHAR2", false, None),
column("ID", "VARCHAR2(100)", false, None),
column("SMC_RESPONSE", "CLOB", true, None),
column("RAW_PAYLOAD", "BLOB", true, None),
column("ARCHIVE_VALUE", "LOB", true, None),
]),
},
columns: vec![
DBX_ROWID_COLUMN.to_string(),
"ID".to_string(),
"SMC_RESPONSE".to_string(),
"RAW_PAYLOAD".to_string(),
"ARCHIVE_VALUE".to_string(),
],
source_columns: None,
rows: vec![
vec![json!("*AAABk1AAEAAAAAgAAA"), json!("task-1"), json!("response"), json!("0011"), json!("archive")],
vec![json!("*AAABk1AAEAAAAAgAAB"), json!("task-2"), Value::Null, Value::Null, Value::Null],
],
dirty_rows: vec![],
deleted_rows: vec![0, 1],
new_rows: vec![],
});
assert_eq!(result.validation_error, None);
assert_eq!(
result.statements,
vec![
"DELETE FROM \"APP\".\"DATA_REPORT_SUB_TASK\" WHERE ROWIDTOCHAR(ROWID) = '*AAABk1AAEAAAAAgAAA';",
"DELETE FROM \"APP\".\"DATA_REPORT_SUB_TASK\" WHERE ROWIDTOCHAR(ROWID) = '*AAABk1AAEAAAAAgAAB';",
]
);
}
#[test]
fn prepares_oceanbase_oracle_lob_delete_with_declared_primary_key() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::OceanbaseOracle),
table_meta: DataGridTableMeta {
catalog: None,
database: None,
schema: Some("APP".to_string()),
table_name: "DOCUMENTS".to_string(),
primary_keys: vec!["ID".to_string()],
columns: Some(vec![
column("ID", "NUMBER", false, None),
column("TITLE", "VARCHAR2(100)", false, None),
column("BODY", "CLOB", true, None),
column("CONTENT", "BLOB", true, None),
]),
},
columns: vec!["ID".to_string(), "TITLE".to_string(), "BODY".to_string(), "CONTENT".to_string()],
source_columns: None,
rows: vec![vec![json!(42), json!("report"), json!("body"), Value::Null]],
dirty_rows: vec![],
deleted_rows: vec![0],
new_rows: vec![],
});
assert_eq!(result.validation_error, None);
assert_eq!(result.statements, vec!["DELETE FROM \"APP\".\"DOCUMENTS\" WHERE \"ID\" = 42;"]);
}
#[test]
fn rejects_oceanbase_oracle_keyless_lob_writes_without_rowid() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::OceanbaseOracle),
table_meta: DataGridTableMeta {
catalog: None,
database: None,
schema: Some("APP".to_string()),
table_name: "DOCUMENTS".to_string(),
primary_keys: vec![],
columns: Some(vec![column("TITLE", "VARCHAR2(100)", false, None), column("BODY", "CLOB", true, None)]),
},
columns: vec!["TITLE".to_string(), "BODY".to_string()],
source_columns: None,
rows: vec![vec![json!("duplicate title"), json!("unique body")]],
dirty_rows: vec![],
deleted_rows: vec![0],
new_rows: vec![],
});
assert_eq!(
result.validation_error.as_deref(),
Some("Cannot safely update or delete this Oracle-compatible row because the table has LOB columns but no primary key or ROWID identifier.")
);
assert!(result.statements.is_empty());
assert!(result.rollback_statements.is_empty());
}
#[test]
fn preserves_oceanbase_oracle_keyless_predicates_for_comparable_columns() {
let result = prepare_data_grid_save(DataGridSaveStatementOptions {
database_type: Some(DatabaseType::OceanbaseOracle),
table_meta: DataGridTableMeta {
catalog: None,
database: None,
schema: Some("APP".to_string()),
table_name: "TASK_STATUS".to_string(),
primary_keys: vec![],
columns: Some(vec![
column("TASK_NAME", "VARCHAR2(100)", false, None),
column("STATUS", "VARCHAR2(16)", true, None),
]),
},
columns: vec!["TASK_NAME".to_string(), "STATUS".to_string()],
source_columns: None,
rows: vec![vec![json!("task-1"), json!("RUNNING")], vec![json!("task-2"), Value::Null]],
dirty_rows: vec![],
deleted_rows: vec![0, 1],
new_rows: vec![],
});
assert_eq!(result.validation_error, None);
assert_eq!(
result.statements,
vec![
"DELETE FROM \"APP\".\"TASK_STATUS\" WHERE \"TASK_NAME\" = 'task-1' AND \"STATUS\" = 'RUNNING';",
"DELETE FROM \"APP\".\"TASK_STATUS\" WHERE \"TASK_NAME\" = 'task-2' AND \"STATUS\" IS NULL;",
]
);
}
#[test] #[test]
fn formats_mysql_bit_literals_without_string_quotes() { fn formats_mysql_bit_literals_without_string_quotes() {
let bit = column("flag", "bit(1)", true, None); let bit = column("flag", "bit(1)", true, None);
+22 -3
View File
@@ -697,9 +697,10 @@ pub fn build_export_insert_statements(options: BuildExportInsertStatementsOption
} }
pub(crate) fn is_internal_export_column(database_type: Option<DatabaseType>, column: &str) -> bool { pub(crate) fn is_internal_export_column(database_type: Option<DatabaseType>, column: &str) -> bool {
// Oracle ROWID is injected only to identify editable rows. It is not a // Oracle-compatible ROWID is injected only to identify editable rows. It
// physical table column and must never propagate into exported SQL. // is not a physical table column and must never propagate into exports.
database_type == Some(DatabaseType::Oracle) && column.eq_ignore_ascii_case(crate::sql_dialect::DBX_ROWID_COLUMN) crate::sql_dialect::uses_oracle_row_id(database_type)
&& column.eq_ignore_ascii_case(crate::sql_dialect::DBX_ROWID_COLUMN)
} }
fn is_postgres_tsvector_export_column(database_type: Option<DatabaseType>, column_type: Option<&str>) -> bool { fn is_postgres_tsvector_export_column(database_type: Option<DatabaseType>, column_type: Option<&str>) -> bool {
@@ -1758,6 +1759,24 @@ mod tests {
assert_eq!(statements, vec!["INSERT INTO \"APP\".\"USERS\" (\"ID\", \"NAME\") VALUES (1, 'Ada');"]); assert_eq!(statements, vec!["INSERT INTO \"APP\".\"USERS\" (\"ID\", \"NAME\") VALUES (1, 'Ada');"]);
} }
#[test]
fn oceanbase_oracle_export_omits_synthetic_rowid_from_insert_columns() {
let statements = build_export_insert_statements(BuildExportInsertStatementsOptions {
database_type: Some(DatabaseType::OceanbaseOracle),
schema: Some("APP".to_string()),
table_name: Some("USERS".to_string()),
qualified_table_name: None,
columns: vec!["__DBX_ROWID".to_string(), "ID".to_string(), "NAME".to_string()],
column_types: vec![Some("VARCHAR2".to_string()), Some("NUMBER".to_string()), Some("VARCHAR2".to_string())],
column_extras: Vec::new(),
rows: vec![vec![json!("*AAABk1AAEAAAAAgAAA"), json!(1), json!("Ada")]],
batch_size: Some(100),
})
.unwrap();
assert_eq!(statements, vec!["INSERT INTO \"APP\".\"USERS\" (\"ID\", \"NAME\") VALUES (1, 'Ada');"]);
}
#[test] #[test]
fn non_oracle_export_preserves_dbx_rowid_named_column() { fn non_oracle_export_preserves_dbx_rowid_named_column() {
let statements = build_export_insert_statements(BuildExportInsertStatementsOptions { let statements = build_export_insert_statements(BuildExportInsertStatementsOptions {
+1 -1
View File
@@ -8,7 +8,7 @@ mod tests;
pub use capabilities::{ pub use capabilities::{
firebird_rows_clause, is_schema_aware, pagination_strategy, table_pagination_strategy, uses_fetch_first, firebird_rows_clause, is_schema_aware, pagination_strategy, table_pagination_strategy, uses_fetch_first,
uses_single_row_insert_statements, PaginationContext, TablePaginationStrategy, uses_oracle_row_id, uses_single_row_insert_statements, PaginationContext, TablePaginationStrategy,
}; };
pub use identifiers::{ pub use identifiers::{
normalize_where_input, qualified_table_name, qualified_table_name_with_catalog, quote_table_identifier, normalize_where_input, qualified_table_name, qualified_table_name_with_catalog, quote_table_identifier,
@@ -65,6 +65,10 @@ pub fn uses_fetch_first(database_type: DatabaseType) -> bool {
matches!(database_type, DatabaseType::Oracle | DatabaseType::Dameng | DatabaseType::Db2) matches!(database_type, DatabaseType::Oracle | DatabaseType::Dameng | DatabaseType::Db2)
} }
pub fn uses_oracle_row_id(database_type: Option<DatabaseType>) -> bool {
matches!(database_type, Some(DatabaseType::Oracle | DatabaseType::OceanbaseOracle))
}
/// Oracle 系方言不支持 `INSERT ... VALUES (...), (...)` 多行语法, /// Oracle 系方言不支持 `INSERT ... VALUES (...), (...)` 多行语法,
/// 复制为 INSERT 与导出 INSERT 都需按行生成单条语句。 /// 复制为 INSERT 与导出 INSERT 都需按行生成单条语句。
pub fn uses_single_row_insert_statements(database_type: DatabaseType) -> bool { pub fn uses_single_row_insert_statements(database_type: DatabaseType) -> bool {
@@ -1,6 +1,8 @@
use crate::models::connection::DatabaseType; use crate::models::connection::DatabaseType;
use super::capabilities::{firebird_rows_clause, table_pagination_strategy, uses_fetch_first, TablePaginationStrategy}; use super::capabilities::{
firebird_rows_clause, table_pagination_strategy, uses_oracle_row_id, TablePaginationStrategy,
};
use super::identifiers::{ use super::identifiers::{
normalize_where_input, qualified_table_name, qualified_table_name_with_catalog, quote_table_identifier, normalize_where_input, qualified_table_name, qualified_table_name_with_catalog, quote_table_identifier,
}; };
@@ -50,7 +52,7 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
// Oracle join views can raise ORA-01445 when ROWID is selected; keep the // Oracle join views can raise ORA-01445 when ROWID is selected; keep the
// synthetic ROWID fallback scoped to base-table reads. // synthetic ROWID fallback scoped to base-table reads.
let include_oracle_row_id = options.include_row_id let include_oracle_row_id = options.include_row_id
&& database_type == Some(DatabaseType::Oracle) && uses_oracle_row_id(database_type)
&& !is_view_table_type(options.table_type.as_deref()); && !is_view_table_type(options.table_type.as_deref());
let select_columns = if include_oracle_row_id { let select_columns = if include_oracle_row_id {
@@ -72,8 +74,7 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
} else { } else {
rownum_select_columns.clone() rownum_select_columns.clone()
}; };
let table_alias = let table_alias = if include_oracle_row_id { format!("{table} t") } else { table };
if include_oracle_row_id && database_type.is_some_and(uses_fetch_first) { format!("{table} t") } else { table };
match table_pagination_strategy(database_type) { match table_pagination_strategy(database_type) {
TablePaginationStrategy::IrisTop => { TablePaginationStrategy::IrisTop => {
+18
View File
@@ -784,6 +784,24 @@ 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" "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::OceanbaseOracle),
schema: Some("APP".to_string()),
table_name: "DATA_REPORT_SUB_TASK".to_string(),
table_type: Some("TABLE".to_string()),
primary_keys: vec![DBX_ROWID_COLUMN.to_string()],
columns: vec!["ID".to_string(), "SMC_RESPONSE".to_string()],
fallback_order_columns: Vec::new(),
order_by: None,
limit: Some(100),
offset: None,
where_input: None,
include_row_id: true,
..Default::default()
}),
"SELECT \"__DBX_ROWID\", \"ID\", \"SMC_RESPONSE\" FROM (SELECT ROWIDTOCHAR(t.ROWID) AS \"__DBX_ROWID\", t.* FROM \"APP\".\"DATA_REPORT_SUB_TASK\" t) WHERE ROWNUM <= 100"
);
assert_eq!( assert_eq!(
build_table_data_select_sql(TableDataSelectSqlOptions { build_table_data_select_sql(TableDataSelectSqlOptions {
database_type: Some(DatabaseType::Oracle), database_type: Some(DatabaseType::Oracle),
+3
View File
@@ -30,6 +30,7 @@ function column(name: string, isPrimaryKey = false): ColumnInfo {
test("uses ROWID as Oracle editable key when a table has no primary key", () => { test("uses ROWID as Oracle editable key when a table has no primary key", () => {
assert.deepEqual(editablePrimaryKeys("oracle", [column("ID"), column("CITY")]), [DBX_ROWID_COLUMN]); assert.deepEqual(editablePrimaryKeys("oracle", [column("ID"), column("CITY")]), [DBX_ROWID_COLUMN]);
assert.deepEqual(editablePrimaryKeys("oceanbase-oracle", [column("ID"), column("CITY")]), [DBX_ROWID_COLUMN]);
}); });
test("keeps declared primary keys ahead of Oracle ROWID fallback", () => { test("keeps declared primary keys ahead of Oracle ROWID fallback", () => {
@@ -141,6 +142,7 @@ test("keeps TDengine existing row identity and tag columns read-only", () => {
test("detects the synthetic Oracle ROWID key case", () => { test("detects the synthetic Oracle ROWID key case", () => {
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN]), true); assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN]), true);
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN.toLowerCase()]), true); assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN.toLowerCase()]), true);
assert.equal(usesSyntheticRowIdKey("oceanbase-oracle", [DBX_ROWID_COLUMN]), true);
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "VIEW"), false); assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "VIEW"), false);
assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW"), false); assert.equal(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW"), false);
assert.equal(usesSyntheticRowIdKey("postgres", [DBX_ROWID_COLUMN]), false); assert.equal(usesSyntheticRowIdKey("postgres", [DBX_ROWID_COLUMN]), false);
@@ -150,6 +152,7 @@ test("detects the synthetic Oracle ROWID key case", () => {
test("hides only the synthetic Oracle ROWID grid column", () => { 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]), true);
assert.equal(isHiddenGridColumn("oceanbase-oracle", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN]), true);
assert.equal(isHiddenGridColumn("oracle", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN], "VIEW"), false); assert.equal(isHiddenGridColumn("oracle", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN], "VIEW"), false);
assert.equal(isHiddenGridColumn("oracle", "ROWID", [DBX_ROWID_COLUMN]), false); assert.equal(isHiddenGridColumn("oracle", "ROWID", [DBX_ROWID_COLUMN]), false);
assert.equal(isHiddenGridColumn("mysql", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN]), false); assert.equal(isHiddenGridColumn("mysql", DBX_ROWID_COLUMN, [DBX_ROWID_COLUMN]), false);