fix(tdengine): fix table data preview
This commit is contained in:
parent
dbf9cae4c0
commit
d5b483b050
|
|
@ -81,7 +81,10 @@ public final class TDengineAgent extends BaseDatabaseAgent {
|
|||
try (java.sql.Statement stmt = requireConnected().createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SHOW DATABASES")) {
|
||||
while (rs.next()) {
|
||||
result.add(new DatabaseInfo(rs.getString(1)));
|
||||
String name = rs.getString(1);
|
||||
if (!isSystemDatabase(name)) {
|
||||
result.add(new DatabaseInfo(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
@ -323,6 +326,14 @@ public final class TDengineAgent extends BaseDatabaseAgent {
|
|||
return "`" + identifier.replace("`", "``") + "`";
|
||||
}
|
||||
|
||||
private static boolean isSystemDatabase(String name) {
|
||||
if (name == null) {
|
||||
return false;
|
||||
}
|
||||
String normalized = name.trim().toLowerCase(Locale.ROOT);
|
||||
return "information_schema".equals(normalized) || "performance_schema".equals(normalized);
|
||||
}
|
||||
|
||||
private static Integer parseNumericPrecision(String dataType) {
|
||||
return parseIntGroup(NUMERIC_PRECISION_PATTERN, dataType, 2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ const props = defineProps<{
|
|||
tableMeta?: {
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
columns: ColumnInfo[];
|
||||
primaryKeys: string[];
|
||||
};
|
||||
|
|
@ -3858,6 +3859,7 @@ async function applyOrderBySearch() {
|
|||
databaseType: resolvedDatabaseType.value,
|
||||
schema: tableMeta.schema,
|
||||
tableName: tableMeta.tableName,
|
||||
tableType: tableMeta.tableType,
|
||||
columns: tableMeta.columns.map((column) => column.name),
|
||||
primaryKeys: tableMeta.primaryKeys,
|
||||
orderBy: orderByClause,
|
||||
|
|
@ -3888,6 +3890,7 @@ async function applyWhereFilter() {
|
|||
databaseType: resolvedDatabaseType.value,
|
||||
schema: tableMeta.schema,
|
||||
tableName: tableMeta.tableName,
|
||||
tableType: tableMeta.tableType,
|
||||
columns: tableMeta.columns.map((column) => column.name),
|
||||
primaryKeys: tableMeta.primaryKeys,
|
||||
orderBy: orderByInput.value.trim() || (sortCol.value ? `${queryColumnRef(sortCol.value)} ${sortDir.value.toUpperCase()}` : undefined),
|
||||
|
|
|
|||
|
|
@ -904,7 +904,7 @@ async function openData() {
|
|||
dbType: config?.db_type,
|
||||
});
|
||||
const tableSchema = connectionObjectTreeNodeSchema(config, node.database, node.schema);
|
||||
const tableType = node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : "TABLE";
|
||||
const tableType = node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : (node.tableType ?? "TABLE");
|
||||
const isSameDataTableTab = (tab: (typeof queryStore.tabs)[number]) => tab.mode === "data" && tab.connectionId === node.connectionId && tab.database === node.database && (tab.schema || "") === (tableSchema || "") && (tab.tableMeta?.tableName || tab.title) === node.label;
|
||||
const activateExistingSameTableTab = () => {
|
||||
const existing = queryStore.tabs.find(isSameDataTableTab);
|
||||
|
|
@ -1072,6 +1072,7 @@ async function openData() {
|
|||
databaseType: effectiveDbType,
|
||||
schema: tableSchema,
|
||||
tableName: node.label,
|
||||
tableType,
|
||||
columns: columns.map((column) => column.name),
|
||||
primaryKeys,
|
||||
limit,
|
||||
|
|
@ -2719,6 +2720,7 @@ async function exportDataLegacy(format: "csv" | "json" | "sql") {
|
|||
databaseType: effectiveDbType,
|
||||
schema: node.schema,
|
||||
tableName: node.label,
|
||||
tableType: node.tableType,
|
||||
columns: queryColumns,
|
||||
executePage: (sql) => api.executeQuery(connectionId, database, sql),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
|
|||
databaseType: effectiveDbType,
|
||||
schema: tableMeta?.schema,
|
||||
tableName: tableMeta?.tableName ?? "",
|
||||
tableType: tableMeta?.tableType,
|
||||
columns: tableMeta?.columns.map((column) => column.name),
|
||||
primaryKeys,
|
||||
includeRowId: useRowId,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ export interface FetchTableDataForExportOptions {
|
|||
databaseType?: DatabaseType;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
columns?: string[];
|
||||
pageSize?: number;
|
||||
buildPageSql?: (options: { databaseType?: DatabaseType; schema?: string; tableName: string; columns?: string[]; limit: number; offset: number }) => Promise<string> | string;
|
||||
buildPageSql?: (options: { databaseType?: DatabaseType; schema?: string; tableName: string; tableType?: string; columns?: string[]; limit: number; offset: number }) => Promise<string> | string;
|
||||
executePage: (sql: string) => Promise<QueryResult>;
|
||||
}
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ export async function fetchTableDataForExport(options: FetchTableDataForExportOp
|
|||
databaseType: options.databaseType,
|
||||
schema: options.schema,
|
||||
tableName: options.tableName,
|
||||
tableType: options.tableType,
|
||||
columns: options.columns,
|
||||
limit: pageSize,
|
||||
offset,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export interface BuildTableSelectSqlOptions {
|
|||
databaseType?: DatabaseType;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
tableType?: string;
|
||||
primaryKeys?: string[];
|
||||
columns?: string[];
|
||||
fallbackOrderColumns?: string[];
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const SYSTEM_DATABASE_RULES: Partial<Record<DatabaseType, ReadonlySet<string>>>
|
|||
vastbase: new Set(["template0", "template1"]),
|
||||
redshift: new Set(["template0", "template1"]),
|
||||
clickhouse: new Set(["information_schema", "system"]),
|
||||
tdengine: new Set(["information_schema", "performance_schema"]),
|
||||
sqlserver: new Set(["master", "model", "msdb", "tempdb"]),
|
||||
mongodb: new Set(["admin", "config", "local"]),
|
||||
oracle: new Set([
|
||||
|
|
|
|||
|
|
@ -2172,6 +2172,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||
databaseType: effectiveDbType,
|
||||
schema: tableMeta.schema,
|
||||
tableName: tableMeta.tableName,
|
||||
tableType: tableMeta.tableType,
|
||||
columns: tableMeta.columns.map((column) => column.name),
|
||||
primaryKeys,
|
||||
whereInput: tab.whereInput,
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ pub async fn export_table_data_csv_core(state: &AppState, options: TableCsvExpor
|
|||
database_type: Some(database_type),
|
||||
schema: options.schema.clone(),
|
||||
table_name: options.table_name.clone(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: options.columns.clone(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -33,7 +33,11 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String
|
|||
let select_columns = if options.include_row_id && database_type == Some(DatabaseType::Oracle) {
|
||||
format!("ROWIDTOCHAR(t.ROWID) AS \"{DBX_ROWID_COLUMN}\", t.*")
|
||||
} else {
|
||||
build_select_columns(database_type, &options.columns)
|
||||
build_select_columns(
|
||||
database_type,
|
||||
&options.columns,
|
||||
tdengine_should_include_tbname(database_type, options.table_type.as_deref()),
|
||||
)
|
||||
};
|
||||
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) {
|
||||
|
|
@ -209,16 +213,40 @@ pub(super) fn is_tdengine_tbname(database_type: Option<DatabaseType>, name: &str
|
|||
database_type == Some(DatabaseType::Tdengine) && name.eq_ignore_ascii_case(DBX_TDENGINE_TBNAME_COLUMN)
|
||||
}
|
||||
|
||||
pub(super) fn build_select_columns(database_type: Option<DatabaseType>, columns: &[String]) -> String {
|
||||
fn tdengine_should_include_tbname(database_type: Option<DatabaseType>, table_type: Option<&str>) -> bool {
|
||||
if database_type != Some(DatabaseType::Tdengine) {
|
||||
return false;
|
||||
}
|
||||
matches!(
|
||||
table_type.map(|value| value.trim().to_ascii_uppercase()),
|
||||
Some(value) if value == "STABLE" || value == "SUPER TABLE" || value == "SUPERTABLE"
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn build_select_columns(
|
||||
database_type: Option<DatabaseType>,
|
||||
columns: &[String],
|
||||
include_tdengine_tbname: bool,
|
||||
) -> String {
|
||||
if columns.is_empty() {
|
||||
return "*".to_string();
|
||||
}
|
||||
if database_type == Some(DatabaseType::Tdengine) {
|
||||
let mut tdengine_columns = Vec::new();
|
||||
if !columns.iter().any(|column| column.eq_ignore_ascii_case(DBX_TDENGINE_TBNAME_COLUMN)) {
|
||||
if include_tdengine_tbname
|
||||
&& !columns.iter().any(|column| column.eq_ignore_ascii_case(DBX_TDENGINE_TBNAME_COLUMN))
|
||||
{
|
||||
tdengine_columns.push(DBX_TDENGINE_TBNAME_COLUMN.to_string());
|
||||
}
|
||||
tdengine_columns.extend(columns.iter().cloned());
|
||||
tdengine_columns.extend(
|
||||
columns
|
||||
.iter()
|
||||
.filter(|column| include_tdengine_tbname || !column.eq_ignore_ascii_case(DBX_TDENGINE_TBNAME_COLUMN))
|
||||
.cloned(),
|
||||
);
|
||||
if tdengine_columns.is_empty() {
|
||||
return "*".to_string();
|
||||
}
|
||||
return tdengine_columns
|
||||
.iter()
|
||||
.map(|column| {
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Mysql),
|
||||
schema: None,
|
||||
table_name: "users".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["id".to_string()],
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -240,6 +241,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Goldendb),
|
||||
schema: None,
|
||||
table_name: "sys_dic".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -256,6 +258,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Postgres),
|
||||
schema: Some("public".to_string()),
|
||||
table_name: "orders".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -272,6 +275,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Xugu),
|
||||
schema: Some("DBX_TEST".to_string()),
|
||||
table_name: "PRODUCTS".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -288,6 +292,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::StarRocks),
|
||||
schema: None,
|
||||
table_name: "sales_report".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: vec!["customer_name".to_string(), "amount".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -304,6 +309,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Db2),
|
||||
schema: Some("DB2INST1".to_string()),
|
||||
table_name: "ORDERS".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -336,6 +342,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::OceanbaseOracle),
|
||||
schema: Some("DBXTEST".to_string()),
|
||||
table_name: "ORDERS".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -352,6 +359,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::OceanbaseOracle),
|
||||
schema: Some("DBXTEST".to_string()),
|
||||
table_name: "ORDERS".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["ID".to_string()],
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -368,6 +376,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Db2),
|
||||
schema: Some("DB2INST1".to_string()),
|
||||
table_name: "ORDERS".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["ID".to_string()],
|
||||
columns: vec!["ID".to_string(), "AMOUNT".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -384,6 +393,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Iris),
|
||||
schema: Some("Ens".to_string()),
|
||||
table_name: "AlarmResponse".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -400,6 +410,7 @@ fn builds_table_data_where_and_schema_queries() {
|
|||
database_type: Some(DatabaseType::Iotdb),
|
||||
schema: Some("root.test".to_string()),
|
||||
table_name: "device2".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -420,6 +431,7 @@ fn builds_informix_table_data_with_skip_first_pagination() {
|
|||
database_type: Some(DatabaseType::Informix),
|
||||
schema: Some("ignored".to_string()),
|
||||
table_name: "users".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["id".to_string()],
|
||||
columns: vec!["id".to_string(), "name".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -452,6 +464,7 @@ fn explicit_table_data_order_is_preserved() {
|
|||
database_type: Some(DatabaseType::Postgres),
|
||||
schema: Some("public".to_string()),
|
||||
table_name: "country_gdp".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["year".to_string()],
|
||||
columns: vec!["iso3".to_string(), "year".to_string(), "gdp_pc".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -471,6 +484,7 @@ fn builds_iris_table_data_sql_with_literal_top_and_quoted_object() {
|
|||
database_type: Some(DatabaseType::Iris),
|
||||
schema: Some("Ens".to_string()),
|
||||
table_name: "AlarmResponse".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["ID".to_string()],
|
||||
columns: vec!["ID".to_string(), "Status".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -493,32 +507,68 @@ fn builds_iris_table_data_sql_with_literal_top_and_quoted_object() {
|
|||
#[test]
|
||||
fn builds_table_data_special_column_queries() {
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Tdengine),
|
||||
schema: Some("test_db".to_string()),
|
||||
table_name: "meters".to_string(),
|
||||
primary_keys: vec!["ts".to_string()],
|
||||
columns: vec![
|
||||
"ts".to_string(),
|
||||
"current".to_string(),
|
||||
"voltage".to_string(),
|
||||
"location".to_string(),
|
||||
"groupid".to_string(),
|
||||
],
|
||||
fallback_order_columns: Vec::new(),
|
||||
order_by: None,
|
||||
limit: Some(100),
|
||||
offset: None,
|
||||
where_input: None,
|
||||
include_row_id: false,
|
||||
}),
|
||||
"SELECT tbname, `ts` AS `ts`, `current` AS `current`, `voltage` AS `voltage`, `location` AS `location`, `groupid` AS `groupid` FROM `test_db`.`meters` LIMIT 100;"
|
||||
);
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Tdengine),
|
||||
schema: Some("test_db".to_string()),
|
||||
table_name: "meters".to_string(),
|
||||
table_type: Some("STABLE".to_string()),
|
||||
primary_keys: vec!["ts".to_string()],
|
||||
columns: vec![
|
||||
"ts".to_string(),
|
||||
"current".to_string(),
|
||||
"voltage".to_string(),
|
||||
"location".to_string(),
|
||||
"groupid".to_string(),
|
||||
],
|
||||
fallback_order_columns: Vec::new(),
|
||||
order_by: None,
|
||||
limit: Some(100),
|
||||
offset: None,
|
||||
where_input: None,
|
||||
include_row_id: false,
|
||||
}),
|
||||
"SELECT tbname, `ts` AS `ts`, `current` AS `current`, `voltage` AS `voltage`, `location` AS `location`, `groupid` AS `groupid` FROM `test_db`.`meters` LIMIT 100;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Tdengine),
|
||||
schema: Some("test_db".to_string()),
|
||||
table_name: "d1001".to_string(),
|
||||
table_type: Some("TABLE".to_string()),
|
||||
primary_keys: vec!["ts".to_string()],
|
||||
columns: vec!["ts".to_string(), "current".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
order_by: None,
|
||||
limit: Some(100),
|
||||
offset: None,
|
||||
where_input: None,
|
||||
include_row_id: false,
|
||||
}),
|
||||
"SELECT `ts` AS `ts`, `current` AS `current` FROM `test_db`.`d1001` LIMIT 100;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Tdengine),
|
||||
schema: Some("test_db".to_string()),
|
||||
table_name: "d1001".to_string(),
|
||||
table_type: Some("TABLE".to_string()),
|
||||
primary_keys: vec!["ts".to_string()],
|
||||
columns: vec!["tbname".to_string(), "ts".to_string(), "current".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
order_by: None,
|
||||
limit: Some(100),
|
||||
offset: None,
|
||||
where_input: None,
|
||||
include_row_id: false,
|
||||
}),
|
||||
"SELECT `ts` AS `ts`, `current` AS `current` FROM `test_db`.`d1001` LIMIT 100;"
|
||||
);
|
||||
assert_eq!(
|
||||
build_table_data_select_sql(TableDataSelectSqlOptions {
|
||||
database_type: Some(DatabaseType::Hive),
|
||||
schema: None,
|
||||
table_name: "departments".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: Vec::new(),
|
||||
columns: vec!["id".to_string(), "name".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -539,6 +589,7 @@ fn builds_sqlserver_table_data_pages() {
|
|||
database_type: Some(DatabaseType::SqlServer),
|
||||
schema: Some("dbo".to_string()),
|
||||
table_name: "accounts".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["id".to_string()],
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -555,6 +606,7 @@ fn builds_sqlserver_table_data_pages() {
|
|||
database_type: Some(DatabaseType::SqlServer),
|
||||
schema: Some("sales".to_string()),
|
||||
table_name: "orders".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["order_id".to_string()],
|
||||
columns: vec!["order_id".to_string(), "customer".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -575,6 +627,7 @@ fn builds_oracle_and_neo4j_table_data_queries() {
|
|||
database_type: Some(DatabaseType::Oracle),
|
||||
schema: Some("DBXTEST".to_string()),
|
||||
table_name: "DBX_LOAD_TABLE_006".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec![DBX_ROWID_COLUMN.to_string()],
|
||||
columns: Vec::new(),
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
@ -607,6 +660,7 @@ fn builds_oracle_and_neo4j_table_data_queries() {
|
|||
database_type: Some(DatabaseType::Neo4j),
|
||||
schema: None,
|
||||
table_name: "Employee".to_string(),
|
||||
table_type: None,
|
||||
primary_keys: vec!["id".to_string()],
|
||||
columns: vec!["id".to_string(), "first name".to_string(), "role".to_string()],
|
||||
fallback_order_columns: Vec::new(),
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ pub struct TableDataSelectSqlOptions {
|
|||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub schema: Option<String>,
|
||||
pub table_name: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub table_type: Option<String>,
|
||||
#[serde(default)]
|
||||
pub primary_keys: Vec<String>,
|
||||
#[serde(default)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue