-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mismatch in MemTable (Select Into with aggregate window functions having no alias) #6492
Comments
@alamb, when you have some time, could you please review my suggestions? I wonder about your thoughts on how to solve this issue. |
Hi @berkaysynnada -- Here is a small reproducer: ❯ create table foo (x int) as values (1);
0 rows in set. Query took 0.001 seconds.
❯ explain select first_value(x) OVER () INTO bar from foo ;
+--------------+------------------------------------------------------------------------------------------------------------+
| plan_type | plan |
+--------------+------------------------------------------------------------------------------------------------------------+
| logical_plan | CreateMemoryTable: Bare { table: "bar" } |
| | Projection: FIRST_VALUE(foo.x) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
| | WindowAggr: windowExpr=[[FIRST_VALUE(foo.x) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING]] |
| | TableScan: foo projection=[x] |
+--------------+------------------------------------------------------------------------------------------------------------+
1 row in set. Query took 0.001 seconds.
❯ select first_value(x) OVER () INTO bar from foo ;
Error during planning: Mismatch between schema and batches I wonder if you can solve this with an explicit alias rather than changing how the display names work. So when the
For the record, here is what postgres does: postgres=# select first_value(x) OVER () INTO bar from foo ;
SELECT 1
postgres=# select * from bar;
first_value
-------------
1
(1 row) |
Another similar case of failure: DataFusion CLI v25.0.0
❯ create table t (a int not null);
0 rows in set. Query took 0.004 seconds.
❯ insert into t values(1);
Error during planning: Inserting query must have the same schema with the table. Two non-matching schemas are: Can we ignore the schema part of batches and only focus on the actual data part? impl MemTable {
/// Create a new in-memory table from the provided schema and record batches
pub fn try_new(schema: SchemaRef, partitions: Vec<Vec<RecordBatch>>) -> Result<Self> {
let mut batches = Vec::with_capacity(partitions.len());
for partition in partitions {
let new_partition = partition
.iter()
.map(|batch| {
RecordBatch::try_new(schema.clone(), batch.columns().to_vec())
.map_err(DataFusionError::ArrowError)
})
.collect::<Result<Vec<_>>>()?;
batches.push(Arc::new(RwLock::new(new_partition)));
}
Ok(Self { schema, batches })
}
} |
I think the proper solution is to update the planner code to project (e.g. with a In the example #6492 (comment) from @byteink the table's schema says "it can't have nulls" (because it is marked as non-nullable) so in my opinion DataFusion is correctly rejecting this plan I think this was not previously a problem because MemoryTables could not be updated so if the initial contents didn't contain nulls, they never would contain nulls (and thus that could be reflected in the schema) Now that we an add new data to MemTables, what do you think about ensuring the schema of the MemTable when created via |
I think there may be two issues that need to be considered. 1. What is the schema of a table created with initial contents (particularly the behavior of nullability)MySQLQuery expressions and column definitions can appear together in the create table statement, and MySQL will check if there is a conflict between the two.
PostgreSQL
Datafusion
I think it is ok, just like PostgreSQL. But we should reject the following statement:
The user specified that the column should not be nullable, but this specification did not take effect. 2. What kind of data can be inserted into MemoryTableI think using the comparison of whether the two schemas are the same may be too strict. The following example can run successfully in PostgreSQL, but not in Datafusion.
We should verify the resulting data after executing the input's execution plan. |
In |
Describe the bug
When writing query result to MemTable using
SELECT .. INTO
syntax, some queries gives error because of schema mismatch. As an example,SELECT SUM(c1) OVER(ORDER BY c1) as sum1 INTO new_table FROM annotated_data_infinite
has no problem but
SELECT SUM(c1) OVER(ORDER BY c1) INTO new_table FROM annotated_data_infinite
gives an error: Plan("Mismatch between schema and batches").
The reason is that in
MemTable::try_new()
, the schema and partitions' schema don't match. I have tracked the reason and saw that the schema, which is created from the input LogicalPlan, has fields whose names are the result of display_name() (It writes the whole expression, func + window specs). However, the RecordBatch's fields of partitions are the result of physical_name(), in case of no alias. (It writes only the function part of the expr).To Reproduce
SELECT SUM(c1) OVER(ORDER BY c1) INTO new_table FROM annotated_data_infinite
Expected behavior
I have 2 solution approaches:
create_window_expr()
gives the name withdisplay_name()
while constructing the window expr. However, there needs to be many changes in tests, and the exec lines will become too long like:ProjectionExec: expr=[c2@0 as c2, MAX(aggregate_test_100.c9) ORDER BY [aggregate_test_100.c2 ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@3 as MAX(aggregate_test_100.c9) ORDER BY [aggregate_test_100.c2 ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, SUM(aggregate_test_100.c9) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING@4 as SUM(aggregate_test_100.c9) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, MIN(aggregate_test_100.c9) ORDER BY [aggregate_test_100.c2 ASC NULLS LAST, aggregate_test_100.c9 ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@2 as MIN(aggregate_test_100.c9) ORDER BY [aggregate_test_100.c2 ASC NULLS LAST, aggregate_test_100.c9 ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]
Maybe Display's can be changed?
contains()
function implemented forSchema
is used in try_new() of MemTable to match fields, and it usescontains()
implemented forField
. It checks one-to-one equalities of all elements in the Field struct. Just for the name element, we can reduce the equality to something likeschema_field.name().starts_with(partition_field.name())
. If we do not prefer changingcontains()
function, maybe we can write some specialized function likeBut this approach also does not seem solid to me.
Additional context
Any advice is welcomed. I will solve the issue when we reach a common ground.
The text was updated successfully, but these errors were encountered: