def from_ast(cls, qltree, schema, modaliases): norm_tree = imprint_expr_context(qltree, modaliases) norm_text = qlcodegen.generate_source(norm_tree, pretty=False) return cls( text=norm_text, origtext=qlcodegen.generate_source(qltree), _qlast=norm_tree, )
def from_ast(cls, qltree, schema, modaliases, *, as_fragment=False): orig_text = qlcodegen.generate_source(qltree, pretty=False) if not as_fragment: qltree = imprint_expr_context(qltree, modaliases) norm_text = qlcodegen.generate_source(qltree, pretty=False) return cls( text=norm_text, origtext=orig_text, _qlast=qltree, )
def _parse_field_setters(self, scls, field_decls: typing.List[qlast.Field]): fields = type(scls).get_fields() updates = {} for field_decl in field_decls: fieldname = field_decl.name.name attrfield = fields.get(fieldname) if attrfield is None or not attrfield.allow_ddl_set: raise errors.SchemaError(f'unexpected field {fieldname}', context=field_decl.context) if issubclass(attrfield.type, s_expr.Expression): updates[fieldname] = s_expr.Expression( text=qlcodegen.generate_source(field_decl.value)) else: updates[fieldname] = qlcompiler.evaluate_ast_to_python_val( field_decl.value, self._schema, modaliases=self._mod_aliases) if updates: self._schema = scls.update(self._schema, updates)
def handle_signature(self, sig, signode): if debug.flags.disable_docs_edgeql_validation: signode['eql-fullname'] = fullname = re.split(r'\(| ', sig)[0] signode['eql-signature'] = sig mod, name = fullname.split('::') signode['eql-module'] = mod signode['eql-name'] = name return fullname from edb.edgeql.parser import parser as edgeql_parser from edb.edgeql import ast as ql_ast from edb.edgeql import codegen as ql_gen parser = edgeql_parser.EdgeQLBlockParser() try: astnode = parser.parse(f'create abstract constraint {sig};')[0] except Exception as ex: raise self.error( f'could not parse constraint signature {sig!r}') from ex if (not isinstance(astnode, ql_ast.CreateConstraint) or not isinstance(astnode.name, ql_ast.ObjectRef)): raise self.error(f'EdgeQL parser returned unsupported AST') modname = astnode.name.module constr_name = astnode.name.name if not modname: raise self.error( f'Missing module in EdgeQL constraint declaration') constr_repr = ql_gen.generate_source(astnode) m = re.match( r'''(?xs) ^ create\sabstract\sconstraint\s (?P<f>.*?)(?:\s*on(?P<subj>.*))? $ ''', constr_repr) if not m or not m.group('f'): raise self.error( f'could not recreate constraint signature from AST') constr_repr = m.group('f') signode['eql-module'] = modname signode['eql-name'] = constr_name signode['eql-fullname'] = fullname = f'{modname}::{constr_name}' signode['eql-signature'] = constr_repr subject = m.group('subj') if subject: subject = subject.strip()[1:-1] signode['eql-subjexpr'] = subject signode['eql-signature'] += f' on ({subject})' signode += s_nodes.desc_annotation('constraint', 'constraint') signode += d_nodes.Text(' ') signode += s_nodes.desc_name(fullname, fullname) return fullname
def value_to_edgeql_const(setting: spec.Setting, value: Any) -> str: if isinstance(setting.type, types.ConfigType): raise NotImplementedError( 'cannot render non-scalar configuration value') ql = s_utils.const_ast_from_python(value) return qlcodegen.generate_source(ql)
def _compile_view(self, viewdecl): view_ql = None for field_decl in viewdecl.fields: fieldname = field_decl.name.name if fieldname == 'expr': view_ql = field_decl.value break if view_ql is None: raise errors.SchemaError( 'missing required expression in view definition', context=viewdecl.context, ) if not isinstance(view_ql, qlast.Statement): view_ql = qlast.SelectQuery(result=view_ql) viewname = s_name.Name(module=self._module.get_name(self._schema), name=viewdecl.name) ir = qlcompiler.compile_ast_to_ir( view_ql, self._schema, derived_target_module=self._module.get_name(self._schema), modaliases=self._mod_aliases, result_view_name=viewname, schema_view_mode=True) self._schema = ir.schema scls = self._schema.get(viewname) self._parse_field_setters(scls, viewdecl.fields) existing_aliases = {} for alias in view_ql.aliases: if isinstance(alias, qlast.ModuleAliasDecl): existing_aliases[alias.alias] = alias.module aliases_to_add = set(self._mod_aliases) - set(existing_aliases) for alias in aliases_to_add: view_ql.aliases.append( qlast.ModuleAliasDecl( alias=alias, module=self._mod_aliases[alias], )) view_expr = qlcodegen.generate_source(view_ql, pretty=False) self._schema = scls.set_field_value(self._schema, 'expr', s_expr.Expression(text=view_expr)) self._schema = scls.set_field_value(self._schema, 'view_type', s_types.ViewType.Select)
def _init_constraints(self, constraints): for constraint, decl in constraints.items(): attrs = {a.name.name: a.value for a in decl.fields} assert 'subject' not in attrs # TODO: Add proper validation assert 'subjectexpr' not in attrs # TODO: Add proper validation expr = attrs.pop('expr', None) if expr is not None: self._schema = constraint.set_field_value( self._schema, 'expr', s_expr.Expression(text=qlcodegen.generate_source(expr))) subjexpr = decl.subject if subjexpr is not None: self._schema = constraint.set_field_value( self._schema, 'subjectexpr', s_expr.Expression( text=qlcodegen.generate_source(subjexpr))) self._schema, params = s_func.FuncParameterList.from_ast( self._schema, decl, self._mod_aliases, func_fqname=constraint.get_name(self._schema)) for param in params.objects(self._schema): p_kind = param.get_kind(self._schema) if p_kind is qltypes.ParameterKind.NAMED_ONLY: raise errors.InvalidConstraintDefinitionError( 'named only parameters are not allowed ' 'in this context', context=decl.context) if param.get_default(self._schema) is not None: raise errors.InvalidConstraintDefinitionError( 'constraints do not support parameters ' 'with defaults', context=decl.context) self._schema = constraint.set_field_value(self._schema, 'params', params)
def _parse_subject_constraints(self, subject, subjdecl): # Perform initial collection of constraints defined in subject context. # At this point all referenced constraints should be fully initialized. for constrdecl in subjdecl.constraints: attrs = {a.name.name: a.value for a in constrdecl.fields} assert 'subject' not in attrs # TODO: Add proper validation constr_name = self._get_ref_name(constrdecl.name) if constrdecl.args: args = [ s_expr.Expression( text=qlcodegen.generate_source(arg, pretty=False)) for arg in constrdecl.args ] else: args = [] modaliases = {None: subject.get_name(self._schema).module} if constrdecl.subject is not None: subjectexpr = s_expr.Expression( text=qlcodegen.generate_source(constrdecl.subject)) else: subjectexpr = None self._schema, c, _ = \ s_constr.Constraint.create_concrete_constraint( self._schema, subject, name=constr_name, is_abstract=constrdecl.delegated, sourcectx=constrdecl.context, subjectexpr=subjectexpr, args=args, modaliases=modaliases, ) self._schema = subject.add_constraint(self._schema, c)
def trace_ConcreteConstraint(node: qlast.CreateConcreteConstraint, *, ctx: DepTraceContext): deps = set() base_name = ctx.get_ref_name(node.name) if base_name.module == ctx.module: deps.add(base_name) exprs = list(node.args) if node.subjectexpr: exprs.append(node.subjectexpr) for cmd in node.commands: if isinstance(cmd, qlast.SetField) and cmd.name == "expr": exprs.append(node.value) if isinstance(ctx.depstack[-1][0], qlast.AlterScalarType): # Scalars are tightly bound to their constraints, so # we must prohibit any possible reference to this scalar # type from within the constraint. loop_control = ctx.depstack[-1][1] else: loop_control = None if exprs: texts = [qlcodegen.generate_source(e) for e in exprs] m = hashlib.sha1() m.update('|'.join(texts).encode()) extra_name = m.hexdigest() else: extra_name = None _register_item( node, deps=deps, hard_dep_exprs=exprs, loop_control=loop_control, subject=ctx.depstack[-1][1], extra_name=extra_name, ctx=ctx, )
def get_fq_name(self, decl: qlast.CreateObject) -> Tuple[str, str]: # Get the basic name form. if isinstance(decl, qlast.CreateConcretePointer): name = decl.name.name elif isinstance(decl, qlast.BaseSetField): name = decl.name else: name = self.get_local_name(decl.name) # The name may need to be augmented if it's a pointer. if self.depstack: fq_name = self.depstack[-1][1] + "@" + name else: fq_name = name # Additionally, functions and concrete constraints may need an # extra name piece. extra_name = None if isinstance(decl, qlast.CreateFunction): # Functions are defined by their name + call signature, so we # need to add that to the "extra_name". extra_name = f'({qlcodegen.generate_source(decl.params)})' elif isinstance(decl, qlast.CreateConcreteConstraint): # Concrete constraints are defined by their expr, so we need # to add that to the "extra_name". exprs = list(decl.args) if decl.subjectexpr: exprs.append(decl.subjectexpr) for cmd in decl.commands: if isinstance(cmd, qlast.SetField) and cmd.name == "expr": exprs.append(decl.value) extra_name = '|'.join(qlcodegen.generate_source(e) for e in exprs) if extra_name is not None: fq_name = f'{fq_name}:{extra_name}' return name, fq_name
def handle_signature(self, sig, signode): if debug.flags.disable_docs_edgeql_validation: signode['eql-fullname'] = fullname = sig.split('(')[0] signode['eql-signature'] = sig mod, name = fullname.split('::') signode['eql-module'] = mod signode['eql-name'] = name return fullname from edb.edgeql.parser import parser as edgeql_parser from edb.edgeql import ast as ql_ast from edb.edgeql import codegen as ql_gen from edb.edgeql import qltypes parser = edgeql_parser.EdgeQLBlockParser() try: astnode = parser.parse( f'create function {sig} using SQL function "xxx";')[0] except Exception as ex: raise self.error( f'could not parse function signature {sig!r}') from ex if (not isinstance(astnode, ql_ast.CreateFunction) or not isinstance(astnode.name, ql_ast.ObjectRef)): raise self.error(f'EdgeQL parser returned unsupported AST') modname = astnode.name.module funcname = astnode.name.name if not modname: raise self.error( f'EdgeQL function declaration is missing namespace') func_repr = ql_gen.generate_source(astnode) m = re.match( r'''(?xs) ^ create\sfunction\s (?P<f>.*?) \susing\ssql\sfunction .*$ ''', func_repr) if not m or not m.group('f'): raise self.error(f'could not recreate function signature from AST') func_repr = m.group('f') signode['eql-module'] = modname signode['eql-name'] = funcname signode['eql-fullname'] = fullname = f'{modname}::{funcname}' signode['eql-signature'] = func_repr signode += s_nodes.desc_annotation('function', 'function') signode += d_nodes.Text(' ') signode += s_nodes.desc_name(fullname, fullname) ret_repr = ql_gen.generate_source(astnode.returning) if astnode.returning_typemod is qltypes.TypeModifier.SetOfType: ret_repr = f'set of {ret_repr}' elif astnode.returning_typemod is qltypes.TypeModifier.OptionalType: ret_repr = f'optional {ret_repr}' signode += s_nodes.desc_returns(ret_repr, ret_repr) return fullname