Skip to content

Commit fc57e89

Browse files
fantixelprans
authored andcommitted
pg-ext: add more SQL func wrappers (#8488)
Adds wrappers for: * `has_database_privilege` * `has_any_column_privilege` (this fixes Metabase compatibility) If the same table name exists in more than one schema, the `has*_privilege()` functions that take an unqualified table name are not yet looking into `search_path` as they are expected to be, but rather choose a "random" one as the result. This is not a problem yet, because Gel user is "superuser" only, it has access to any of the tables with the same name, so the result is inaccurately correct. Also fixes the `pg_database` view. Refs #5307, #5319 Fixes #8457
1 parent 1efca6b commit fc57e89

File tree

3 files changed

+145
-15
lines changed

3 files changed

+145
-15
lines changed

Diff for: edb/pgsql/metaschema.py

+109-15
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ class GetDatabaseFrontendNameFunction(trampoline.VersionedFunction):
12271227
THEN
12281228
substring(db_name, position('_' in db_name) + 1)
12291229
ELSE
1230-
'edgedb'
1230+
'main'
12311231
END
12321232
'''
12331233

@@ -7143,22 +7143,36 @@ def make_wrapper_view(name: str) -> trampoline.VersionedView:
71437143
query="""
71447144
SELECT
71457145
oid,
7146-
edgedb_VER.get_current_database()::name as datname,
7146+
frontend_name.n as datname,
71477147
datdba,
71487148
encoding,
7149+
datlocprovider,
71497150
datcollate,
71507151
datctype,
71517152
datistemplate,
71527153
datallowconn,
7154+
dathasloginevt,
71537155
datconnlimit,
71547156
0::oid AS datlastsysoid,
71557157
datfrozenxid,
71567158
datminmxid,
71577159
dattablespace,
7160+
datlocale,
7161+
daticurules,
7162+
datcollversion,
71587163
datacl,
71597164
tableoid, xmin, cmin, xmax, cmax, ctid
7160-
FROM pg_database
7161-
WHERE datname LIKE '%_edgedb'
7165+
FROM
7166+
pg_database,
7167+
LATERAL (
7168+
SELECT edgedb_VER.get_database_frontend_name(datname) AS n
7169+
) frontend_name,
7170+
LATERAL (
7171+
SELECT edgedb_VER.get_database_metadata(frontend_name.n) AS j
7172+
) metadata
7173+
WHERE
7174+
metadata.j->>'tenant_id' = edgedb_VER.get_backend_tenant_id()
7175+
AND NOT (metadata.j->'builtin')::bool
71627176
""",
71637177
),
71647178

@@ -7692,6 +7706,65 @@ def construct_pg_view(
76927706
views.append(v)
76937707

76947708
util_functions = [
7709+
# WARNING: this `edgedbsql.to_regclass()` function is currently not
7710+
# accurately implemented to take application-level `search_path`
7711+
# into consideration. It is currently here to support the following
7712+
# `has_*privilege` functions (which are less sensitive to such issue).
7713+
# SO DO NOT USE `edgedbsql.to_regclass()` FOR ANYTHING ELSE.
7714+
trampoline.VersionedFunction(
7715+
name=('edgedbsql', 'to_regclass'),
7716+
args=(
7717+
('name', 'text',),
7718+
),
7719+
returns=('regclass',),
7720+
text="""
7721+
SELECT
7722+
CASE
7723+
WHEN array_length(parts, 1) = 1 THEN
7724+
(
7725+
SELECT oid::regclass
7726+
FROM edgedbsql_VER.pg_class
7727+
WHERE relname = parts[1]
7728+
LIMIT 1 -- HACK: see comments above
7729+
)
7730+
WHEN array_length(parts, 1) = 2 THEN
7731+
(
7732+
SELECT pc.oid::regclass
7733+
FROM edgedbsql_VER.pg_class pc
7734+
JOIN edgedbsql_VER.pg_namespace pn
7735+
ON pn.oid = pc.relnamespace
7736+
WHERE relname = parts[2] AND nspname = parts[1]
7737+
)
7738+
ELSE
7739+
NULL::regclass
7740+
END
7741+
FROM parse_ident(name) parts
7742+
"""
7743+
),
7744+
trampoline.VersionedFunction(
7745+
name=('edgedbsql', 'has_database_privilege'),
7746+
args=(
7747+
('database_name', 'text'),
7748+
('privilege', 'text'),
7749+
),
7750+
returns=('bool',),
7751+
text="""
7752+
SELECT has_database_privilege(oid, privilege)
7753+
FROM edgedbsql_VER.pg_database
7754+
WHERE datname = database_name
7755+
"""
7756+
),
7757+
trampoline.VersionedFunction(
7758+
name=('edgedbsql', 'has_database_privilege'),
7759+
args=(
7760+
('database_oid', 'oid'),
7761+
('privilege', 'text'),
7762+
),
7763+
returns=('bool',),
7764+
text="""
7765+
SELECT has_database_privilege(database_oid, privilege)
7766+
"""
7767+
),
76957768
trampoline.VersionedFunction(
76967769
name=('edgedbsql', 'has_schema_privilege'),
76977770
args=(
@@ -7728,20 +7801,19 @@ def construct_pg_view(
77287801
),
77297802
returns=('bool',),
77307803
text="""
7731-
SELECT has_table_privilege(oid, privilege)
7732-
FROM edgedbsql_VER.pg_class
7733-
WHERE relname = table_name;
7804+
SELECT has_table_privilege(
7805+
edgedbsql_VER.to_regclass(table_name), privilege)
77347806
"""
77357807
),
77367808
trampoline.VersionedFunction(
77377809
name=('edgedbsql', 'has_table_privilege'),
77387810
args=(
7739-
('schema_oid', 'oid'),
7811+
('table_oid', 'oid'),
77407812
('privilege', 'text'),
77417813
),
77427814
returns=('bool',),
77437815
text="""
7744-
SELECT has_table_privilege(schema_oid, privilege)
7816+
SELECT has_table_privilege(table_oid, privilege)
77457817
"""
77467818
),
77477819

@@ -7766,9 +7838,8 @@ def construct_pg_view(
77667838
),
77677839
returns=('bool',),
77687840
text="""
7769-
SELECT has_column_privilege(oid, col, privilege)
7770-
FROM edgedbsql_VER.pg_class
7771-
WHERE relname = tbl;
7841+
SELECT has_column_privilege(
7842+
edgedbsql_VER.to_regclass(tbl), col, privilege)
77727843
"""
77737844
),
77747845
trampoline.VersionedFunction(
@@ -7795,9 +7866,32 @@ def construct_pg_view(
77957866
returns=('bool',),
77967867
text="""
77977868
SELECT has_column_privilege(pc.oid, attnum_internal, privilege)
7798-
FROM edgedbsql_VER.pg_class pc
7799-
JOIN edgedbsql_VER.pg_attribute_ext pa ON pa.attrelid = pc.oid
7800-
WHERE pc.relname = tbl AND pa.attname = col;
7869+
FROM edgedbsql_VER.pg_attribute_ext pa,
7870+
LATERAL (SELECT edgedbsql_VER.to_regclass(tbl) AS oid) pc
7871+
WHERE pa.attrelid = pc.oid AND pa.attname = col
7872+
"""
7873+
),
7874+
trampoline.VersionedFunction(
7875+
name=('edgedbsql', 'has_any_column_privilege'),
7876+
args=(
7877+
('tbl', 'oid'),
7878+
('privilege', 'text'),
7879+
),
7880+
returns=('bool',),
7881+
text="""
7882+
SELECT has_any_column_privilege(tbl, privilege)
7883+
"""
7884+
),
7885+
trampoline.VersionedFunction(
7886+
name=('edgedbsql', 'has_any_column_privilege'),
7887+
args=(
7888+
('tbl', 'text'),
7889+
('privilege', 'text'),
7890+
),
7891+
returns=('bool',),
7892+
text="""
7893+
SELECT has_any_column_privilege(
7894+
edgedbsql_VER.to_regclass(tbl), privilege)
78017895
"""
78027896
),
78037897
trampoline.VersionedFunction(

Diff for: edb/pgsql/resolver/static.py

+2
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,11 @@ def eval_FuncCall(
383383
# schema and table names need to be remapped. This is accomplished
384384
# with wrapper functions defined in metaschema.py.
385385
has_wrapper = {
386+
'has_database_privilege',
386387
'has_schema_privilege',
387388
'has_table_privilege',
388389
'has_column_privilege',
390+
'has_any_column_privilege',
389391
}
390392
if fn_name in has_wrapper:
391393
return pgast.FuncCall(name=(V('edgedbsql'), fn_name), args=fn_args)

Diff for: tests/test_sql_query.py

+34
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,40 @@ async def test_sql_query_be_state(self):
16221622
finally:
16231623
await con.aclose()
16241624

1625+
async def test_sql_query_privileges_01(self):
1626+
1627+
res = await self.squery_values(
1628+
'''
1629+
select has_database_privilege($1, 'CONNECT');
1630+
''',
1631+
self.con.dbname,
1632+
)
1633+
self.assertEqual(res, [[True]])
1634+
1635+
async def test_sql_query_privileges_02(self):
1636+
res = await self.squery_values(
1637+
'''
1638+
select has_table_privilege('"Movie"', 'SELECT');
1639+
'''
1640+
)
1641+
self.assertEqual(res, [[True]])
1642+
1643+
async def test_sql_query_privileges_03(self):
1644+
res = await self.squery_values(
1645+
'''
1646+
select has_column_privilege('"Movie"', 'title', 'SELECT');
1647+
'''
1648+
)
1649+
self.assertEqual(res, [[True]])
1650+
1651+
async def test_sql_query_privileges_04(self):
1652+
res = await self.squery_values(
1653+
'''
1654+
select has_any_column_privilege('"Movie"', 'SELECT');
1655+
'''
1656+
)
1657+
self.assertEqual(res, [[True]])
1658+
16251659
async def test_sql_query_client_encoding_1(self):
16261660
self.assertEqual(
16271661
self.scon.get_settings().client_encoding.lower(), "utf_8"

0 commit comments

Comments
 (0)