callicles opened a new pull request, #2154: URL: https://github.com/apache/datafusion-sqlparser-rs/pull/2154
## feat: Add dialect-aware SQL serialization with `ToSql` trait for ClickHouse PascalCase types ### Summary This PR introduces a new `ToSql` trait that enables dialect-aware SQL serialization, specifically addressing ClickHouse's requirement for PascalCase type names (e.g., `String`, `Int64`, `Nullable(String)`) instead of the standard uppercase convention (`STRING`, `INT64`). **Related Issue:** https://github.com/apache/datafusion-sqlparser-rs/issues/2153 ### Problem ClickHouse is case-sensitive for data type names and requires PascalCase. When using `sqlparser-rs` to parse and regenerate SQL: ```rust let sql = "CREATE TABLE t (col String)"; let ast = Parser::parse_sql(&dialect, sql)?; let regenerated = format!("{}", ast[0]); // Produces "CREATE TABLE t (col STRING)" ``` The regenerated SQL fails with `UNKNOWN_TYPE` errors when executed against ClickHouse because `STRING` is not recognized—only `String` is valid. ### Solution Introduce a `ToSql` trait that accepts dialect context: ```rust pub trait ToSql { fn to_sql(&self, dialect: &dyn Dialect) -> String; fn write_sql(&self, f: &mut dyn Write, dialect: &dyn Dialect) -> fmt::Result; } ``` Usage: ```rust use sqlparser::ast::ToSql; let sql = "CREATE TABLE t (col String)"; let ast = Parser::parse_sql(&ClickHouseDialect {}, sql)?; let regenerated = ast[0].to_sql(&ClickHouseDialect {}); // Produces: "CREATE TABLE t (col String)" ✓ ``` ### Design Decisions 1. **Coexistence with `Display`**: The existing `Display` trait continues to work unchanged (uppercase types). `ToSql` is opt-in for users who need dialect-aware formatting. 2. **`impl_to_sql_display!` macro**: Types without `DataType` fields delegate to their `Display` implementation via macro, avoiding code duplication. 3. **Recursive propagation**: Types containing `DataType` fields implement `write_sql` explicitly, calling `write_sql` on nested types to propagate dialect context through the AST. 4. **`requires_pascalcase_types()` dialect method**: A new method on the `Dialect` trait that returns `true` for ClickHouse. ### Changes | File | Description | |------|-------------| | `src/ast/to_sql.rs` | New module with `ToSql` trait, `impl_to_sql_display!` macro, and helpers | | `src/ast/data_type.rs` | `ToSql` impl for `DataType` with PascalCase formatting | | `src/ast/mod.rs` | `ToSql` impls for `Expr`, `Statement`, `Function`, window types, etc. | | `src/ast/ddl.rs` | `ToSql` impls for `CreateTable`, `AlterTable`, `ColumnDef`, etc. | | `src/ast/query.rs` | `ToSql` impls for `Query`, `Select`, `TableFactor`, etc. | | `src/dialect/mod.rs` | Add `requires_pascalcase_types()` to `Dialect` trait | | `src/dialect/clickhouse.rs` | Override `requires_pascalcase_types()` to return `true` | | `tests/sqlparser_clickhouse.rs` | Comprehensive tests including round-trip verification | ### Coverage `ToSql` is implemented for all major AST types users are likely to serialize: - **Statements**: `Statement`, `Query`, `Select`, `CreateTable`, `AlterTable`, `CreateView`, `CreateFunction`, `Declare`, `Prepare` - **Expressions**: `Expr` (all variants including `Cast`, `Case`, `BinaryOp`, `Function`, etc.) - **Types**: `DataType`, `ColumnDef`, `ViewColumnDef`, `StructField`, `UnionField` - **Query components**: `TableFactor`, `Join`, `OrderByExpr`, `WindowSpec`, `Cte`, `With` - **Functions**: `Function`, `FunctionArg`, `FunctionArguments`, window functions ### Testing Added 8 new tests covering: - Basic `DataType` PascalCase formatting - `CREATE TABLE` with nested types (`Nullable`, `Array`, `Map`) - `ALTER TABLE` operations - `SELECT` with `CAST` expressions - Complex expressions (`CASE`, nested `CAST`) - `CREATE VIEW` statements - **Round-trip test**: parse → `to_sql` → parse → verify equivalence ``` test result: ok. 55 passed; 0 failed (ClickHouse tests) ``` ### Migration Guide If you currently use `Display` and need dialect-aware formatting: ```rust // Before let sql = format!("{}", statement); // After use sqlparser::ast::ToSql; let sql = statement.to_sql(&dialect); ``` ### Breaking Changes None. This is a purely additive change. Existing code using `Display` continues to work unchanged. Disclaimer: This was worked on with AI -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
