mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 05:20:02 +00:00
fix(export): keep owner schema prefix in query result INSERT export
Closes #11192
This commit is contained in:
@@ -498,6 +498,7 @@ interface DataGridProps {
|
||||
sourceColumns?: Array<string | undefined>;
|
||||
joinedWriteTargets?: import("@/types/database").QueryTab["queryWriteTargets"];
|
||||
queryMultiSource?: boolean;
|
||||
hasUniqueQueryInsertTarget?: boolean;
|
||||
readonlyColumnIndexes?: number[];
|
||||
/**
|
||||
* Column comments for a multi-source query result (e.g. JOIN), indexed by
|
||||
@@ -8561,6 +8562,7 @@ const {
|
||||
pageSql: computed(() => props.pageSql),
|
||||
tableMeta: computed(() => (props.tableMeta ? { ...props.tableMeta } : undefined)),
|
||||
includeDatabaseName: computed(() => settingsStore.editorSettings.generateSqlIncludeDatabaseName),
|
||||
hasUniqueQueryInsertTarget: computed(() => props.hasUniqueQueryInsertTarget === true),
|
||||
copyInsertTargetLabel: computed(() => props.tableMeta?.tableName ?? props.customSaveHandler?.targetLabel),
|
||||
mongoUpdateTarget: computed(() => props.mongoUpdateTarget),
|
||||
databaseType: computed(() => props.databaseType),
|
||||
|
||||
@@ -2554,6 +2554,7 @@ defineExpose({
|
||||
:source-columns="hasNeo4jNodes ? undefined : activeTab.querySourceColumns"
|
||||
:joined-write-targets="hasNeo4jNodes ? undefined : activeTab.queryWriteTargets"
|
||||
:query-multi-source="(activeTab.queryWriteTargets?.length ?? 0) > 1"
|
||||
:has-unique-query-insert-target="!!activeTab.tableMeta && activeTab.queryAnalysis?.multiSource !== true && (activeTab.queryAnalysis?.sources?.length ?? 1) === 1 && (activeTab.queryWriteTargets?.length ?? 1) <= 1"
|
||||
:readonly-column-indexes="hasNeo4jNodes ? undefined : groupedQueryReadonlyColumnIndexes(activeTab)"
|
||||
:result-column-comments="hasNeo4jNodes ? undefined : activeTab.resultColumnComments"
|
||||
:query-display-source-columns="hasNeo4jNodes ? undefined : activeTab.queryDisplaySourceColumns"
|
||||
@@ -2595,6 +2596,7 @@ defineExpose({
|
||||
format: 'csv' | 'xlsx' | 'json' | 'txt' | 'sql';
|
||||
includeSqlSheet?: boolean;
|
||||
exportTableName?: string;
|
||||
exportSchema?: string;
|
||||
exportColumnTypes?: Array<string | null | undefined>;
|
||||
exportColumnExtras?: Array<string | null | undefined>;
|
||||
insertMode?: SqlInsertMode;
|
||||
|
||||
@@ -40,6 +40,8 @@ function createOptions(overrides: Partial<UseDataGridExportOptions> = {}): UseDa
|
||||
],
|
||||
})),
|
||||
databaseType: computed(() => "postgres"),
|
||||
includeDatabaseName: computed(() => false),
|
||||
hasUniqueQueryInsertTarget: computed(() => true),
|
||||
connectionId: computed(() => "conn"),
|
||||
database: computed(() => "dbx"),
|
||||
context: computed(() => "results"),
|
||||
@@ -104,6 +106,34 @@ describe("SQL export column selection across entrypoints", () => {
|
||||
expect(saveTextFile).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ includeDatabaseName: true, expectedSchema: "APP_OWNER" },
|
||||
{ includeDatabaseName: false, expectedSchema: undefined },
|
||||
])("uses the Oracle owner only when generated SQL includes database names ($includeDatabaseName)", async ({ includeDatabaseName, expectedSchema }) => {
|
||||
await useDataGridExport(
|
||||
createOptions({
|
||||
databaseType: computed(() => "oracle"),
|
||||
includeDatabaseName: computed(() => includeDatabaseName),
|
||||
tableMeta: computed(() => ({ tableName: "USERS", schema: "APP_OWNER", primaryKeys: [], columns: [] })),
|
||||
}),
|
||||
).exportSql([7]);
|
||||
|
||||
expect(formatSqlInsert).toHaveBeenCalledWith(expect.objectContaining({ schema: expectedSchema, tableName: "USERS" }));
|
||||
});
|
||||
|
||||
it("does not infer an INSERT target from first-source metadata for a multi-source result", async () => {
|
||||
await useDataGridExport(
|
||||
createOptions({
|
||||
databaseType: computed(() => "oracle"),
|
||||
includeDatabaseName: computed(() => true),
|
||||
hasUniqueQueryInsertTarget: computed(() => false),
|
||||
tableMeta: computed(() => ({ tableName: "USERS", schema: "APP_OWNER", primaryKeys: [], columns: [] })),
|
||||
}),
|
||||
).exportSql([7]);
|
||||
|
||||
expect(formatSqlInsert).toHaveBeenCalledWith(expect.objectContaining({ schema: undefined, tableName: "query_result" }));
|
||||
});
|
||||
|
||||
it("keeps second duplicate, spatial metadata and raw types aligned after internal-key removal", async () => {
|
||||
const names = ["__DBX_ROWID", "id", "id", "shape"];
|
||||
const choices = sqlExportColumnChoices(names);
|
||||
|
||||
@@ -36,6 +36,7 @@ vi.mock("@/stores/settingsStore", () => ({
|
||||
editorSettings: {
|
||||
exportBatchSize: 1000,
|
||||
globalDateTimeExportFormat: "",
|
||||
generateSqlIncludeDatabaseName: false,
|
||||
numericColumnRightAlign: true,
|
||||
},
|
||||
}),
|
||||
@@ -63,6 +64,8 @@ function createOptions(overrides: Partial<UseDataGridExportOptions> = {}): UseDa
|
||||
],
|
||||
})),
|
||||
databaseType: computed(() => "postgres"),
|
||||
includeDatabaseName: computed(() => false),
|
||||
hasUniqueQueryInsertTarget: computed(() => true),
|
||||
connectionId: computed(() => "connection-1"),
|
||||
database: computed(() => "dbx"),
|
||||
context: computed(() => "results"),
|
||||
@@ -142,6 +145,59 @@ describe("query result SQL export progress", () => {
|
||||
expect(api.startQueryResultExport).toHaveBeenCalledWith(expect.objectContaining({ format: "sql", exportTableName: "users", exportColumnTypes: ["int4", "text"] }), expect.any(Function));
|
||||
});
|
||||
|
||||
it("passes a resolved Oracle owner as the INSERT target without changing query execution schema", async () => {
|
||||
const queryResultExportRequest = vi.fn(async (request) => ({
|
||||
...request,
|
||||
connectionId: "connection-1",
|
||||
database: "ORCLPDB1",
|
||||
databaseType: "oracle" as const,
|
||||
schema: "CURRENT_USER",
|
||||
queryBaseSql: "SELECT id, name FROM APP_OWNER.users",
|
||||
sql: "SELECT id, name FROM APP_OWNER.users",
|
||||
executionId: "execution-1",
|
||||
}));
|
||||
const state = useDataGridExport(
|
||||
createOptions({
|
||||
databaseType: computed(() => "oracle"),
|
||||
includeDatabaseName: computed(() => true),
|
||||
tableMeta: computed(() => ({ tableName: "USERS", schema: "APP_OWNER", primaryKeys: [], columns: [] })),
|
||||
queryResultExportRequest,
|
||||
}),
|
||||
);
|
||||
|
||||
await state.exportSql();
|
||||
|
||||
expect(queryResultExportRequest).toHaveBeenCalledWith(expect.objectContaining({ exportTableName: "USERS", exportSchema: "APP_OWNER" }));
|
||||
expect(api.startQueryResultExport).toHaveBeenCalledWith(expect.objectContaining({ schema: "CURRENT_USER", exportSchema: "APP_OWNER", exportTableName: "USERS" }), expect.any(Function));
|
||||
});
|
||||
|
||||
it("does not infer a target table for query results without a unique source", async () => {
|
||||
const queryResultExportRequest = vi.fn(async (request) => ({
|
||||
...request,
|
||||
connectionId: "connection-1",
|
||||
database: "dbx",
|
||||
databaseType: "oracle" as const,
|
||||
schema: "CURRENT_USER",
|
||||
queryBaseSql: "SELECT users.id FROM users JOIN orders ON orders.user_id = users.id",
|
||||
sql: "SELECT users.id FROM users JOIN orders ON orders.user_id = users.id",
|
||||
executionId: "execution-1",
|
||||
}));
|
||||
const state = useDataGridExport(
|
||||
createOptions({
|
||||
databaseType: computed(() => "oracle"),
|
||||
includeDatabaseName: computed(() => true),
|
||||
hasUniqueQueryInsertTarget: computed(() => false),
|
||||
tableMeta: computed(() => ({ tableName: "USERS", schema: "APP_OWNER", primaryKeys: [], columns: [] })),
|
||||
queryResultExportRequest,
|
||||
}),
|
||||
);
|
||||
|
||||
await state.exportSql();
|
||||
|
||||
expect(queryResultExportRequest).toHaveBeenCalledWith(expect.objectContaining({ exportTableName: undefined, exportSchema: undefined }));
|
||||
expect(api.startQueryResultExport).toHaveBeenCalledWith(expect.objectContaining({ exportTableName: undefined, exportSchema: undefined }), expect.any(Function));
|
||||
});
|
||||
|
||||
it("forwards result column EXTRA metadata so identity INSERT exports can be replayed", async () => {
|
||||
const state = useDataGridExport(
|
||||
createOptions({
|
||||
|
||||
@@ -104,8 +104,10 @@ export interface UseDataGridExportOptions {
|
||||
exportSql?: ComputedRef<string | undefined>;
|
||||
pageSql?: ComputedRef<string | undefined>;
|
||||
tableMeta: ComputedRef<DataGridTableMeta | undefined>;
|
||||
/** Editor setting "Include database name in generated SQL" — passed through to SQL extractors. */
|
||||
/** Editor setting "Include database name in generated SQL" — passed through to SQL extractors and INSERT exports. */
|
||||
includeDatabaseName?: ComputedRef<boolean>;
|
||||
/** True only when query metadata resolves exactly one source table as the INSERT target. */
|
||||
hasUniqueQueryInsertTarget?: ComputedRef<boolean>;
|
||||
copyInsertTargetLabel?: ComputedRef<string | undefined>;
|
||||
mongoUpdateTarget?: ComputedRef<MongoCopyUpdateTarget | undefined>;
|
||||
databaseType: ComputedRef<DatabaseType | undefined>;
|
||||
@@ -145,6 +147,7 @@ export interface UseDataGridExportOptions {
|
||||
format: "csv" | "xlsx" | "json" | "txt" | "sql";
|
||||
includeSqlSheet?: boolean;
|
||||
exportTableName?: string;
|
||||
exportSchema?: string;
|
||||
exportColumnTypes?: Array<string | null | undefined>;
|
||||
exportColumnExtras?: Array<string | null | undefined>;
|
||||
insertMode?: SqlInsertMode;
|
||||
@@ -211,6 +214,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
exportSql: resultExportSql,
|
||||
pageSql: resultPageSql,
|
||||
tableMeta,
|
||||
hasUniqueQueryInsertTarget,
|
||||
copyInsertTargetLabel,
|
||||
sourceColumns,
|
||||
columnComments: columnCommentsOption,
|
||||
@@ -1416,6 +1420,20 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return extras.some((extra) => !!extra) ? extras : undefined;
|
||||
}
|
||||
|
||||
function sqlInsertTargetMeta(): DataGridTableMeta | undefined {
|
||||
if (context.value === "results" && hasUniqueQueryInsertTarget?.value !== true) return undefined;
|
||||
return tableMeta.value;
|
||||
}
|
||||
|
||||
function sqlInsertTargetSchema(meta: DataGridTableMeta | undefined): string | undefined {
|
||||
if (!meta?.schema || dropsSchemaQualifier(databaseType.value, options.includeDatabaseName?.value, meta.catalog)) return undefined;
|
||||
return meta.schema;
|
||||
}
|
||||
|
||||
function sqlInsertTargetName(meta: DataGridTableMeta | undefined): string {
|
||||
return meta?.tableName || (context.value === "results" ? "query_result" : "table_name");
|
||||
}
|
||||
|
||||
/** 供导出请求使用:把“不含主键”设置转换成后端请求字段。 */
|
||||
function sqlExportPrimaryKeyOptions(): { excludePrimaryKeys?: boolean; primaryKeys?: string[] } {
|
||||
const excludeColumns = sqlExportExcludedColumns();
|
||||
@@ -1572,12 +1590,14 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
}
|
||||
|
||||
const exportId = uuid();
|
||||
const insertTarget = format === "sql" ? sqlInsertTargetMeta() : undefined;
|
||||
const baseRequest = await queryResultExportRequest({
|
||||
exportId,
|
||||
filePath: outputPath,
|
||||
format,
|
||||
includeSqlSheet,
|
||||
exportTableName: format === "sql" ? tableMeta.value?.tableName : undefined,
|
||||
exportTableName: insertTarget?.tableName,
|
||||
exportSchema: insertTarget?.tableName ? sqlInsertTargetSchema(insertTarget) : undefined,
|
||||
exportColumnTypes: format === "sql" ? allColumnTypes.value?.map((type) => type ?? null) : undefined,
|
||||
exportColumnExtras: format === "sql" ? sqlExportColumnExtras(allColumns.value) : undefined,
|
||||
...(format === "sql" && sqlExportOptions ? { insertMode: sqlExportOptions.insertMode } : {}),
|
||||
@@ -1771,11 +1791,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
|
||||
logExportStage("sql-build-start");
|
||||
const insertTarget = sqlInsertTargetMeta();
|
||||
const content = await formatSqlInsert({
|
||||
databaseType: databaseType.value,
|
||||
identifierQuote: options.identifierQuote?.value,
|
||||
schema: tableMeta.value?.schema,
|
||||
tableName: tableMeta.value?.tableName || "table_name",
|
||||
schema: sqlInsertTargetSchema(insertTarget),
|
||||
tableName: sqlInsertTargetName(insertTarget),
|
||||
columns: exportData.columns,
|
||||
columnTypes: exportData.columnTypes,
|
||||
columnExtras: exportData.columnExtras,
|
||||
@@ -1817,11 +1838,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
try {
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false, false);
|
||||
const exportData = sqlInsertExportData(result, sqlExportOptions.selectedColumns);
|
||||
const insertTarget = sqlInsertTargetMeta();
|
||||
const content = await formatSqlInsert({
|
||||
databaseType: databaseType.value,
|
||||
identifierQuote: options.identifierQuote?.value,
|
||||
schema: tableMeta.value?.schema,
|
||||
tableName: tableMeta.value?.tableName || "table_name",
|
||||
schema: sqlInsertTargetSchema(insertTarget),
|
||||
tableName: sqlInsertTargetName(insertTarget),
|
||||
columns: exportData.columns,
|
||||
columnTypes: exportData.columnTypes,
|
||||
columnExtras: exportData.columnExtras,
|
||||
|
||||
@@ -6095,6 +6095,8 @@ export interface QueryResultExportRequest {
|
||||
executionId?: string;
|
||||
dateTimeFormat?: string;
|
||||
exportTableName?: string;
|
||||
/** Explicit INSERT target schema; separate from `schema`, which scopes query execution. */
|
||||
exportSchema?: string;
|
||||
exportColumnTypes?: Array<string | null | undefined>;
|
||||
selectedColumns?: SqlExportColumnSelection[];
|
||||
/**
|
||||
|
||||
@@ -102,6 +102,93 @@ describe("queryStore query result export", () => {
|
||||
expect(request?.insertMode).toBe("single");
|
||||
});
|
||||
|
||||
it("keeps query execution schema separate from the resolved Oracle INSERT owner", async () => {
|
||||
mocks.getConfig.mockReturnValue({
|
||||
id: "oracle-1",
|
||||
name: "Oracle",
|
||||
db_type: "oracle",
|
||||
database: "ORCLPDB1",
|
||||
default_schema: "CURRENT_USER",
|
||||
query_timeout_secs: 30,
|
||||
});
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab("oracle-1", "ORCLPDB1", "Query", "query", "CURRENT_USER", "SELECT ID FROM APP_OWNER.USERS");
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
tab.lastExecutedSql = tab.sql;
|
||||
tab.result = {
|
||||
columns: ["ID"],
|
||||
rows: [[1]],
|
||||
affected_rows: 0,
|
||||
execution_time_ms: 1,
|
||||
};
|
||||
tab.tableMeta = { schema: "APP_OWNER", tableName: "USERS", columns: [], primaryKeys: ["ID"] };
|
||||
tab.queryAnalysis = {
|
||||
schema: "APP_OWNER",
|
||||
tableName: "USERS",
|
||||
selectStar: false,
|
||||
sources: [{ key: "USERS:0", schema: "APP_OWNER", tableName: "USERS" }],
|
||||
columns: [{ sourceName: "ID", resultName: "ID", expression: "ID" }],
|
||||
};
|
||||
|
||||
const request = await store.buildQueryResultExportRequest(tabId, {
|
||||
exportId: "oracle-owner-export",
|
||||
filePath: "users.sql",
|
||||
format: "sql",
|
||||
exportTableName: "USERS",
|
||||
exportSchema: "APP_OWNER",
|
||||
});
|
||||
|
||||
expect(request).toMatchObject({
|
||||
databaseType: "oracle",
|
||||
database: "ORCLPDB1",
|
||||
schema: "CURRENT_USER",
|
||||
exportSchema: "APP_OWNER",
|
||||
exportTableName: "USERS",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not use a first source as the INSERT target of a multi-source query", async () => {
|
||||
mocks.getConfig.mockReturnValue({
|
||||
id: "oracle-1",
|
||||
name: "Oracle",
|
||||
db_type: "oracle",
|
||||
database: "ORCLPDB1",
|
||||
default_schema: "CURRENT_USER",
|
||||
query_timeout_secs: 30,
|
||||
});
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab("oracle-1", "ORCLPDB1", "Query", "query", "CURRENT_USER", "SELECT U.ID FROM APP_OWNER.USERS U JOIN APP_OWNER.ORDERS O ON O.USER_ID = U.ID");
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
tab.lastExecutedSql = tab.sql;
|
||||
tab.result = { columns: ["ID"], rows: [[1]], affected_rows: 0, execution_time_ms: 1 };
|
||||
tab.tableMeta = { schema: "APP_OWNER", tableName: "USERS", columns: [], primaryKeys: ["ID"] };
|
||||
tab.queryAnalysis = {
|
||||
schema: "APP_OWNER",
|
||||
tableName: "USERS",
|
||||
selectStar: false,
|
||||
multiSource: true,
|
||||
sources: [
|
||||
{ key: "USERS:0", schema: "APP_OWNER", tableName: "USERS" },
|
||||
{ key: "ORDERS:1", schema: "APP_OWNER", tableName: "ORDERS" },
|
||||
],
|
||||
columns: [{ sourceName: "ID", resultName: "ID", expression: "U.ID" }],
|
||||
};
|
||||
|
||||
const request = await store.buildQueryResultExportRequest(tabId, {
|
||||
exportId: "oracle-join-export",
|
||||
filePath: "query.sql",
|
||||
format: "sql",
|
||||
exportTableName: "USERS",
|
||||
exportSchema: "APP_OWNER",
|
||||
});
|
||||
|
||||
expect(request?.schema).toBe("CURRENT_USER");
|
||||
expect(request?.exportTableName).toBeUndefined();
|
||||
expect(request?.exportSchema).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses the Agent cursor for SQL Server legacy result export", async () => {
|
||||
mocks.getConfig.mockReturnValue({
|
||||
id: "sqlserver-2000",
|
||||
|
||||
@@ -210,6 +210,7 @@ interface BuildQueryResultExportRequestOptions {
|
||||
format: "csv" | "xlsx" | "json" | "txt" | "sql";
|
||||
includeSqlSheet?: boolean;
|
||||
exportTableName?: string;
|
||||
exportSchema?: string;
|
||||
exportColumnTypes?: Array<string | null | undefined>;
|
||||
exportColumnExtras?: Array<string | null | undefined>;
|
||||
insertMode?: SqlInsertMode;
|
||||
@@ -9620,6 +9621,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const rowLimit = settings.exportRowLimitEnabled ? settings.exportRowLimit : null;
|
||||
const totalRows = typeof tab.resultTotalRowCount === "number" ? (rowLimit === null ? tab.resultTotalRowCount : Math.min(tab.resultTotalRowCount, rowLimit)) : null;
|
||||
const clientSessionId = `${tabClientSessionId(tab, "export")}:${options.exportId}`;
|
||||
const hasUniqueInsertTarget = tab.tableMeta !== undefined && tab.queryAnalysis?.multiSource !== true && (tab.queryAnalysis?.sources?.length ?? 1) === 1 && (tab.queryWriteTargets?.length ?? 1) <= 1;
|
||||
|
||||
return {
|
||||
exportId: options.exportId,
|
||||
@@ -9645,7 +9647,8 @@ export const useQueryStore = defineStore("query", () => {
|
||||
clientSessionId,
|
||||
nullLiteral: csvNullLiteralForMode(settings.csvNullMode),
|
||||
executionId: uuid(),
|
||||
exportTableName: options.exportTableName,
|
||||
exportTableName: options.format === "sql" && hasUniqueInsertTarget ? options.exportTableName : undefined,
|
||||
exportSchema: options.format === "sql" && hasUniqueInsertTarget && options.exportTableName ? options.exportSchema : undefined,
|
||||
exportColumnTypes: options.exportColumnTypes,
|
||||
exportColumnExtras: options.exportColumnExtras,
|
||||
numericColumnRightAlign: settings.numericColumnRightAlign,
|
||||
|
||||
@@ -112,6 +112,9 @@ pub struct QueryResultExportRequest {
|
||||
// -- new fields for SQL INSERT export --
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub export_table_name: Option<String>,
|
||||
/// Explicit INSERT target schema; separate from `schema`, which scopes query execution.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub export_schema: Option<String>,
|
||||
/// Column type overrides for SQL INSERT export. Each entry may be `null`
|
||||
/// (meaning "infer from the query result") so the inner element is `Option`.
|
||||
/// Frontend sends these in original full-query column order.
|
||||
@@ -442,7 +445,7 @@ impl SqlInsertWriter {
|
||||
column_extras: Vec::new(),
|
||||
spatial_columns: Vec::new(),
|
||||
database_type: request.database_type,
|
||||
schema: request.schema.clone(),
|
||||
schema: request.export_schema.clone(),
|
||||
table_name,
|
||||
identifier_quote: request.identifier_quote.clone(),
|
||||
projection: None,
|
||||
@@ -2234,6 +2237,7 @@ mod tests {
|
||||
execution_id: None,
|
||||
date_time_format: None,
|
||||
export_table_name: Some("gen_table".to_string()),
|
||||
export_schema: Some("dbo".to_string()),
|
||||
export_column_types: Some(vec![Some("int".to_string()), Some("nvarchar(200)".to_string())]),
|
||||
selected_columns: None,
|
||||
export_column_extras,
|
||||
@@ -2325,6 +2329,7 @@ mod tests {
|
||||
execution_id: None,
|
||||
date_time_format: None,
|
||||
export_table_name: Some("users".to_string()),
|
||||
export_schema: Some("public".to_string()),
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -2413,6 +2418,7 @@ mod tests {
|
||||
csv_quote_mode: CsvQuoteMode::All,
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -2425,6 +2431,29 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oracle_sql_insert_uses_export_schema_not_query_execution_schema() {
|
||||
let dir = tempfile::tempdir().expect("temp dir");
|
||||
let destination = dir.path().join("users.sql");
|
||||
let mut req = request("sql", None, None);
|
||||
req.database_type = DatabaseType::Oracle;
|
||||
req.file_path = destination.to_string_lossy().into_owned();
|
||||
req.schema = Some("CURRENT_USER".to_string());
|
||||
req.export_table_name = Some("USERS".to_string());
|
||||
req.export_schema = Some("APP_OWNER".to_string());
|
||||
|
||||
let mut writer = SqlInsertWriter::create(&req).expect("create SQL writer");
|
||||
assert_eq!(writer.schema.as_deref(), Some("APP_OWNER"));
|
||||
writer.set_columns(vec!["ID".to_string()], &["NUMBER".to_string()], &[], &req).expect("set SQL export columns");
|
||||
writer.write_row(vec![serde_json::json!(1)], None).expect("write export row");
|
||||
writer.finish().expect("finish export");
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(destination).expect("read SQL export"),
|
||||
"INSERT INTO \"APP_OWNER\".\"USERS\" (\"ID\") VALUES (1);\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn rendered_sql_insert_output(mode: SqlInsertMode) -> String {
|
||||
let dir = tempfile::tempdir().expect("temp dir");
|
||||
let destination = dir.path().join("users.sql");
|
||||
|
||||
@@ -104,6 +104,7 @@ async fn live_clickhouse_query_result_export_xlsx_streams_random_order_query_onc
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
|
||||
@@ -479,6 +479,7 @@ async fn live_mysql_query_result_export_xlsx_streams_single_query_without_duplic
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -575,6 +576,7 @@ async fn live_mysql_csv_temporal_export_round_trip_preserves_dbx_force_text_valu
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -695,6 +697,7 @@ async fn live_mysql_xlsx_export_can_outlive_query_timeout_while_rows_keep_arrivi
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
|
||||
@@ -155,6 +155,7 @@ async fn live_postgres_query_result_export_uses_single_streamed_query() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -244,6 +245,7 @@ async fn live_postgres_query_result_xlsx_preserves_temporal_cell_types() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -322,6 +324,7 @@ async fn live_postgres_numeric_xlsx_ignores_fractional_trailing_zeros() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -405,6 +408,7 @@ async fn live_postgres_truncated_batch_result_export_replays_safe_temp_setup() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -432,6 +436,7 @@ async fn live_postgres_truncated_batch_result_export_replays_safe_temp_setup() {
|
||||
format: "xlsx".to_string(),
|
||||
client_session_id: Some(format!("temp-export-xlsx-{short_suffix}")),
|
||||
execution_id: Some(format!("temp-export-xlsx-{short_suffix}")),
|
||||
export_schema: None,
|
||||
..request
|
||||
};
|
||||
let xlsx_rows = AtomicU64::new(0);
|
||||
@@ -490,6 +495,7 @@ async fn live_postgres_xlsx_export_can_outlive_query_timeout_while_rows_keep_arr
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
@@ -566,6 +572,7 @@ async fn live_postgres_stream_still_times_out_without_progress_and_recovers() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
|
||||
@@ -1222,6 +1222,7 @@ async fn live_sqlserver_query_result_export_streams_cte_query_to_csv() {
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
|
||||
@@ -134,6 +134,7 @@ async fn live_sqlserver_xlsx_export_can_outlive_query_timeout_while_rows_keep_ar
|
||||
csv_quote_mode: Default::default(),
|
||||
null_literal: String::new(),
|
||||
export_table_name: None,
|
||||
export_schema: None,
|
||||
export_column_types: None,
|
||||
selected_columns: None,
|
||||
export_column_extras: None,
|
||||
|
||||
Reference in New Issue
Block a user