Skip to content

Commit 4466c87

Browse files
committed
Support more SHOW ... WHERE
Fixes #1529. The only remaining unsupported case is SHOW VIEWS/SOURCES, because we don't plumb down the needed info via the catalog. I got rid of some ordering stuff, I can add it back in the form of a RowSetFinishing, but it doesn't seem to have been tested so I'm not sure it was actually important.
1 parent 976355f commit 4466c87

File tree

5 files changed

+267
-114
lines changed

5 files changed

+267
-114
lines changed

src/materialized/tests/pgwire.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ fn test_persistence() -> Result<(), Box<dyn Error>> {
252252
&[
253253
("@1".into(), 1),
254254
("@2".into(), 2),
255+
("@4".into(), 4),
255256
("c".into(), 3),
256-
("@4".into(), 4)
257257
],
258258
);
259259
assert_eq!(

src/sql/query.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,12 @@ pub fn plan_show_where(
142142
Ok((
143143
row_expr,
144144
RowSetFinishing {
145-
order_by: vec![],
145+
order_by: (0..num_cols)
146+
.map(|c| ColumnOrder {
147+
column: c,
148+
desc: false,
149+
})
150+
.collect(),
146151
limit: None,
147152
offset: 0,
148153
project: (0..num_cols).collect(),

src/sql/statement.rs

Lines changed: 119 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,52 @@ use tokio::io::AsyncBufReadExt;
5050
lazy_static! {
5151
static ref SHOW_DATABASES_DESC: RelationDesc =
5252
{ RelationDesc::empty().add_column("Database", ScalarType::String) };
53+
static ref SHOW_INDEXES_DESC: RelationDesc = RelationDesc::new(
54+
RelationType::new(vec![
55+
ColumnType::new(ScalarType::String),
56+
ColumnType::new(ScalarType::String),
57+
ColumnType::new(ScalarType::String).nullable(true),
58+
ColumnType::new(ScalarType::String).nullable(true),
59+
ColumnType::new(ScalarType::Bool),
60+
ColumnType::new(ScalarType::Int64),
61+
]),
62+
vec![
63+
"Source_or_view",
64+
"Key_name",
65+
"Column_name",
66+
"Expression",
67+
"Null",
68+
"Seq_in_index",
69+
]
70+
.into_iter()
71+
.map(Some),
72+
);
73+
static ref SHOW_COLUMNS_DESC: RelationDesc = RelationDesc::empty()
74+
.add_column("Field", ScalarType::String)
75+
.add_column("Nullable", ScalarType::String)
76+
.add_column("Type", ScalarType::String);
77+
}
78+
79+
pub fn make_show_objects_desc(
80+
object_type: ObjectType,
81+
materialized: bool,
82+
full: bool,
83+
) -> RelationDesc {
84+
let col_name = object_type_as_plural_str(object_type);
85+
if full {
86+
let mut relation_desc = RelationDesc::empty()
87+
.add_column(col_name, ScalarType::String)
88+
.add_column("TYPE", ScalarType::String);
89+
if ObjectType::View == object_type {
90+
relation_desc = relation_desc.add_column("QUERYABLE", ScalarType::Bool);
91+
}
92+
if !materialized && (ObjectType::View == object_type || ObjectType::Source == object_type) {
93+
relation_desc = relation_desc.add_column("MATERIALIZED", ScalarType::Bool);
94+
}
95+
relation_desc
96+
} else {
97+
RelationDesc::empty().add_column(col_name, ScalarType::String)
98+
}
5399
}
54100

55101
pub fn describe_statement(
@@ -124,40 +170,9 @@ pub fn describe_statement(
124170
vec![],
125171
),
126172

127-
Statement::ShowColumns { .. } => (
128-
Some(
129-
RelationDesc::empty()
130-
.add_column("Field", ScalarType::String)
131-
.add_column("Nullable", ScalarType::String)
132-
.add_column("Type", ScalarType::String),
133-
),
134-
vec![],
135-
),
173+
Statement::ShowColumns { .. } => (Some(SHOW_COLUMNS_DESC.clone()), vec![]),
136174

137-
Statement::ShowIndexes { .. } => (
138-
Some(RelationDesc::new(
139-
RelationType::new(vec![
140-
ColumnType::new(ScalarType::String),
141-
ColumnType::new(ScalarType::String),
142-
ColumnType::new(ScalarType::String).nullable(true),
143-
ColumnType::new(ScalarType::String).nullable(true),
144-
ColumnType::new(ScalarType::Bool),
145-
ColumnType::new(ScalarType::Int64),
146-
]),
147-
vec![
148-
"Source_or_view",
149-
"Key_name",
150-
"Column_name",
151-
"Expression",
152-
"Null",
153-
"Seq_in_index",
154-
]
155-
.iter()
156-
.map(|s| Some(*s))
157-
.collect::<Vec<_>>(),
158-
)),
159-
vec![],
160-
),
175+
Statement::ShowIndexes { .. } => (Some(SHOW_INDEXES_DESC.clone()), vec![]),
161176

162177
Statement::ShowDatabases { .. } => (Some(SHOW_DATABASES_DESC.clone()), vec![]),
163178

@@ -166,28 +181,10 @@ pub fn describe_statement(
166181
full,
167182
materialized,
168183
..
169-
} => {
170-
let col_name = object_type_as_plural_str(object_type);
171-
(
172-
Some(if full {
173-
let mut relation_desc = RelationDesc::empty()
174-
.add_column(col_name, ScalarType::String)
175-
.add_column("TYPE", ScalarType::String);
176-
if ObjectType::View == object_type {
177-
relation_desc = relation_desc.add_column("QUERYABLE", ScalarType::Bool);
178-
}
179-
if !materialized
180-
&& (ObjectType::View == object_type || ObjectType::Source == object_type)
181-
{
182-
relation_desc = relation_desc.add_column("MATERIALIZED", ScalarType::Bool);
183-
}
184-
relation_desc
185-
} else {
186-
RelationDesc::empty().add_column(col_name, ScalarType::String)
187-
}),
188-
vec![],
189-
)
190-
}
184+
} => (
185+
Some(make_show_objects_desc(object_type, materialized, full)),
186+
vec![],
187+
),
191188
Statement::ShowVariable { variable, .. } => {
192189
if variable.value == unicase::Ascii::new("ALL") {
193190
(
@@ -281,7 +278,7 @@ pub fn handle_statement(
281278
from,
282279
materialized,
283280
filter,
284-
} => handle_show_objects(scx, extended, full, materialized, ot, from, filter),
281+
} => handle_show_objects(scx, extended, full, materialized, ot, from, filter.as_ref()),
285282
Statement::ShowIndexes {
286283
extended,
287284
table_name,
@@ -346,17 +343,13 @@ fn handle_tail(scx: &StatementContext, from: ObjectName) -> Result<Plan, failure
346343
}
347344
}
348345

349-
fn handle_show_databases(
346+
fn finish_show_where(
350347
scx: &StatementContext,
351348
filter: Option<&ShowStatementFilter>,
349+
rows: Vec<Vec<Datum>>,
350+
desc: &RelationDesc,
352351
) -> Result<Plan, failure::Error> {
353-
let rows = scx
354-
.catalog
355-
.databases()
356-
.map(|database| vec![Datum::from(database)])
357-
.collect();
358-
359-
let (r, finishing) = query::plan_show_where(scx, filter, rows, &SHOW_DATABASES_DESC)?;
352+
let (r, finishing) = query::plan_show_where(scx, filter, rows, desc)?;
360353

361354
Ok(Plan::Peek {
362355
source: r.decorrelate()?,
@@ -366,32 +359,45 @@ fn handle_show_databases(
366359
})
367360
}
368361

362+
fn handle_show_databases(
363+
scx: &StatementContext,
364+
filter: Option<&ShowStatementFilter>,
365+
) -> Result<Plan, failure::Error> {
366+
let rows = scx
367+
.catalog
368+
.databases()
369+
.map(|database| vec![Datum::from(database)])
370+
.collect();
371+
372+
finish_show_where(scx, filter, rows, &SHOW_DATABASES_DESC)
373+
}
374+
369375
fn handle_show_objects(
370376
scx: &StatementContext,
371377
extended: bool,
372378
full: bool,
373379
materialized: bool,
374380
object_type: ObjectType,
375381
from: Option<ObjectName>,
376-
filter: Option<ShowStatementFilter>,
382+
filter: Option<&ShowStatementFilter>,
377383
) -> Result<Plan, failure::Error> {
378384
let classify_id = |id| match id {
379385
GlobalId::System(_) => "SYSTEM",
380386
GlobalId::User(_) => "USER",
381387
};
382-
let make_row = |name: &str, class| {
388+
let arena = RowArena::new();
389+
let make_row = |name: &str, class: &str| {
383390
if full {
384-
Row::pack(&[Datum::from(name), Datum::from(class)])
391+
vec![
392+
Datum::from(arena.push_string(name.to_string())),
393+
Datum::from(arena.push_string(class.to_string())),
394+
]
385395
} else {
386-
Row::pack(&[Datum::from(name)])
396+
vec![Datum::from(arena.push_string(name.to_string()))]
387397
}
388398
};
389399

390400
if let ObjectType::Schema = object_type {
391-
if filter.is_some() {
392-
bail!("SHOW SCHEMAS ... {LIKE | WHERE} is not supported");
393-
}
394-
395401
let schemas = if let Some(from) = from {
396402
if from.0.len() != 1 {
397403
bail!(
@@ -427,15 +433,15 @@ fn handle_show_objects(
427433
rows.push(make_row(name, "SYSTEM"));
428434
}
429435
}
430-
rows.sort_unstable_by(move |a, b| a.unpack_first().cmp(&b.unpack_first()));
431-
Ok(Plan::SendRows(rows))
436+
// TODO(justin): it's unfortunate that we call make_show_objects_desc twice, I think we
437+
// should be able to restructure this so that it only gets called once.
438+
finish_show_where(
439+
scx,
440+
filter,
441+
rows,
442+
&make_show_objects_desc(object_type, materialized, full),
443+
)
432444
} else {
433-
let like_regex = match filter {
434-
Some(ShowStatementFilter::Like(pattern)) => like_pattern::build_regex(&pattern)?,
435-
Some(ShowStatementFilter::Where(_)) => bail!("SHOW ... WHERE is not supported"),
436-
None => like_pattern::build_regex("%")?,
437-
};
438-
439445
let empty_schema = BTreeMap::new();
440446
let items = if let Some(mut from) = from {
441447
if from.0.len() > 2 {
@@ -469,12 +475,20 @@ fn handle_show_objects(
469475
let filtered_items = items
470476
.iter()
471477
.map(|(name, id)| (name, scx.catalog.get_by_id(id)))
472-
.filter(|(_name, entry)| {
473-
object_type_matches(object_type, entry.item())
474-
&& like_regex.is_match(&entry.name().to_string())
475-
});
478+
.filter(|(_name, entry)| object_type_matches(object_type, entry.item()));
476479

477480
if object_type == ObjectType::View || object_type == ObjectType::Source {
481+
// TODO(justin): we can't handle SHOW ... WHERE here yet because the coordinator adds
482+
// extra columns to this result that we don't have access to here yet. This could be
483+
// fixed by passing down this extra catalog info somehow.
484+
let like_regex = match filter {
485+
Some(ShowStatementFilter::Like(pattern)) => like_pattern::build_regex(&pattern)?,
486+
Some(ShowStatementFilter::Where(_)) => bail!("SHOW ... WHERE is not supported"),
487+
None => like_pattern::build_regex("%")?,
488+
};
489+
490+
let filtered_items = filtered_items
491+
.filter(|(_name, entry)| like_regex.is_match(&entry.name().to_string()));
478492
Ok(Plan::ShowViews {
479493
ids: filtered_items
480494
.map(|(name, entry)| (name.clone(), entry.id()))
@@ -484,11 +498,16 @@ fn handle_show_objects(
484498
limit_materialized: materialized,
485499
})
486500
} else {
487-
let mut rows = filtered_items
501+
let rows = filtered_items
488502
.map(|(name, entry)| make_row(name, classify_id(entry.id())))
489503
.collect::<Vec<_>>();
490-
rows.sort_unstable_by(move |a, b| a.unpack_first().cmp(&b.unpack_first()));
491-
Ok(Plan::SendRows(rows))
504+
505+
finish_show_where(
506+
scx,
507+
filter,
508+
rows,
509+
&make_show_objects_desc(object_type, materialized, full),
510+
)
492511
}
493512
}
494513
}
@@ -502,9 +521,6 @@ fn handle_show_indexes(
502521
if extended {
503522
bail!("SHOW EXTENDED INDEXES is not supported")
504523
}
505-
if filter.is_some() {
506-
bail!("SHOW INDEXES ... WHERE is not supported");
507-
}
508524
let from_name = scx.resolve_name(from_name)?;
509525
let from_entry = scx.catalog.get(&from_name)?;
510526
if !object_type_matches(ObjectType::View, from_entry.item())
@@ -516,6 +532,7 @@ fn handle_show_indexes(
516532
from_entry.item().type_string()
517533
);
518534
}
535+
let arena = RowArena::new();
519536
let rows = scx
520537
.catalog
521538
.iter()
@@ -541,7 +558,6 @@ fn handle_show_indexes(
541558
for (i, (key_expr, key_sql)) in keys.iter().zip_eq(key_sqls).enumerate() {
542559
let desc = scx.catalog.get_by_id(&on).desc().unwrap();
543560
let key_sql = key_sql.to_string();
544-
let arena = RowArena::new();
545561
let (col_name, func) = match key_expr {
546562
expr::ScalarExpr::Column(i) => {
547563
let col_name = match desc.get_unambiguous_name(*i) {
@@ -552,21 +568,22 @@ fn handle_show_indexes(
552568
}
553569
_ => (Datum::Null, Datum::String(arena.push_string(key_sql))),
554570
};
555-
row_subset.push(Row::pack(&vec![
556-
Datum::String(&from_entry.name().to_string()),
557-
Datum::String(&entry.name().to_string()),
571+
row_subset.push(vec![
572+
Datum::String(arena.push_string(from_entry.name().to_string())),
573+
Datum::String(arena.push_string(entry.name().to_string())),
558574
col_name,
559575
func,
560576
Datum::from(key_expr.typ(desc.typ()).nullable),
561577
Datum::from((i + 1) as i64),
562-
]));
578+
]);
563579
}
564580
row_subset
565581
}
566582
_ => unreachable!(),
567583
})
568584
.collect();
569-
Ok(Plan::SendRows(rows))
585+
586+
finish_show_where(scx, filter, rows, &SHOW_INDEXES_DESC)
570587
}
571588

572589
/// Create an immediate result that describes all the columns for the given table
@@ -583,27 +600,25 @@ fn handle_show_columns(
583600
if full {
584601
bail!("SHOW FULL COLUMNS is not supported");
585602
}
586-
if filter.is_some() {
587-
bail!("SHOW COLUMNS ... { LIKE | WHERE } is not supported");
588-
}
589603

604+
let arena = RowArena::new();
590605
let table_name = scx.resolve_name(table_name)?;
591-
let column_descriptions: Vec<_> = scx
606+
let rows: Vec<_> = scx
592607
.catalog
593608
.get(&table_name)?
594609
.desc()?
595610
.iter()
596611
.map(|(name, typ)| {
597612
let name = name.map(|n| n.to_string());
598-
Row::pack(&[
599-
Datum::String(name.as_deref().unwrap_or("?")),
613+
vec![
614+
Datum::String(name.map(|n| arena.push_string(n)).unwrap_or("?")),
600615
Datum::String(if typ.nullable { "YES" } else { "NO" }),
601616
Datum::String(pgrepr::Type::from(&typ.scalar_type).name()),
602-
])
617+
]
603618
})
604619
.collect();
605620

606-
Ok(Plan::SendRows(column_descriptions))
621+
finish_show_where(scx, filter, rows, &SHOW_COLUMNS_DESC)
607622
}
608623

609624
fn handle_show_create_view(

0 commit comments

Comments
 (0)