-
Notifications
You must be signed in to change notification settings - Fork 706
feat: support RETURNING in DML statements
#7094
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
Conversation
src/frontend/src/handler/query.rs
Outdated
| let rows_count = match stmt_type { | ||
| StatementType::SELECT => None, | ||
| let rows_count: Option<i32> = match stmt_type { | ||
| StatementType::SELECT | StatementType::INSERT_RETURNING | StatementType::DELETE_RETURNING | StatementType::UPDATE_RETURNING => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, in Postgres, we will get both returning rows and affected row count. We may fix this in the future.
postgres=# insert into t values (233) returning *;
v
-----
233
(1 row)
INSERT 0 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any ideas? 😰 cc @BowenXiao1999
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure, but this seems do not affect the correctness.
If the protocol allows, I think we can add the values in future. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have fixed!
BugenZhao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rest LGTM. Great job!
src/frontend/src/binder/insert.rs
Outdated
| pub returning_list: Vec<ExprImpl>, | ||
|
|
||
| pub schema: Schema, | ||
| pub returning_schema: Option<Schema>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding some brief doc for this? 🥰 Same for other occurrences.
| insert into t values (0,1), (1,2) returning *, a, a+b; | ||
| logical_plan: | | ||
| LogicalProject { exprs: [*VALUES*_0.column_0, *VALUES*_0.column_1, *VALUES*_0.column_0, (*VALUES*_0.column_0 + *VALUES*_0.column_1)] } | ||
| └─LogicalInsert { table: t } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll be better if we can add a new field of returning in fmt::Display. Only show it when returning is true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One problem is that current we do not hold returning projection info in LogicalInsert/Delete/Update struct but just a bool indicating if there were returning. Do you think it will be better if we print LogicalInsert { table: t, returning: true }?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LogicalInsert { table: t, returning: true } looks clear 🤣
src/frontend/src/handler/query.rs
Outdated
| let rows_count = match stmt_type { | ||
| StatementType::SELECT => None, | ||
| let rows_count: Option<i32> = match stmt_type { | ||
| StatementType::SELECT | StatementType::INSERT_RETURNING | StatementType::DELETE_RETURNING | StatementType::UPDATE_RETURNING => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree.
RETURNING in DML statements
Codecov Report
@@ Coverage Diff @@
## main #7094 +/- ##
==========================================
- Coverage 72.33% 71.66% -0.67%
==========================================
Files 1071 1083 +12
Lines 171769 173589 +1820
==========================================
+ Hits 124243 124401 +158
- Misses 47526 49188 +1662
Flags with carried forward coverage won't be shown. Click here to find out more.
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
|
Have checked and rebased! Ready to be reviewed! |
BowenXiao1999
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So now, we are returning the columns for the RETURNING clause and no rows count?
Rest LGTM
src/batch/src/executor/insert.rs
Outdated
| } | ||
| // } else { | ||
| // let ret_chunk = DataChunk::new(returning_columns, rows_inserted); | ||
| // yield ret_chunk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
| pub fn new(input: PlanRef, table_name: String, table_id: TableId) -> Self { | ||
| pub fn new(input: PlanRef, table_name: String, table_id: TableId, returning: bool) -> Self { | ||
| let ctx = input.ctx(); | ||
| // TODO: support `RETURNING`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like it is already fully supported, can we remove it?
| | StatementType::INSERT_RETURNING | ||
| | StatementType::DELETE_RETURNING | ||
| | StatementType::UPDATE_RETURNING |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a question, as we treat XXX_RETURNING as a query. Once they are used in the CTE and the CTE is used multiple times in a single query, can we reuse the CTE result? Reuse means we execute the query once, while not reuse means we execute the query multiple times and then the dml will be executed multiple times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! So we must "share" this Insert and support DAG execution in batch as well? 🥵 Luckily, the parser will reject RETURNING in CTE now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, we need to make sure the dml of the RETURNING in CTE will be executed exactly once. That's the PG behavior.
| insert into t values (0,1), (1,2) returning *, a, a+b; | ||
| logical_plan: | | ||
| LogicalProject { exprs: [*VALUES*_0.column_0, *VALUES*_0.column_1, *VALUES*_0.column_0, (*VALUES*_0.column_0 + *VALUES*_0.column_1)] } | ||
| └─LogicalInsert { table: t } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LogicalInsert { table: t, returning: true } looks clear 🤣
src/batch/src/executor/update.rs
Outdated
| let data_types = self.child.schema().data_types(); | ||
| let mut builder = DataChunkBuilder::new(data_types, self.chunk_size); | ||
| let mut builder = DataChunkBuilder::new(data_types.clone(), self.chunk_size); | ||
| let mut updated_builder = DataChunkBuilder::new(data_types.clone(), self.chunk_size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some ideas: the Update with returning should behave like a Project, which does not "buffer" or reorganize the chunks with a builder. 🤔 In this sense, should we directly yield every updated chunk in the execution loop?
…nstead of previous INSERT_RETURNING
Have added some. Those tests would pass should this change to sqllogictest be merged. |
BugenZhao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rest LGTM. Good work!!
| "{} {{ table: {} }}, {{ returning: {} }}", | ||
| name, self.table_name, self.returning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should only be one pair of brackets, like Rust's Debug. Same for other occurrences.
| "{} {{ table: {} }}, {{ returning: {} }}", | |
| name, self.table_name, self.returning | |
| "{} {{ table: {}{} }}", | |
| name, self.table_name, if self.returning { ", returning: true" } else { "" } |
src/utils/pgwire/src/pg_protocol.rs
Outdated
| || lower_sql.starts_with("with") | ||
| || lower_sql.starts_with("describe") | ||
| || lower_sql.starts_with("explain") | ||
| || lower_sql.contains("returning") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this be too hacky? But as we make 🤔 cc @BowenXiao1999RETURNING a reserved word and do not support RETURNING in CTE, it seems okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
directly operating on str is always hacky. For example, I guess insert into t values ('returning') will be bad for this case. starts_with looks no bad. Anyway I don't quite like process str directly before use Parser, but it's seems like non-trival to avoid. let's see whether we can remove all is_query_sql in future (by introduce Parser AST?, not sure).
cc @xxchan not sure whether has better ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reminds me of #6849 (comment) 🤪 So we are really going to introduce parser in pgwire..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
directly operating on str is always hacky.
+1. For sqllogictest, this is good enough. But in pgwire, we should be more careful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I revert this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think we can revert these changes that is not must-required in this pr, and add some comments to explain that it miss the ability to process Returning keywords.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure!
Remember to bump the docker image version and sqllogictest version after you do this. 🥰 risingwave/ci/build-ci-image.sh Lines 13 to 17 in 3a8f9d5
Line 31 in 3a8f9d5
|
|
e2e tests have been commented out (blocked by #7277). This pr should be able to merge! |
Signed-off-by: Eurekaaw <[email protected]>
Signed-off-by: Eurekaaw <[email protected]>
Signed-off-by: Bugen Zhao <[email protected]>
Signed-off-by: Eurekaaw <[email protected]>
BugenZhao
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may also need to bump the sqllogictest library version.
| sqllogictest = "0.9" |
| batch_plan: | | ||
| BatchExchange { order: [], dist: Single } | ||
| └─BatchInsert { table: t } | ||
| └─BatchInsert { table: t } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the trailing space here. Same for Update and Delete.
| insert into t values (timestamp '2020-01-01 01:02:03'), (time '03:04:05'); | ||
| batch_plan: | | ||
| BatchExchange { order: [], dist: Single } | ||
| <<<<<<< HEAD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please resolve the conflicts.
Signed-off-by: Eurekaaw <[email protected]>
Signed-off-by: Eurekaaw <[email protected]>
Signed-off-by: Eurekaaw <[email protected]>
I hereby agree to the terms of the Singularity Data, Inc. Contributor License Agreement.
What's changed and what's your intention?
This section will be used as the commit message. Please do not leave this empty!
Please explain IN DETAIL what the changes are in this PR and why they are needed:
returningfield in DML related structs (INSERT,UPDATE, andDELETE)returningincluded.run_sqlwill return an extraboolto indicate if the statement is a DML with returning, used by query handler.Checklist
./risedev check(or alias,./risedev c)Types of user-facing changes
Please keep the types that apply to your changes, and remove those that do not apply.
Refer to a related PR or issue link (optional)
#7044