Skip to content

Commit bbe0726

Browse files
authored
Merge pull request MaterializeInc#3381 from benesch/column-aliases
sql: allow aliasing column names
2 parents 51eda7d + d96477a commit bbe0726

File tree

7 files changed

+264
-241
lines changed

7 files changed

+264
-241
lines changed

ci/test/slt-fast.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ tests=(
126126
# test/sqllogictest/cockroach/select_index_span_ranges.slt
127127
# test/sqllogictest/cockroach/select_index.slt
128128
test/sqllogictest/cockroach/select_search_path.slt
129-
# test/sqllogictest/cockroach/select_table_alias.slt
129+
test/sqllogictest/cockroach/select_table_alias.slt
130130
# test/sqllogictest/cockroach/select.slt
131131
test/sqllogictest/cockroach/shift.slt
132132
# test/sqllogictest/cockroach/sqlsmith.slt
@@ -148,7 +148,7 @@ tests=(
148148
test/sqllogictest/cockroach/update.slt
149149
test/sqllogictest/cockroach/upsert.slt
150150
test/sqllogictest/cockroach/uuid.slt
151-
# test/sqllogictest/cockroach/values.slt
151+
test/sqllogictest/cockroach/values.slt
152152
# test/sqllogictest/cockroach/views.slt
153153
# test/sqllogictest/cockroach/where.slt
154154
test/sqllogictest/cockroach/window.slt

src/sql/src/plan/func.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use itertools::Itertools;
1919
use lazy_static::lazy_static;
2020

2121
use ore::collections::CollectionExt;
22-
use repr::{ColumnType, Datum, ScalarType};
22+
use repr::{ColumnName, ColumnType, Datum, ScalarType};
2323
use sql_parser::ast::{BinaryOperator, Expr, UnaryOperator};
2424

2525
use super::cast::{self, rescale_decimal, CastTo};
@@ -1304,7 +1304,7 @@ pub fn plan_unary_op<'a>(
13041304
pub struct TableFuncPlan {
13051305
pub func: TableFunc,
13061306
pub exprs: Vec<ScalarExpr>,
1307-
pub column_names: Vec<String>,
1307+
pub column_names: Vec<Option<ColumnName>>,
13081308
}
13091309

13101310
lazy_static! {
@@ -1324,7 +1324,7 @@ lazy_static! {
13241324
Ok(TableFuncPlan {
13251325
func: TableFunc::CsvExtract(ncols),
13261326
exprs: vec![input],
1327-
column_names: (1..=ncols).map(|i| format!("column{}", i)).collect(),
1327+
column_names: (1..=ncols).map(|i| Some(format!("column{}", i).into())).collect(),
13281328
})
13291329
})
13301330
},
@@ -1337,7 +1337,7 @@ lazy_static! {
13371337
Ok(TableFuncPlan {
13381338
func: TableFunc::JsonbArrayElements { stringify: false },
13391339
exprs: vec![jsonb],
1340-
column_names: vec!["value".into()],
1340+
column_names: vec![Some("value".into())],
13411341
})
13421342
})
13431343
},
@@ -1346,7 +1346,7 @@ lazy_static! {
13461346
Ok(TableFuncPlan {
13471347
func: TableFunc::JsonbArrayElements { stringify: true },
13481348
exprs: vec![jsonb],
1349-
column_names: vec!["value".into()],
1349+
column_names: vec![Some("value".into())],
13501350
})
13511351
})
13521352
},
@@ -1355,7 +1355,7 @@ lazy_static! {
13551355
Ok(TableFuncPlan {
13561356
func: TableFunc::JsonbEach { stringify: false },
13571357
exprs: vec![jsonb],
1358-
column_names: vec!["key".into(), "value".into()],
1358+
column_names: vec![Some("key".into()), Some("value".into())],
13591359
})
13601360
})
13611361
},
@@ -1364,7 +1364,7 @@ lazy_static! {
13641364
Ok(TableFuncPlan {
13651365
func: TableFunc::JsonbEach { stringify: true },
13661366
exprs: vec![jsonb],
1367-
column_names: vec!["key".into(), "value".into()],
1367+
column_names: vec![Some("key".into()), Some("value".into())],
13681368
})
13691369
})
13701370
},
@@ -1373,7 +1373,7 @@ lazy_static! {
13731373
Ok(TableFuncPlan {
13741374
func: TableFunc::JsonbObjectKeys,
13751375
exprs: vec![jsonb],
1376-
column_names: vec!["jsonb_object_keys".into()],
1376+
column_names: vec![Some("jsonb_object_keys".into())],
13771377
})
13781378
})
13791379
},
@@ -1385,7 +1385,10 @@ lazy_static! {
13851385
};
13861386
let column_names = regex
13871387
.capture_groups_iter()
1388-
.map(|cg| cg.name.clone().unwrap_or_else(|| format!("column{}", cg.index)))
1388+
.map(|cg| {
1389+
let name = cg.name.clone().unwrap_or_else(|| format!("column{}", cg.index));
1390+
Some(name.into())
1391+
})
13891392
.collect();
13901393
Ok(TableFuncPlan {
13911394
func: TableFunc::RegexpExtract(regex),
@@ -1403,7 +1406,7 @@ fn plan_generate_series(ty: ScalarType) -> Operation<TableFuncPlan> {
14031406
Ok(TableFuncPlan {
14041407
func: TableFunc::GenerateSeries(ty.clone()),
14051408
exprs,
1406-
column_names: vec!["generate_series".into()],
1409+
column_names: vec![Some("generate_series".into())],
14071410
})
14081411
})
14091412
}

src/sql/src/plan/query.rs

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -708,18 +708,6 @@ fn plan_table_factor<'a>(
708708
if !with_hints.is_empty() {
709709
unsupported!("WITH hints");
710710
}
711-
let alias = if let Some(TableAlias { name, columns }) = alias {
712-
if !columns.is_empty() {
713-
unsupported!(3112, "aliasing columns");
714-
}
715-
PartialName {
716-
database: None,
717-
schema: None,
718-
item: normalize::ident(name.clone()),
719-
}
720-
} else {
721-
normalize::object_name(name.clone())?
722-
};
723711
if let Some(args) = args {
724712
let ecx = &ExprContext {
725713
qcx,
@@ -729,19 +717,16 @@ fn plan_table_factor<'a>(
729717
allow_aggregates: false,
730718
allow_subqueries: true,
731719
};
732-
plan_table_function(ecx, left, &name, Some(alias), args)
720+
plan_table_function(ecx, left, &name, alias.as_ref(), args)
733721
} else {
734722
let name = qcx.scx.resolve_item(name.clone())?;
735723
let item = qcx.scx.catalog.get_item(&name);
736724
let expr = RelationExpr::Get {
737725
id: Id::Global(item.id()),
738726
typ: item.desc()?.typ().clone(),
739727
};
740-
let scope = Scope::from_source(
741-
Some(alias),
742-
item.desc()?.iter_names(),
743-
Some(qcx.outer_scope.clone()),
744-
);
728+
let column_names = item.desc()?.iter_names().map(|n| n.cloned()).collect();
729+
let scope = plan_table_alias(qcx, alias.as_ref(), Some(name.into()), column_names)?;
745730
plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope)
746731
}
747732
}
@@ -754,20 +739,9 @@ fn plan_table_factor<'a>(
754739
unsupported!(3111, "LATERAL derived tables");
755740
}
756741
let (expr, scope) = plan_subquery(&qcx, &subquery)?;
757-
let alias = if let Some(TableAlias { name, columns }) = alias {
758-
if !columns.is_empty() {
759-
unsupported!(3112, "aliasing columns");
760-
}
761-
Some(PartialName {
762-
database: None,
763-
schema: None,
764-
item: normalize::ident(name.clone()),
765-
})
766-
} else {
767-
None
768-
};
769-
let scope =
770-
Scope::from_source(alias, scope.column_names(), Some(qcx.outer_scope.clone()));
742+
let table_name = None;
743+
let column_names = scope.column_names().map(|n| n.cloned()).collect();
744+
let scope = plan_table_alias(qcx, alias.as_ref(), table_name, column_names)?;
771745
plan_join_operator(qcx, &join_operator, left, left_scope, expr, scope)
772746
}
773747
TableFactor::NestedJoin(table_with_joins) => {
@@ -780,30 +754,71 @@ fn plan_table_function(
780754
ecx: &ExprContext,
781755
left: RelationExpr,
782756
name: &ObjectName,
783-
alias: Option<PartialName>,
757+
alias: Option<&TableAlias>,
784758
args: &FunctionArgs,
785759
) -> Result<(RelationExpr, Scope), failure::Error> {
786-
let ident = &*normalize::function_name(name.clone())?;
760+
let ident = normalize::function_name(name.clone())?;
787761
let args = match args {
788762
FunctionArgs::Star => bail!("{} does not accept * as an argument", ident),
789763
FunctionArgs::Args(args) => args,
790764
};
791-
let tf = func::select_table_func(ecx, ident, args)?;
765+
let tf = func::select_table_func(ecx, &*ident, args)?;
792766
let call = RelationExpr::FlatMap {
793767
input: Box::new(left),
794768
func: tf.func,
795769
exprs: tf.exprs,
796770
};
797-
let scope = Scope::from_source(
798-
alias,
799-
tf.column_names
800-
.iter()
801-
.map(|name| Some(ColumnName::from(&**name))),
802-
Some(ecx.qcx.outer_scope.clone()),
803-
);
771+
let name = PartialName {
772+
database: None,
773+
schema: None,
774+
item: ident,
775+
};
776+
let scope = plan_table_alias(ecx.qcx, alias, Some(name), tf.column_names)?;
804777
Ok((call, ecx.scope.clone().product(scope)))
805778
}
806779

780+
fn plan_table_alias(
781+
qcx: &QueryContext,
782+
alias: Option<&TableAlias>,
783+
inherent_table_name: Option<PartialName>,
784+
inherent_column_names: Vec<Option<ColumnName>>,
785+
) -> Result<Scope, failure::Error> {
786+
let table_name = match alias {
787+
None => inherent_table_name,
788+
Some(TableAlias { name, .. }) => Some(PartialName {
789+
database: None,
790+
schema: None,
791+
item: normalize::ident(name.to_owned()),
792+
}),
793+
};
794+
let column_names = match alias {
795+
None => inherent_column_names,
796+
Some(TableAlias { columns, .. }) if columns.is_empty() => inherent_column_names,
797+
Some(TableAlias { columns, .. }) if columns.len() > inherent_column_names.len() => {
798+
bail!(
799+
"{} has {} columns available but {} columns specified",
800+
table_name
801+
.map(|n| n.to_string())
802+
.as_deref()
803+
.unwrap_or("subquery"),
804+
inherent_column_names.len(),
805+
columns.len()
806+
);
807+
}
808+
Some(TableAlias { columns, .. }) => columns
809+
.iter()
810+
.cloned()
811+
.map(|n| Some(normalize::column_name(n)))
812+
.chain(inherent_column_names.into_iter().skip(columns.len()))
813+
.collect(),
814+
};
815+
Ok(Scope::from_source(
816+
table_name,
817+
column_names,
818+
Some(qcx.outer_scope.clone()),
819+
))
820+
}
821+
807822
fn plan_select_item<'a>(
808823
ecx: &ExprContext,
809824
s: &'a SelectItem,

0 commit comments

Comments
 (0)