The great Box-ification experiment - #2592
LucaCappelletti94 wants to merge 1 commit into
Conversation
|
No dialect label was applied because the title does not name a SQL dialect. If this change targets one or more dialects, edit the title to Changed files touch BigQuery, ClickHouse, Databricks, DuckDB, Hive, MySQL, Oracle, PostgreSQL, Snowflake, SQL Server, SQLite. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2592 +/- ##
==========================================
+ Coverage 81.09% 81.14% +0.05%
==========================================
Files 42 42
Lines 33684 33881 +197
Branches 33684 33881 +197
==========================================
+ Hits 27316 27493 +177
- Misses 2798 2818 +20
Partials 3570 3570 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
c83b9d5 to
57c822b
Compare
|
I haven't looked at the PR, but the basic idea sounds reasonable COncerns:
I think as long as we have benchmarks that show this doesn't cause (too much) performance regression and a clear upgrade guide it would be good |
The gist is what you imagine, box-ifying all entries in the statements enum, fixing the associated code. My goal here was primarily to measure how big of an impact the change would have.
This is most likely to be the pain point:
I was expecting a slow down, but the size of the enum is currently so massive in my 12k statements corpus it accelerated by 10%. Out of caution, I will prepare another benchmark based on https://sql-ast-benchmark.luca.phd/ corpus and get back to you with those results. |
|
Downstream code can keep destructuring payloads on stable Rust if impl Statement {
pub fn as_create_table(&self) -> Option<&CreateTable> {
match self {
Self::CreateTable(create_table) => Some(create_table),
_ => None,
}
}
pub fn into_create_table(self) -> Option<CreateTable> {
match self {
Self::CreateTable(create_table) => Some(*create_table),
_ => None,
}
}
}match pg_and_generic().one_statement_parses_to(sql, "").into_create_table() {
Some(CreateTable {
name,
columns,
constraints,
table_options,
if_not_exists: false,
external: false,
file_format: None,
location: None,
..
}) => { /* assertions unchanged */ }
_ => unreachable!(),
}A An idea that could be viable, is to create a macro that generates BOTH the I will play with this last idea and tell you whether anything nice pans out of it. The idea of a compile-time typed restricted parser output feels very Rust-y to me, and I suspect it could be done. |
As @xitep pointed out back in #2218 the
Statementenum is very large, back then about 2KB, and now has surpassed 3KB on main becauseCreateTableand the other large payloads are stored inline. Every slot of theVec<Statement>the parser returns pays that size, and so does every query body, sinceSetExprholds aStatementinline forInsert,Update,DeleteandMergeand eachBox<SetExpr>therefore allocates 3440 bytes,SELECT 1included. Parsing a script of 12K short statements retains 109 MB.This PR puts 139 of the 140 data-carrying variants behind a
Box.Statementdrops to 24 bytes andSetExprto 32, thelarge_enum_variantallow is gone and a test keepsStatementat 24 bytes or less.Retained memory falls 54% on the 12K statements script, parse time on statement-heavy scripts drops ~10%, and single-statement parses are about 7% faster.
Now, I understand that reviewing this as a +5k/-5k line is nearly impossible, so if you agree that it is actually desirable to proceed with this non-trivial refactoring which will definitely break code downstream (I checked that in DataFusion 23 of 35 sites need an update, all in
datafusion/sql), I will proceed to turn it into a very long stacked PR which can then be processed a statement at the time.If you reckon that it is an excessive API break and the gains are insufficient, I will close the PR.
Refactoring drafted with Opus 5.5
cc @iffyio @alamb