Skip to content

Commit d50b4cb

Browse files
author
Eric Fu
authored
feat(catalog): support DBeaver constraints view (#15227)
1 parent 9651f39 commit d50b4cb

File tree

14 files changed

+578
-140
lines changed

14 files changed

+578
-140
lines changed

e2e_test/batch/catalog/pg_class.slt.part

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SELECT oid,relname,relowner,relkind FROM pg_catalog.pg_class ORDER BY oid limit
1111
8 pg_cast 1 r
1212
9 pg_class 1 v
1313
10 pg_collation 1 v
14-
11 pg_constraint 1 v
14+
11 pg_constraint 1 r
1515
12 pg_conversion 1 v
1616
13 pg_database 1 v
1717
14 pg_depend 1 v
@@ -20,4 +20,4 @@ SELECT oid,relname,relowner,relkind FROM pg_catalog.pg_class ORDER BY oid limit
2020
query ITIT
2121
SELECT oid,relname,relowner,relkind FROM pg_catalog.pg_class WHERE oid = 'pg_namespace'::regclass;
2222
----
23-
24 pg_namespace 1 v
23+
25 pg_namespace 1 v
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
statement ok
2+
create table t(a int, b int, c varchar, primary key(a,b));
3+
4+
query TTTT
5+
select conname, contype, conkey from pg_constraint where conname='t_pkey';
6+
----
7+
t_pkey p {1,2}
8+
9+
statement ok
10+
drop table t;

src/frontend/planner_test/tests/planner_test_runner.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ fn main() {
4343
let file_name = path.file_name().unwrap().to_string_lossy().to_string();
4444
let test_case_name = file_name.split('.').next().unwrap().to_string();
4545

46-
tests.push(Trial::test(format!("{test_case_name}_test"), move || {
46+
tests.push(Trial::test(test_case_name, move || {
4747
let path = test_data_dir().join("input").join(file_name);
4848

4949
let file_content = std::fs::read_to_string(&path).unwrap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
- sql: |
2+
SELECT DISTINCT dep.deptype, dep.classid, dep.objid, cl.relkind, attr.attname,pg_get_expr(ad.adbin, ad.adrelid) adefval,
3+
CASE WHEN cl.relkind IS NOT NULL THEN cl.relkind::text || COALESCE(dep.objsubid::text, '')::text
4+
WHEN tg.oid IS NOT NULL THEN 'T'::text
5+
WHEN ty.oid IS NOT NULL THEN 'y'::text
6+
WHEN ns.oid IS NOT NULL THEN 'n'::text
7+
WHEN pr.oid IS NOT NULL THEN 'p'::text
8+
WHEN la.oid IS NOT NULL THEN 'l'::text
9+
WHEN rw.oid IS NOT NULL THEN 'R'::text
10+
WHEN co.oid IS NOT NULL THEN 'C'::text || contype::text
11+
WHEN ad.oid IS NOT NULL THEN 'A'::text
12+
ELSE ''
13+
END AS type,
14+
COALESCE(coc.relname, clrw.relname, tgr.relname) AS ownertable,
15+
CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || '.' || att.attname
16+
ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname)
17+
END AS refname,
18+
COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname, tgrn.nspname) AS nspname
19+
FROM pg_depend dep
20+
LEFT JOIN pg_class cl ON dep.objid=cl.oid
21+
LEFT JOIN pg_attribute att ON dep.objid=att.attrelid AND dep.objsubid=att.attnum
22+
LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid
23+
LEFT JOIN pg_proc pr ON dep.objid=pr.oid
24+
LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid
25+
LEFT JOIN pg_trigger tg ON dep.objid=tg.oid
26+
LEFT JOIN pg_class tgr ON tg.tgrelid=tgr.oid
27+
LEFT JOIN pg_namespace tgrn ON tgr.relnamespace=tgrn.oid
28+
LEFT JOIN pg_type ty ON dep.objid=ty.oid
29+
LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid
30+
LEFT JOIN pg_constraint co ON dep.objid=co.oid
31+
LEFT JOIN pg_class coc ON co.conrelid=coc.oid
32+
LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid
33+
LEFT JOIN pg_rewrite rw ON dep.objid=rw.oid
34+
LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class
35+
LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid
36+
LEFT JOIN pg_language la ON dep.objid=la.oid
37+
LEFT JOIN pg_namespace ns ON dep.objid=ns.oid
38+
LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid
39+
LEFT JOIN pg_attribute attr ON attr.attrelid=ad.adrelid and attr.attnum=ad.adnum
40+
WHERE dep.refobjid=$1
41+
ORDER BY type
42+
expected_outputs:
43+
- batch_plan

src/frontend/planner_test/tests/testdata/output/dbeaver.yaml

+171
Large diffs are not rendered by default.

src/frontend/planner_test/tests/testdata/output/subquery.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@
227227
└─LogicalFilter { predicate: In($expr1, 'r':Varchar, 'p':Varchar, 'v':Varchar, 'm':Varchar, 'S':Varchar, 'f':Varchar, '':Varchar) AND (rw_schemas.name <> 'pg_catalog':Varchar) AND Not(RegexpEq(rw_schemas.name, '^pg_toast':Varchar)) AND (rw_schemas.name <> 'information_schema':Varchar) }
228228
└─LogicalJoin { type: LeftOuter, on: (rw_schemas.id = rw_tables.schema_id), output: all }
229229
├─LogicalShare { id: 16 }
230-
│ └─LogicalProject { exprs: [rw_tables.id, rw_tables.name, rw_tables.schema_id, rw_tables.owner, 'p':Varchar, Case(('table':Varchar = 'table':Varchar), 'r':Varchar, ('table':Varchar = 'system table':Varchar), 'r':Varchar, ('table':Varchar = 'index':Varchar), 'i':Varchar, ('table':Varchar = 'view':Varchar), 'v':Varchar, ('table':Varchar = 'materialized view':Varchar), 'm':Varchar) as $expr1, 0:Int32, 0:Int32, Array as $expr2] }
230+
│ └─LogicalProject { exprs: [rw_tables.id, rw_tables.name, rw_tables.schema_id, rw_tables.owner, 'p':Varchar, Case(('table':Varchar = 'table':Varchar), 'r':Varchar, ('table':Varchar = 'system table':Varchar), 'r':Varchar, ('table':Varchar = 'index':Varchar), 'i':Varchar, ('table':Varchar = 'view':Varchar), 'v':Varchar, ('table':Varchar = 'materialized view':Varchar), 'm':Varchar) as $expr1, 0:Int32, 0:Int32, Array as $expr2, null:Varchar] }
231231
│ └─LogicalShare { id: 14 }
232232
│ └─LogicalUnion { all: true }
233233
│ ├─LogicalUnion { all: true }

src/frontend/src/binder/expr/function.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,7 @@ impl Binder {
13231323
("pg_table_is_visible", raw_literal(ExprImpl::literal_bool(true))),
13241324
("pg_type_is_visible", raw_literal(ExprImpl::literal_bool(true))),
13251325
("pg_get_constraintdef", raw_literal(ExprImpl::literal_null(DataType::Varchar))),
1326+
("pg_get_partkeydef", raw_literal(ExprImpl::literal_null(DataType::Varchar))),
13261327
("pg_encoding_to_char", raw_literal(ExprImpl::literal_varchar("UTF8".into()))),
13271328
("has_database_privilege", raw_literal(ExprImpl::literal_bool(true))),
13281329
("pg_backend_pid", raw(|binder, _inputs| {

src/frontend/src/catalog/system_catalog/pg_catalog/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,23 @@ mod pg_index;
3030
mod pg_indexes;
3131
mod pg_inherits;
3232
mod pg_keywords;
33+
mod pg_language;
3334
mod pg_locks;
3435
mod pg_matviews;
3536
mod pg_namespace;
3637
mod pg_opclass;
3738
mod pg_operator;
3839
mod pg_partitioned_table;
3940
mod pg_proc;
41+
mod pg_rewrite;
4042
mod pg_roles;
4143
mod pg_settings;
4244
mod pg_shadow;
4345
mod pg_shdescription;
4446
mod pg_stat_activity;
4547
mod pg_tables;
4648
mod pg_tablespace;
49+
mod pg_trigger;
4750
mod pg_type;
4851
mod pg_user;
4952
mod pg_views;

src/frontend/src/catalog/system_catalog/pg_catalog/pg_class.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ use risingwave_frontend_macro::system_catalog;
2929
END relkind,
3030
0 AS relam,
3131
0 AS reltablespace,
32-
ARRAY[]::varchar[] AS reloptions
32+
ARRAY[]::varchar[] AS reloptions,
33+
null AS relpartbound
3334
FROM rw_catalog.rw_relations
3435
")]
3536
#[derive(Fields)]
@@ -46,4 +47,6 @@ struct PgClass {
4647
relam: i32,
4748
reltablespace: i32,
4849
reloptions: Vec<String>,
50+
// PG uses pg_node_tree type but RW doesn't support it
51+
relpartbound: Option<String>,
4952
}

src/frontend/src/catalog/system_catalog/pg_catalog/pg_constraint.rs

+107-9
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@
1515
use risingwave_common::types::Fields;
1616
use risingwave_frontend_macro::system_catalog;
1717

18+
use crate::catalog::schema_catalog::SchemaCatalog;
19+
use crate::catalog::system_catalog::{SysCatalogReaderImpl, SystemTableCatalog};
20+
use crate::error::Result;
21+
use crate::TableCatalog;
22+
1823
/// The catalog `pg_constraint` records information about table and index inheritance hierarchies.
1924
/// Ref: [`https://www.postgresql.org/docs/current/catalog-pg-constraint.html`]
2025
/// This is introduced only for pg compatibility and is not used in our system.
21-
#[system_catalog(view, "pg_catalog.pg_constraint")]
2226
#[derive(Fields)]
2327
struct PgConstraint {
28+
#[primary_key]
2429
oid: i32,
2530
conname: String,
2631
connamespace: i32,
@@ -38,12 +43,105 @@ struct PgConstraint {
3843
conislocal: bool,
3944
coninhcount: i32,
4045
connoinherit: bool,
41-
conkey: Vec<i16>,
42-
confkey: Vec<i16>,
43-
conpfeqop: Vec<i32>,
44-
conppeqop: Vec<i32>,
45-
conffeqop: Vec<i32>,
46-
confdelsetcols: Vec<i16>,
47-
conexclop: Vec<i32>,
48-
conbin: String,
46+
conkey: Option<Vec<i16>>,
47+
confkey: Option<Vec<i16>>,
48+
conpfeqop: Option<Vec<i32>>,
49+
conppeqop: Option<Vec<i32>>,
50+
conffeqop: Option<Vec<i32>>,
51+
confdelsetcols: Option<Vec<i16>>,
52+
conexclop: Option<Vec<i32>>,
53+
conbin: Option<String>,
54+
}
55+
56+
impl PgConstraint {
57+
fn from_system_table(schema: &SchemaCatalog, table: &SystemTableCatalog) -> PgConstraint {
58+
// List of the constrained columns. First column starts from 1.
59+
let conkey: Vec<_> = table.pk.iter().map(|i| (*i + 1) as i16).collect();
60+
PgConstraint {
61+
oid: table.id.table_id() as i32, // Use table_id as a mock oid of constraint here.
62+
conname: format!("{}_pkey", &table.name),
63+
connamespace: schema.id() as i32,
64+
contype: "p".to_owned(), // p = primary key constraint
65+
condeferrable: false,
66+
convalidated: true,
67+
conrelid: table.id.table_id() as i32,
68+
contypid: 0,
69+
// Use table_id as a mock index oid of constraint here.
70+
conindid: table.id.table_id() as i32,
71+
conparentid: 0,
72+
confrelid: 0,
73+
confupdtype: " ".to_owned(),
74+
confdeltype: " ".to_owned(),
75+
confmatchtype: " ".to_owned(),
76+
conislocal: true,
77+
coninhcount: 0,
78+
connoinherit: true,
79+
conkey: Some(conkey),
80+
confkey: None,
81+
conpfeqop: None,
82+
conppeqop: None,
83+
conffeqop: None,
84+
confdelsetcols: None,
85+
conexclop: None,
86+
conbin: None,
87+
}
88+
}
89+
90+
fn from_table(schema: &SchemaCatalog, table: &TableCatalog) -> PgConstraint {
91+
// List of the constrained columns. First column starts from 1.
92+
let conkey: Vec<_> = table
93+
.pk
94+
.iter()
95+
.map(|i| (i.column_index + 1) as i16)
96+
.collect();
97+
PgConstraint {
98+
oid: table.id.table_id() as i32, // Use table_id as a mock oid of constraint here.
99+
conname: format!("{}_pkey", &table.name),
100+
connamespace: schema.id() as i32,
101+
contype: "p".to_owned(), // p = primary key constraint
102+
condeferrable: false,
103+
convalidated: true,
104+
conrelid: table.id.table_id() as i32,
105+
contypid: 0,
106+
// Use table_id as a mock index oid of constraint here.
107+
conindid: table.id.table_id() as i32,
108+
conparentid: 0,
109+
confrelid: 0,
110+
confupdtype: " ".to_owned(),
111+
confdeltype: " ".to_owned(),
112+
confmatchtype: " ".to_owned(),
113+
conislocal: true,
114+
coninhcount: 0,
115+
connoinherit: true,
116+
conkey: Some(conkey),
117+
confkey: None,
118+
conpfeqop: None,
119+
conppeqop: None,
120+
conffeqop: None,
121+
confdelsetcols: None,
122+
conexclop: None,
123+
conbin: None,
124+
}
125+
}
126+
}
127+
128+
#[system_catalog(table, "pg_catalog.pg_constraint")]
129+
fn read_pg_constraint(reader: &SysCatalogReaderImpl) -> Result<Vec<PgConstraint>> {
130+
let catalog_reader = reader.catalog_reader.read_guard();
131+
let schemas = catalog_reader.iter_schemas(&reader.auth_context.database)?;
132+
133+
Ok(schemas.flat_map(read_pg_constraint_in_schema).collect())
134+
}
135+
136+
fn read_pg_constraint_in_schema(schema: &SchemaCatalog) -> Vec<PgConstraint> {
137+
// Note: We only support primary key constraints now.
138+
let system_table_rows = schema
139+
.iter_system_tables()
140+
.map(|table| PgConstraint::from_system_table(schema, table.as_ref()));
141+
142+
let table_rows = schema
143+
.iter_valid_table()
144+
.map(|table| PgConstraint::from_table(schema, table.as_ref()));
145+
146+
system_table_rows.chain(table_rows).collect()
49147
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 RisingWave Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use risingwave_common::types::Fields;
16+
use risingwave_frontend_macro::system_catalog;
17+
18+
/// The catalog `pg_language` registers languages in which you can write functions or stored procedures.
19+
/// Ref: [`https://www.postgresql.org/docs/current/catalog-pg-language.html`]
20+
/// This is introduced only for pg compatibility and is not used in our system.
21+
#[system_catalog(view, "pg_catalog.pg_language")]
22+
#[derive(Fields)]
23+
struct PgLanguage {
24+
#[primary_key]
25+
oid: i32,
26+
lanname: String,
27+
lanowner: i32,
28+
lanispl: bool,
29+
lanpltrusted: bool,
30+
lanplcallfoid: i32,
31+
laninline: i32,
32+
lanvalidator: i32,
33+
lanacl: Vec<String>,
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2024 RisingWave Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use risingwave_common::types::Fields;
16+
use risingwave_frontend_macro::system_catalog;
17+
18+
/// The catalog `pg_rewrite` stores rewrite rules for tables and views.
19+
/// Ref: [`https://www.postgresql.org/docs/current/catalog-pg-rewrite.html`]
20+
/// This is introduced only for pg compatibility and is not used in our system.
21+
#[system_catalog(view, "pg_catalog.pg_rewrite")]
22+
#[derive(Fields)]
23+
struct PgRewrite {
24+
#[primary_key]
25+
oid: i32,
26+
rulename: String,
27+
ev_class: i32,
28+
ev_type: String,
29+
ev_enabled: String,
30+
is_instead: bool,
31+
ev_qual: String,
32+
ev_action: String,
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2024 RisingWave Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use risingwave_common::types::Fields;
16+
use risingwave_frontend_macro::system_catalog;
17+
18+
/// The catalog `pg_trigger` stores triggers on tables and views.
19+
/// Ref: [`https://www.postgresql.org/docs/current/catalog-pg-trigger.html`]
20+
/// This is introduced only for pg compatibility and is not used in our system.
21+
#[system_catalog(view, "pg_catalog.pg_trigger")]
22+
#[derive(Fields)]
23+
struct PgTrigger {
24+
#[primary_key]
25+
oid: i32,
26+
tgrelid: i32,
27+
tgparentid: i32,
28+
tgname: String,
29+
tgfoid: i32,
30+
tgtype: i16,
31+
tgenabled: String,
32+
tgisinternal: bool,
33+
tgconstrrelid: i32,
34+
tgconstrindid: i32,
35+
tgconstraint: i32,
36+
tgdeferrable: bool,
37+
tginitdeferred: bool,
38+
tgnargs: i16,
39+
tgattr: Vec<i16>,
40+
tgargs: Vec<u8>,
41+
tgqual: Option<String>,
42+
tgoldtable: Option<String>,
43+
tgnewtable: Option<String>,
44+
}

0 commit comments

Comments
 (0)