Skip to content

Commit a8bb769

Browse files
authored
Merge pull request #5 from DanCardin/dc/schema-grants
2 parents 001c213 + 9e85949 commit a8bb769

30 files changed

Lines changed: 409 additions & 60 deletions

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "sqlalchemy-declarative-extensions"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
description = "Library to declare additional kinds of objects not natively supported by SqlAlchemy/Alembic."
55

66
authors = ["Dan Cardin <ddcardin@gmail.com>"]

src/sqlalchemy_declarative_extensions/alembic/row.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
@comparators.dispatch_for("schema")
2323
def compare_rows(autogen_context: AutogenContext, upgrade_ops: UpgradeOps, _):
24-
if autogen_context.metadata is None or autogen_context.connection is None:
24+
if (
25+
autogen_context.metadata is None or autogen_context.connection is None
26+
): # pragma: no cover
2527
return
2628

2729
rows: Optional[Rows] = autogen_context.metadata.info.get("rows")

src/sqlalchemy_declarative_extensions/api.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from sqlalchemy import event
66
from sqlalchemy.orm import DeclarativeMeta
77
from sqlalchemy.sql.schema import MetaData
8+
from typing_extensions import Protocol
89

910
from sqlalchemy_declarative_extensions.grant.base import Grants
1011
from sqlalchemy_declarative_extensions.role.base import Roles
@@ -115,8 +116,12 @@ def declare_database(
115116
metadata.info["rows"] = Rows.coerce_from_unknown(rows)
116117

117118

119+
class HasMetaData(Protocol):
120+
metadata: MetaData
121+
122+
118123
def register_sqlalchemy_events(
119-
maybe_metadata: MetaData | DeclarativeMeta,
124+
maybe_metadata: Union[MetaData, HasMetaData],
120125
schemas=False,
121126
roles=False,
122127
grants=False,

src/sqlalchemy_declarative_extensions/compat.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/sqlalchemy_declarative_extensions/dialects/from_string.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,4 @@ def from_string(
2424
return string
2525

2626
def __lt__(self, other):
27-
if isinstance(other, self.__class__):
28-
return self.value < other.value
29-
return self.value < other
27+
return self.value < other.value

src/sqlalchemy_declarative_extensions/dialects/postgresql/grant_type.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class SchemaGrants(GrantOptions):
156156
def acl_symbols(cls):
157157
return {
158158
cls.create: "C",
159-
cls.usage: "C",
159+
cls.usage: "U",
160160
}
161161

162162

@@ -218,12 +218,6 @@ def _str_to_kind(cls):
218218
def from_relkind(cls, relkind: str):
219219
return cls._str_to_kind()[relkind]
220220

221-
def to_relkind(self) -> str:
222-
for k, v in self._str_to_kind().items():
223-
if v == self:
224-
return k
225-
raise NotImplementedError() # pragma: nocover
226-
227221
def to_variants(self):
228222
return {
229223
self.database: DatabaseGrants,
@@ -255,12 +249,6 @@ def _str_to_kind(cls):
255249
def from_relkind(cls, relkind: str):
256250
return cls._str_to_kind()[relkind]
257251

258-
def to_relkind(self) -> str:
259-
for k, v in self._str_to_kind().items():
260-
if v == self:
261-
return k
262-
raise NotImplementedError() # pragma: nocover
263-
264252
def to_variants(self):
265253
return {
266254
self.table: TableGrants,

src/sqlalchemy_declarative_extensions/dialects/postgresql/query.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def get_default_grants_postgresql(
6262
for default_grant in default_grants:
6363
if roles is None or default_grant.grant.target_role in roles:
6464
result.append(default_grant)
65+
6566
return result
6667

6768

@@ -89,6 +90,7 @@ def get_grants_postgresql(
8990
for grant in grants:
9091
if roles is None or grant.grant.target_role in roles:
9192
result.append(grant)
93+
9294
return result
9395

9496

@@ -104,6 +106,6 @@ def get_roles_postgresql(connection: Connection, exclude=None):
104106

105107

106108
def qualify_name(schema, name):
107-
if schema == "public":
109+
if not schema or schema == "public":
108110
return name
109111
return f"{schema}.{name}"

src/sqlalchemy_declarative_extensions/dialects/postgresql/role.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def from_pg_role(cls, r) -> Role:
4747

4848
@classmethod
4949
def from_unknown_role(cls, r: Union[generic.Role, Role]) -> Role:
50-
if isinstance(r, generic.Role):
50+
if not isinstance(r, Role):
5151
return Role(r.name, in_roles=r.in_roles)
5252

5353
return r

src/sqlalchemy_declarative_extensions/dialects/postgresql/schema.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from sqlalchemy import column, select, String, table, text
1+
from sqlalchemy import and_, column, literal, select, String, table, text, union
22
from sqlalchemy.dialects.postgresql import ARRAY
33

44
pg_class = table(
@@ -15,6 +15,8 @@
1515
"pg_namespace",
1616
column("oid"),
1717
column("nspname"),
18+
column("nspowner"),
19+
column("nspacl"),
1820
)
1921

2022
pg_roles = table(
@@ -55,10 +57,15 @@
5557
"""
5658
)
5759

60+
_schema_not_pg = and_(
61+
pg_namespace.c.nspname != "information_schema",
62+
pg_namespace.c.nspname.not_like("pg_%"),
63+
)
64+
_schema_not_public = pg_namespace.c.nspname != "public"
65+
_table_not_pg = pg_class.c.relname.not_like("pg_%")
66+
5867
schemas_query = (
59-
select(pg_namespace.c.nspname)
60-
.where(pg_namespace.c.nspname.not_in(["information_schema", "public"]))
61-
.where(pg_namespace.c.nspname.not_like("pg_%"))
68+
select(pg_namespace.c.nspname).where(_schema_not_pg).where(_schema_not_public)
6269
)
6370

6471

@@ -78,7 +85,7 @@
7885
)
7986
)
8087

81-
object_acl_query = (
88+
object_acl_query = union(
8289
select(
8390
pg_namespace.c.nspname.label("schema"),
8491
pg_class.c.relname.label("name"),
@@ -92,8 +99,20 @@
9299
)
93100
)
94101
.where(pg_class.c.relkind.in_(["r", "S", "f", "n", "T"]))
95-
.where(pg_class.c.relname.not_like("pg_%"))
96-
.where(pg_namespace.c.nspname != "information_schema")
102+
.where(_table_not_pg)
103+
.where(_schema_not_pg),
104+
select(
105+
literal(None).label("schema"),
106+
pg_namespace.c.nspname.label("name"),
107+
literal("n").label("relkind"),
108+
pg_authid.c.rolname.label("owner"),
109+
pg_namespace.c.nspacl.cast(ARRAY(String)),
110+
)
111+
.select_from(
112+
pg_namespace.join(pg_authid, pg_namespace.c.nspowner == pg_authid.c.oid)
113+
)
114+
.where(_schema_not_pg)
115+
.where(_schema_not_public),
97116
)
98117

99118
objects_query = (
@@ -106,6 +125,6 @@
106125
pg_class.join(pg_namespace, pg_class.c.relnamespace == pg_namespace.c.oid)
107126
)
108127
.where(pg_class.c.relkind.in_(["r", "S", "f", "n", "T"]))
109-
.where(pg_class.c.relname.not_like("pg_%"))
110-
.where(pg_namespace.c.nspname != "information_schema")
128+
.where(_table_not_pg)
129+
.where(_schema_not_pg)
111130
)

src/sqlalchemy_declarative_extensions/grant/ddl.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010

1111
def grant_ddl(grants: Grants, after: bool):
1212
def receive_event(metadata: MetaData, connection: Connection, **_):
13-
grants: Optional[Grants] = metadata.info.get("grants")
14-
if not grants:
15-
return
16-
1713
roles: Optional[Roles] = metadata.info.get("roles")
1814
result = compare_grants(connection, grants, roles=roles)
1915
for op in result:

0 commit comments

Comments
 (0)