Skip to content

Commit f6e2b4e

Browse files
committed
WIP: ODBC
1 parent 41a8b99 commit f6e2b4e

3 files changed

Lines changed: 21 additions & 9 deletions

File tree

src/execute/casting.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ pub fn determine_layer_source(
216216
match &layer.source {
217217
Some(DataSource::Identifier(name)) => {
218218
if materialized_ctes.contains(name) {
219-
naming::cte_table(name)
219+
naming::quote_ident(&naming::cte_table(name))
220220
} else {
221221
name.clone()
222222
}
@@ -227,7 +227,7 @@ pub fn determine_layer_source(
227227
None => {
228228
// Layer uses global data - caller must ensure has_global is true
229229
debug_assert!(has_global, "Layer has no source and no global data");
230-
naming::global_table()
230+
naming::quote_ident(&naming::global_table())
231231
}
232232
}
233233
}

src/execute/cte.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub fn transform_cte_references(sql: &str, cte_names: &HashSet<String>) -> Strin
9494
let mut result = sql.to_string();
9595

9696
for cte_name in cte_names {
97-
let temp_table_name = naming::cte_table(cte_name);
97+
let temp_table_name = naming::quote_ident(&naming::cte_table(cte_name));
9898

9999
// Replace table references: FROM cte_name, JOIN cte_name, cte_name.column
100100
// Use word boundary matching to avoid replacing substrings
@@ -360,26 +360,26 @@ mod tests {
360360
(
361361
"SELECT * FROM sales WHERE year = 2024",
362362
vec!["sales"],
363-
vec!["FROM __ggsql_cte_sales_", "__ WHERE year = 2024"],
363+
vec!["FROM \"__ggsql_cte_sales_", "__\" WHERE year = 2024"],
364364
None,
365365
),
366366
// Multiple CTE references with qualified columns
367367
(
368368
"SELECT sales.date, targets.revenue FROM sales JOIN targets ON sales.id = targets.id",
369369
vec!["sales", "targets"],
370370
vec![
371-
"FROM __ggsql_cte_sales_",
372-
"JOIN __ggsql_cte_targets_",
373-
"__ggsql_cte_sales_", // qualified reference sales.date
374-
"__ggsql_cte_targets_", // qualified reference targets.revenue
371+
"FROM \"__ggsql_cte_sales_",
372+
"JOIN \"__ggsql_cte_targets_",
373+
"\"__ggsql_cte_sales_", // qualified reference sales.date
374+
"\"__ggsql_cte_targets_", // qualified reference targets.revenue
375375
],
376376
None,
377377
),
378378
// Qualified column references only (no FROM/JOIN transformation needed)
379379
(
380380
"WHERE sales.date > '2024-01-01' AND sales.revenue > 100",
381381
vec!["sales"],
382-
vec!["__ggsql_cte_sales_"],
382+
vec!["\"__ggsql_cte_sales_"],
383383
None,
384384
),
385385
// No matching CTE (unchanged)

src/naming.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ pub const SOURCE_COLUMN: &str = concatcp!(GGSQL_PREFIX, "source", GGSQL_SUFFIX);
8888
/// Alias for schema extraction queries
8989
pub const SCHEMA_ALIAS: &str = concatcp!(GGSQL_SUFFIX, "schema", GGSQL_SUFFIX);
9090

91+
// ============================================================================
92+
// Quoting
93+
// ============================================================================
94+
95+
/// Quote a SQL identifier with double quotes (ANSI SQL).
96+
///
97+
/// This ensures case-preserving behavior on backends like Snowflake that
98+
/// uppercase unquoted identifiers.
99+
pub fn quote_ident(name: &str) -> String {
100+
format!("\"{}\"", name.replace('"', "\"\""))
101+
}
102+
91103
// ============================================================================
92104
// Constructor Functions
93105
// ============================================================================

0 commit comments

Comments
 (0)