Skip to content

Commit 5ab89f7

Browse files
authored
Add permissions to schema. (#8736)
Allows creation of custom permissions within the schema: ``` Permission foo; ``` Permissions can be used in general expressions by using the `global` keyword: ``` select global foo; ``` Currently, all such expressions evaluate to `false`.
1 parent 6855914 commit 5ab89f7

File tree

19 files changed

+671
-56
lines changed

19 files changed

+671
-56
lines changed

edb/edgeql-parser/src/ast.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ pub enum CreateObjectKind {
10101010
CreateConcretePointer(CreateConcretePointer),
10111011
CreateAlias(CreateAlias),
10121012
CreateGlobal(CreateGlobal),
1013+
CreatePermission(CreatePermission),
10131014
CreateConcreteConstraint(CreateConcreteConstraint),
10141015
CreateConcreteIndex(CreateConcreteIndex),
10151016
CreateAnnotationValue(CreateAnnotationValue),
@@ -1044,6 +1045,7 @@ pub enum AlterObjectKind {
10441045
AlterObjectType(AlterObjectType),
10451046
AlterAlias(AlterAlias),
10461047
AlterGlobal(AlterGlobal),
1048+
AlterPermission(AlterPermission),
10471049
AlterLink(AlterLink),
10481050
AlterConcreteLink(AlterConcreteLink),
10491051
AlterConstraint(AlterConstraint),
@@ -1084,6 +1086,7 @@ pub enum DropObjectKind {
10841086
DropObjectType(DropObjectType),
10851087
DropAlias(DropAlias),
10861088
DropGlobal(DropGlobal),
1089+
DropPermission(DropPermission),
10871090
DropLink(DropLink),
10881091
DropConcreteLink(DropConcreteLink),
10891092
DropConstraint(DropConstraint),
@@ -1426,6 +1429,18 @@ pub struct SetGlobalType {
14261429
pub reset_value: bool,
14271430
}
14281431

1432+
#[derive(Debug, Clone)]
1433+
#[cfg_attr(feature = "python", derive(IntoPython))]
1434+
pub struct CreatePermission {}
1435+
1436+
#[derive(Debug, Clone)]
1437+
#[cfg_attr(feature = "python", derive(IntoPython))]
1438+
pub struct AlterPermission {}
1439+
1440+
#[derive(Debug, Clone)]
1441+
#[cfg_attr(feature = "python", derive(IntoPython))]
1442+
pub struct DropPermission {}
1443+
14291444
#[derive(Debug, Clone)]
14301445
#[cfg_attr(feature = "python", derive(IntoPython))]
14311446
pub struct CreateLink {}
@@ -2017,6 +2032,7 @@ pub enum SchemaObjectClass {
20172032
MODULE,
20182033
OPERATOR,
20192034
PARAMETER,
2035+
PERMISSION,
20202036
PROPERTY,
20212037
PSEUDO_TYPE,
20222038
RANGE_TYPE,

edb/edgeql-parser/src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub const UNRESERVED_KEYWORDS: phf::Set<&str> = phf_set!(
6868
"overloaded",
6969
"owned",
7070
"package",
71+
"permission",
7172
"policy",
7273
"populate",
7374
"postfix",

edb/edgeql/ast.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,23 @@ class SetGlobalType(SetField):
11661166
reset_value: bool = False
11671167

11681168

1169+
class PermissionCommand(ObjectDDL):
1170+
1171+
__abstract_node__ = True
1172+
1173+
1174+
class CreatePermission(CreateObject, PermissionCommand):
1175+
pass
1176+
1177+
1178+
class AlterPermission(AlterObject, PermissionCommand):
1179+
pass
1180+
1181+
1182+
class DropPermission(DropObject, PermissionCommand):
1183+
pass
1184+
1185+
11691186
class LinkCommand(ObjectDDL):
11701187

11711188
__abstract_node__ = True

edb/edgeql/codegen.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,6 +2465,15 @@ def visit_AlterGlobal(self, node: qlast.AlterGlobal) -> None:
24652465
def visit_DropGlobal(self, node: qlast.DropGlobal) -> None:
24662466
self._visit_DropObject(node, 'GLOBAL')
24672467

2468+
def visit_CreatePermission(self, node: qlast.CreatePermission) -> None:
2469+
self._visit_CreateObject(node, 'PERMISSION')
2470+
2471+
def visit_AlterPermission(self, node: qlast.AlterPermission) -> None:
2472+
self._visit_AlterObject(node, 'PERMISSION')
2473+
2474+
def visit_DropPermission(self, node: qlast.DropPermission) -> None:
2475+
self._visit_DropObject(node, 'PERMISSION')
2476+
24682477
def visit_ConfigSet(self, node: qlast.ConfigSet) -> None:
24692478
if node.scope == qltypes.ConfigScope.GLOBAL:
24702479
self._write_keywords('SET GLOBAL ')

edb/edgeql/compiler/expr.py

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@
3939
from edb.schema import globals as s_globals
4040
from edb.schema import indexes as s_indexes
4141
from edb.schema import name as sn
42+
from edb.schema import objects as so
4243
from edb.schema import objtypes as s_objtypes
44+
from edb.schema import permissions as s_permissions
4345
from edb.schema import pseudo as s_pseudo
4446
from edb.schema import scalars as s_scalars
47+
from edb.schema import schema as s_schema
4548
from edb.schema import types as s_types
4649
from edb.schema import utils as s_utils
4750
from edb.schema import expr as s_expr
@@ -549,10 +552,56 @@ def compile_UnaryOp(
549552
def compile_GlobalExpr(
550553
expr: qlast.GlobalExpr, *, ctx: context.ContextLevel
551554
) -> irast.Set:
552-
glob = ctx.env.get_schema_object_and_track(
553-
s_utils.ast_ref_to_name(expr.name), expr.name,
554-
modaliases=ctx.modaliases, type=s_globals.Global)
555-
assert isinstance(glob, s_globals.Global)
555+
# The expr object can be either a Permission or Global.
556+
# Get an Object and manually check for correct type and None.
557+
expr_schema_name = s_utils.ast_ref_to_name(expr.name)
558+
expr_obj = ctx.env.get_schema_object_and_track(
559+
expr_schema_name,
560+
expr.name,
561+
default=None,
562+
modaliases=ctx.modaliases,
563+
type=so.Object,
564+
)
565+
566+
# Check for None first.
567+
if expr_obj is None:
568+
# If no object is found, we want to raise an error with 'global' as
569+
# the desired type.
570+
# If we let `get_schema_object_and_track`, the error will contain
571+
# 'object' instead.
572+
s_schema.Schema.raise_bad_reference(
573+
expr_schema_name,
574+
module_aliases=ctx.modaliases,
575+
span=expr.span,
576+
type=s_globals.Global,
577+
)
578+
579+
# Check for permission
580+
if isinstance(expr_obj, s_permissions.Permission):
581+
std_type = sn.QualName('std', 'bool')
582+
ct = typegen.type_to_typeref(
583+
ctx.env.get_schema_type_and_track(std_type),
584+
env=ctx.env,
585+
)
586+
ir_expr = irast.BooleanConstant(
587+
value="false", typeref=ct, span=expr.span
588+
)
589+
return setgen.ensure_set(ir_expr, ctx=ctx)
590+
591+
# Check for non-global
592+
if not isinstance(expr_obj, s_globals.Global):
593+
s_schema.Schema.raise_wrong_type(
594+
expr_schema_name,
595+
expr_obj.__class__,
596+
s_globals.Global,
597+
span=expr.span,
598+
)
599+
# Raise an error here so mypy knows that expr_obj can only be a global
600+
# past this point.
601+
raise AssertionError('should never happen')
602+
603+
# Non-permission global
604+
glob = expr_obj
556605

557606
if glob.is_computable(ctx.env.schema):
558607
obj_ref = s_utils.name_to_ast_ref(

edb/edgeql/declarative.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ def sdl_to_ddl(
404404
ctx.objects[fq_name] = qltracer.Annotation(fq_name)
405405
elif isinstance(decl_ast, qlast.CreateGlobal):
406406
ctx.objects[fq_name] = qltracer.Global(fq_name)
407+
elif isinstance(decl_ast, qlast.CreatePermission):
408+
ctx.objects[fq_name] = qltracer.Permission(fq_name)
407409
elif isinstance(decl_ast, qlast.CreateIndex):
408410
ctx.objects[fq_name] = qltracer.Index(fq_name)
409411
else:
@@ -1046,6 +1048,21 @@ def trace_Global(
10461048
_register_item(node, hard_dep_exprs=deps, ctx=ctx)
10471049

10481050

1051+
@trace_dependencies.register
1052+
def trace_Permission(
1053+
node: qlast.CreatePermission,
1054+
*,
1055+
ctx: DepTraceContext,
1056+
) -> None:
1057+
deps: list[Dependency] = [
1058+
TypeDependency(texpr=qlast.TypeName(
1059+
maintype=qlast.ObjectRef(module='__std__', name='bool')
1060+
))
1061+
]
1062+
1063+
_register_item(node, hard_dep_exprs=deps, ctx=ctx)
1064+
1065+
10491066
@trace_dependencies.register
10501067
def trace_Function(
10511068
node: qlast.CreateFunction,
@@ -1602,6 +1619,8 @@ def _get_tracer_type(
16021619
elif isinstance(decl, (qlast.CreateLink,
16031620
qlast.CreateConcreteLink)):
16041621
tracer_type = qltracer.Link
1622+
elif isinstance(decl, qlast.CreatePermission):
1623+
tracer_type = qltracer.Permission
16051624
elif isinstance(decl, (qlast.CreateIndex,
16061625
qlast.CreateConcreteIndex)):
16071626
tracer_type = qltracer.Index

edb/edgeql/parser/grammar/ddl.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,18 @@ def reduce_AlterGlobalStmt(self, *_):
248248
def reduce_DropGlobalStmt(self, *_):
249249
pass
250250

251+
@parsing.inline(0)
252+
def reduce_CreatePermissionStmt(self, *_):
253+
pass
254+
255+
@parsing.inline(0)
256+
def reduce_AlterPermissionStmt(self, *_):
257+
pass
258+
259+
@parsing.inline(0)
260+
def reduce_DropPermissionStmt(self, *_):
261+
pass
262+
251263
@parsing.inline(0)
252264
def reduce_DropCastStmt(self, *_):
253265
pass
@@ -3600,6 +3612,67 @@ def reduce_DropGlobal(self, *kids):
36003612
name=kids[2].val
36013613
)
36023614

3615+
3616+
#
3617+
# CREATE PERMISSION
3618+
#
3619+
commands_block(
3620+
'CreatePermission',
3621+
CreateAnnotationValueStmt,
3622+
)
3623+
3624+
3625+
class CreatePermissionStmt(Nonterm):
3626+
def reduce_CreatePermission(self, *kids):
3627+
"""%reduce
3628+
CREATE PERMISSION NodeName
3629+
OptCreatePermissionCommandsBlock
3630+
"""
3631+
_, _, name, commands = kids
3632+
self.val = qlast.CreatePermission(
3633+
name=name.val,
3634+
commands=commands.val,
3635+
)
3636+
3637+
3638+
#
3639+
# ALTER PERMISSION
3640+
#
3641+
commands_block(
3642+
'AlterPermission',
3643+
CreateAnnotationValueStmt,
3644+
AlterAnnotationValueStmt,
3645+
DropAnnotationValueStmt,
3646+
RenameStmt,
3647+
opt=False
3648+
)
3649+
3650+
3651+
class AlterPermissionStmt(Nonterm):
3652+
def reduce_AlterPermission(self, *kids):
3653+
r"""%reduce \
3654+
ALTER PERMISSION NodeName \
3655+
AlterPermissionCommandsBlock \
3656+
"""
3657+
_, _, name, commands = kids
3658+
self.val = qlast.AlterPermission(
3659+
name=name.val,
3660+
commands=commands.val,
3661+
)
3662+
3663+
3664+
#
3665+
# DROP PERMISSION
3666+
#
3667+
class DropPermissionStmt(Nonterm):
3668+
def reduce_DropPermission(self, *kids):
3669+
r"""%reduce DROP PERMISSION NodeName"""
3670+
_, _, name = kids
3671+
self.val = qlast.DropPermission(
3672+
name=name.val
3673+
)
3674+
3675+
36033676
#
36043677
# MIGRATIONS
36053678
#

edb/edgeql/parser/grammar/sdl.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ def reduce_GlobalDeclaration(self, *kids):
103103
def reduce_IndexDeclaration(self, *kids):
104104
pass
105105

106+
@parsing.inline(0)
107+
def reduce_PermissionDeclaration(self, *kids):
108+
pass
109+
106110

107111
# these statements have no {} block
108112
class SDLShortStatement(Nonterm):
@@ -155,6 +159,10 @@ def reduce_GlobalDeclarationShort(self, *kids):
155159
def reduce_IndexDeclarationShort(self, *kids):
156160
pass
157161

162+
@parsing.inline(0)
163+
def reduce_PermissionDeclarationShort(self, *kids):
164+
pass
165+
158166

159167
# A rule for an SDL block, either as part of `module` declaration or
160168
# as top-level schema used in MIGRATION DDL.
@@ -1949,3 +1957,38 @@ def reduce_CreateComputedGlobalShort(self, *kids):
19491957
name=name.val,
19501958
target=expr.val,
19511959
)
1960+
1961+
1962+
#
1963+
# Permissions
1964+
#
1965+
1966+
1967+
sdl_commands_block(
1968+
'CreatePermission',
1969+
SetAnnotation,
1970+
)
1971+
1972+
1973+
class PermissionDeclaration(Nonterm):
1974+
def reduce_CreatePermission(self, *kids):
1975+
"""%reduce
1976+
PERMISSION NodeName
1977+
CreatePermissionSDLCommandsBlock
1978+
"""
1979+
_, name, commands = kids
1980+
self.val = qlast.CreatePermission(
1981+
name=name.val,
1982+
commands=commands.val,
1983+
)
1984+
1985+
1986+
class PermissionDeclarationShort(Nonterm):
1987+
def reduce_CreatePermission(self, *kids):
1988+
"""%reduce
1989+
PERMISSION NodeName
1990+
"""
1991+
_, name = kids
1992+
self.val = qlast.CreatePermission(
1993+
name=name.val,
1994+
)

edb/edgeql/qltypes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ class SchemaObjectClass(s_enum.StrEnum):
290290
MULTIRANGE_TYPE = 'MULTIRANGE_TYPE'
291291
OPERATOR = 'OPERATOR'
292292
PARAMETER = 'PARAMETER'
293+
PERMISSION = 'PERMISSION'
293294
PROPERTY = 'PROPERTY'
294295
PSEUDO_TYPE = 'PSEUDO TYPE'
295296
RANGE_TYPE = 'RANGE TYPE'

edb/edgeql/tracer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ class Global(NamedObject):
8989
pass
9090

9191

92+
class Permission(NamedObject):
93+
pass
94+
95+
9296
class Index(NamedObject):
9397
pass
9498

0 commit comments

Comments
 (0)