diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index bae692a57955..c3a80429ee84 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -33,3 +33,6 @@ f8266748dcb70541da944664552c1944ff8362e4 # feat(risedev): add check for trailing spaces in `risedev check` (#11294) f2a3fd021059e680b35b24c63cff5f8dbe9f9d5f + +# chore(rustfmt): format let-chains and let-else #9409 +d70dba827c303373f3220c9733f7c7443e5c2d37 \ No newline at end of file diff --git a/Makefile.toml b/Makefile.toml index 0c2a99351d55..4c01da30376b 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1313,9 +1313,12 @@ echo "All processes has exited." [tasks.slt] category = "RiseDev - SQLLogicTest" -install_crate = { version = "0.17.0", crate_name = "sqllogictest-bin", binary = "sqllogictest", test_arg = [ +install_crate = { version = "0.17.1", crate_name = "sqllogictest-bin", binary = "sqllogictest", test_arg = [ "--help", ], install_command = "binstall" } +dependencies = ["check-risedev-env-file"] +env_files = ["${PREFIX_CONFIG}/risedev-env"] +env = { SLT_HOST = "${RW_FRONTEND_LISTEN_ADDRESS}", SLT_PORT = "${RW_FRONTEND_PORT}", SLT_DB = "dev" } command = "sqllogictest" args = ["${@}"] description = "🌟 Run SQLLogicTest" @@ -1418,3 +1421,15 @@ cargo run -p risingwave_common --bin example-config >> src/config/example.toml category = "RiseDev - Backwards Compatibility Test" description = "Run backwards compatibility test" script = "./backwards-compat-tests/scripts/run_local.sh" + +# For debugging. +# To show the env for a specific task, use `run_task = "show-env"` for that task. +[tasks.show-env] +private = true +description = "Show cargo-make runtime environment variables" +script = """ +#!@duckscript +# https://github.com/sagiegurari/cargo-make/issues/889 +vars = dump_variables +echo ${vars} +""" diff --git a/e2e_test/batch/basic/cte.slt.part b/e2e_test/batch/basic/cte.slt.part index 8d1858ac0cd3..7bdd3407baa6 100644 --- a/e2e_test/batch/basic/cte.slt.part +++ b/e2e_test/batch/basic/cte.slt.part @@ -65,3 +65,14 @@ drop table t1; statement ok drop table t2; + +# more tests for alias https://github.com/risingwavelabs/risingwave/issues/12526 +query I +with cte as (select 1) select x from cte t(x); +---- +1 + +query I +with cte(a) as (select 1,2) select x,y from cte t(x,y); +---- +1 2 diff --git a/e2e_test/batch/basic/lookup_join.slt.part b/e2e_test/batch/basic/lookup_join.slt.part index e653b3e2000a..400c35068d69 100644 --- a/e2e_test/batch/basic/lookup_join.slt.part +++ b/e2e_test/batch/basic/lookup_join.slt.part @@ -47,6 +47,13 @@ select * from t1 right join t3 on t1.v1 = t3.v1 order by t1.v1, t1.v2; 1 4 1 2 NULL NULL 4 1 +# A bug in https://github.com/risingwavelabs/risingwave/pull/12527 +# lookup join panics when the scan is single distribution +query I +select 1 from (select * from t1 left join t3 on t1.v1 = t3.v1 order by t1.v1, t1.v2) where exists (select 1) limit 1; +---- +1 + statement ok drop materialized view t3; diff --git a/e2e_test/batch/duckdb/all.slt.part b/e2e_test/batch/duckdb/all.slt.part index 81ed19074945..68adce73dea2 100644 --- a/e2e_test/batch/duckdb/all.slt.part +++ b/e2e_test/batch/duckdb/all.slt.part @@ -6,3 +6,4 @@ include ./conjunction/*.slt.part include ./conjunction/*/*.slt.part include ./limit/*.slt.part include ./select/*.slt.part +include ./cte/*.slt.part diff --git a/e2e_test/batch/duckdb/cte/insert_cte_bug_3417.test.slt.part b/e2e_test/batch/duckdb/cte/insert_cte_bug_3417.test.slt.part new file mode 100644 index 000000000000..d4a6a955caf6 --- /dev/null +++ b/e2e_test/batch/duckdb/cte/insert_cte_bug_3417.test.slt.part @@ -0,0 +1,18 @@ +# name: test/sql/cte/insert_cte_bug_3417.test +# description: Test for a crash reported in issue #3417 +# group: [cte] + +statement ok +CREATE TABLE table1 (id INTEGER, a INTEGER); + +statement ok +CREATE TABLE table2 (table1_id INTEGER); + +statement error +INSERT INTO table2 WITH cte AS (INSERT INTO table1 SELECT 1, 2 RETURNING id) SELECT id FROM cte; + +statement ok +DROP TABLE table1; + +statement ok +DROP TABLE table2; diff --git a/e2e_test/batch/duckdb/cte/test_bug_922.test.slt.part b/e2e_test/batch/duckdb/cte/test_bug_922.test.slt.part new file mode 100644 index 000000000000..371fc7b5b8ef --- /dev/null +++ b/e2e_test/batch/duckdb/cte/test_bug_922.test.slt.part @@ -0,0 +1,8 @@ +# name: test/sql/cte/test_bug_922.test +# description: Test for a crash reported in issue #922 +# group: [cte] + +query I +WITH my_list(value) AS (VALUES (1), (2), (3)) + SELECT * FROM my_list LIMIT 0 OFFSET 1 +---- diff --git a/e2e_test/batch/duckdb/cte/test_cte.test.slt.part b/e2e_test/batch/duckdb/cte/test_cte.test.slt.part new file mode 100644 index 000000000000..4e073e8ebcac --- /dev/null +++ b/e2e_test/batch/duckdb/cte/test_cte.test.slt.part @@ -0,0 +1,145 @@ +# name: test/sql/cte/test_cte.test +# description: Test Common Table Expressions (CTE) +# group: [cte] + +statement ok +SET RW_IMPLICIT_FLUSH TO TRUE; + +statement ok +create table a(i integer); + +statement ok +insert into a values (42); + +query I +with cte1 as (Select i as j from a) select * from cte1; +---- +42 + +query I +with cte1 as (Select i as j from a) select x from cte1 t1(x); +---- +42 + +query I +with cte1(xxx) as (Select i as j from a) select xxx from cte1; +---- +42 + +query I +with cte1(xxx) as (Select i as j from a) select x from cte1 t1(x); +---- +42 + +query II +with cte1 as (Select i as j from a), cte2 as (select ref.j as k from cte1 as ref), cte3 as (select ref2.j+1 as i from cte1 as ref2) select * from cte2 , cte3; +---- +42 43 + +query I rowsort +with cte1 as (select i as j from a), cte2 as (select ref.j as k from cte1 as ref), cte3 as (select ref2.j+1 as i from cte1 as ref2) select * from cte2 union all select * FROM cte3; +---- +42 +43 + + +# FIXME: this should be an error +# duplicate CTE alias +query I +with cte1 as (select 42), cte1 as (select 43) select * FROM cte1; +---- +43 + +# reference to CTE before its actually defined +# duckdb is ok +# postgres: query failed: db error: ERROR: relation "cte1" does not exist +# DETAIL: There is a WITH item named "cte1", but it cannot be referenced from this part of the query. +# HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. +query error table or source not found: cte1 +with cte3 as (select ref2.j as i from cte1 as ref2), cte1 as (Select i as j from a), cte2 as (select ref.j+1 as k from cte1 as ref) select * from cte2 union all select * FROM cte3; + + +# multiple uses of same CTE +query II +with cte1 as (Select i as j from a) select * from cte1 cte11, cte1 cte12; +---- +42 42 + +# refer to CTE in subquery +query I +with cte1 as (Select i as j from a) select * from cte1 where j = (select max(j) from cte1 as cte2); +---- +42 + +# multi-column name alias +query II +with cte1(x, y) as (select 42 a, 84 b) select zzz, y from cte1 t1(zzz); +---- +42 84 + +# use a CTE in a view definition +statement ok +create view va AS (with cte as (Select i as j from a) select * from cte); + +query I +select * from va +---- +42 + +# nested CTE views that re-use CTE aliases +query I +with cte AS (SELECT * FROM va) SELECT * FROM cte; +---- +42 + +# multiple ctes in a view definition +statement ok +create view vb AS (with cte1 as (Select i as j from a), cte2 as (select ref.j+1 as k from cte1 as ref) select * from cte2); + +query I +select * from vb +---- +43 + +# cte in set operation node +query I +SELECT 1 UNION ALL (WITH cte AS (SELECT 42) SELECT * FROM cte); +---- +1 +42 + +# # cte in recursive cte +# query I +# WITH RECURSIVE cte(d) AS ( +# SELECT 1 +# UNION ALL +# (WITH c(d) AS (SELECT * FROM cte) +# SELECT d + 1 +# FROM c +# WHERE FALSE +# ) +# ) +# SELECT max(d) FROM cte; +# ---- +# 1 + +# test CTE with nested aliases in where clause +# Note: postgres doesn't support this: column "alias1" does not exist +query error failed to bind expression: alias1 +with cte (a) as ( + select 1 +) +select + a as alias1, + alias1 as alias2 +from cte +where alias2 > 0; + +statement ok +drop view vb; + +statement ok +drop view va; + +statement ok +drop table a; diff --git a/e2e_test/batch/duckdb/cte/test_cte_in_cte.test.slt.part b/e2e_test/batch/duckdb/cte/test_cte_in_cte.test.slt.part new file mode 100644 index 000000000000..08c31f2b40f7 --- /dev/null +++ b/e2e_test/batch/duckdb/cte/test_cte_in_cte.test.slt.part @@ -0,0 +1,57 @@ +# name: test/sql/cte/test_cte_in_cte.test +# description: Test Nested Common Table Expressions (CTE) +# group: [cte] + +statement ok +SET RW_IMPLICIT_FLUSH TO TRUE; + +statement ok +create table a(i integer); + +statement ok +insert into a values (42); + +query I +with cte1 as (Select i as j from a) select * from cte1; +---- +42 + +query I +with cte1 as (with b as (Select i as j from a) Select j from b) select x from cte1 t1(x); +---- +42 + +query I +with cte1(xxx) as (with ncte(yyy) as (Select i as j from a) Select yyy from ncte) select xxx from cte1; +---- +42 + +query II +with cte1 as (with b as (Select i as j from a) select j from b), cte2 as (with c as (select ref.j+1 as k from cte1 as ref) select k from c) select * from cte1 , cte2; +---- +42 43 + +# refer to CTE in subquery tableref +query I +with cte1 as (Select i as j from a) select * from (with cte2 as (select max(j) as j from cte1) select * from cte2) f +---- +42 + +# refer to CTE in subquery expression +query I +with cte1 as (Select i as j from a) select * from cte1 where j = (with cte2 as (select max(j) as j from cte1) select j from cte2); +---- +42 + +# refer to same-named CTE in a subquery expression +query I +with cte as (Select i as j from a) select * from cte where j = (with cte as (select max(j) as j from cte) select j from cte); +---- +42 + +# self-refer to non-existent cte +statement error +with cte as (select * from cte) select * from cte + +statement ok +drop table a; diff --git a/e2e_test/batch/duckdb/cte/test_cte_overflow.test.slt.part b/e2e_test/batch/duckdb/cte/test_cte_overflow.test.slt.part new file mode 100644 index 000000000000..4d27a9c7a3df --- /dev/null +++ b/e2e_test/batch/duckdb/cte/test_cte_overflow.test.slt.part @@ -0,0 +1,26 @@ +# name: test/sql/cte/test_cte_overflow.test +# description: Ensure no stack overflow for CTE names that match existing tables +# group: [cte] + +statement ok +SET RW_IMPLICIT_FLUSH TO TRUE; + +statement ok +create table a (id integer) + +statement ok +insert into a values (1729) + +statement ok +create view va as (with v as (select * from a) select * from v) + +query I +with a as (select * from va) select * from a +---- +1729 + +statement ok +drop view va; + +statement ok +drop table a; diff --git a/e2e_test/batch/duckdb/cte/test_issue_5673.test.slt.part b/e2e_test/batch/duckdb/cte/test_issue_5673.test.slt.part new file mode 100644 index 000000000000..6fa4c3d8c23d --- /dev/null +++ b/e2e_test/batch/duckdb/cte/test_issue_5673.test.slt.part @@ -0,0 +1,54 @@ +# name: test/sql/cte/test_issue_5673.test +# description: Issue #5673 and #4987: CTE and Table name are name shadowing +# group: [cte] + +statement ok +SET RW_IMPLICIT_FLUSH TO TRUE; + +statement ok +create table orders(ordered_at int); + +statement ok +create table stg_orders(ordered_at int); + +statement ok +insert into orders values (1); + +statement ok +insert into stg_orders values (1); + +# Note: postgres succeeds. +# duckdb returns Binder Error: Circular reference to CTE "orders", There are two possible solutions. +query ok +with +orders as ( + select * from stg_orders + where ordered_at >= (select max(ordered_at) from orders) +), +some_more_logic as ( + select * + from orders +) +select * from some_more_logic; +---- +1 + +query I +with +orders as ( + select * from public.stg_orders + where ordered_at >= (select max(ordered_at) from public.orders) +), +some_more_logic as ( + select * + from orders +) +select * from some_more_logic; +---- +1 + +statement ok +drop table orders; + +statement ok +drop table stg_orders; diff --git a/src/frontend/planner_test/tests/testdata/input/limit.yaml b/src/frontend/planner_test/tests/testdata/input/limit.yaml index 31344d6b4dc1..0d15a5de6cd1 100644 --- a/src/frontend/planner_test/tests/testdata/input/limit.yaml +++ b/src/frontend/planner_test/tests/testdata/input/limit.yaml @@ -53,6 +53,11 @@ expected_outputs: - batch_plan - stream_plan +- sql: | + select 1 c limit 1 offset 2 + expected_outputs: + - batch_plan + - stream_plan - sql: | select 1 c order by 1 limit 1 expected_outputs: diff --git a/src/frontend/planner_test/tests/testdata/output/limit.yaml b/src/frontend/planner_test/tests/testdata/output/limit.yaml index 81a832e8f48a..d45ef084fe9b 100644 --- a/src/frontend/planner_test/tests/testdata/output/limit.yaml +++ b/src/frontend/planner_test/tests/testdata/output/limit.yaml @@ -83,6 +83,15 @@ StreamMaterialize { columns: [c, _row_id(hidden)], stream_key: [], pk_columns: [], pk_conflict: NoCheck } └─StreamTopN [append_only] { order: [1:Int32 ASC], limit: 1, offset: 0 } └─StreamValues { rows: [[1:Int32, 0:Int64]] } +- sql: | + select 1 c limit 1 offset 2 + batch_plan: |- + BatchLimit { limit: 1, offset: 2 } + └─BatchValues { rows: [[1:Int32]] } + stream_plan: |- + StreamMaterialize { columns: [c, _row_id(hidden)], stream_key: [], pk_columns: [], pk_conflict: NoCheck } + └─StreamTopN [append_only] { order: [1:Int32 ASC], limit: 1, offset: 2 } + └─StreamValues { rows: [[1:Int32, 0:Int64]] } - sql: | select 1 c order by 1 limit 1 batch_plan: |- diff --git a/src/frontend/src/binder/relation/mod.rs b/src/frontend/src/binder/relation/mod.rs index 4ee4337c2f04..a6a1a8d2b02f 100644 --- a/src/frontend/src/binder/relation/mod.rs +++ b/src/frontend/src/binder/relation/mod.rs @@ -15,6 +15,7 @@ use std::collections::hash_map::Entry; use std::ops::Deref; +use itertools::{EitherOrBoth, Itertools}; use risingwave_common::catalog::{Field, TableId, DEFAULT_SCHEMA_NAME}; use risingwave_common::error::{internal_error, ErrorCode, Result, RwError}; use risingwave_sqlparser::ast::{ @@ -337,11 +338,13 @@ impl Binder { if let Some(from_alias) = alias { original_alias.name = from_alias.name; - let mut alias_iter = from_alias.columns.into_iter(); original_alias.columns = original_alias .columns .into_iter() - .map(|ident| alias_iter.next().unwrap_or(ident)) + .zip_longest( + from_alias.columns + ) + .map(EitherOrBoth::into_right) .collect(); } diff --git a/src/frontend/src/optimizer/plan_node/batch_limit.rs b/src/frontend/src/optimizer/plan_node/batch_limit.rs index 5fe37b1713ec..e617f1c2cd54 100644 --- a/src/frontend/src/optimizer/plan_node/batch_limit.rs +++ b/src/frontend/src/optimizer/plan_node/batch_limit.rs @@ -40,10 +40,10 @@ impl BatchLimit { BatchLimit { base, logical } } - fn two_phase_limit(&self, input: PlanRef) -> Result { + fn two_phase_limit(&self, new_input: PlanRef) -> Result { let new_limit = self.logical.limit + self.logical.offset; let new_offset = 0; - let logical_partial_limit = generic::Limit::new(input, new_limit, new_offset); + let logical_partial_limit = generic::Limit::new(new_input.clone(), new_limit, new_offset); let batch_partial_limit = Self::new(logical_partial_limit); let any_order = Order::any(); @@ -52,7 +52,7 @@ impl BatchLimit { single_dist.enforce_if_not_satisfies(batch_partial_limit.into(), &any_order)? } else { // The input's distribution is singleton, so use one phase limit is enough. - return Ok(batch_partial_limit.into()); + return Ok(self.clone_with_input(new_input).into()); }; let batch_global_limit = self.clone_with_input(ensure_single_dist); diff --git a/src/risedevtool/src/risedev_env.rs b/src/risedevtool/src/risedev_env.rs index 20b5fa97dae3..2ab3e350165f 100644 --- a/src/risedevtool/src/risedev_env.rs +++ b/src/risedevtool/src/risedev_env.rs @@ -19,8 +19,9 @@ use std::process::Command; use crate::{add_hummock_backend, HummockInMemoryStrategy, ServiceConfig}; -/// Generate environment variables from the given service configurations to be used by future -/// RiseDev commands, like `risedev ctl` or `risedev psql`. +/// Generate environment variables (put in file `.risingwave/config/risedev-env`) +/// from the given service configurations to be used by future +/// RiseDev commands, like `risedev ctl` or `risedev psql` (). pub fn generate_risedev_env(services: &Vec) -> String { let mut env = String::new(); for item in services {