def _get_ast(self, schema, context): value = self.new_value new_value_empty = \ (value is None or (isinstance(value, collections.abc.Container) and not value)) old_value_empty = \ (self.old_value is None or (isinstance(self.old_value, collections.abc.Container) and not self.old_value)) if new_value_empty and not old_value_empty: op = qlast.DropAnnotationValue( name=qlast.ObjectRef(module='', name=self.property)) return op if new_value_empty and old_value_empty: return if isinstance(value, s_expr.Expression): value = edgeql.parse(value.text) elif utils.is_nontrivial_container(value): value = qlast.Tuple( elements=[qlast.BaseConstant.from_python(el) for el in value]) else: value = qlast.BaseConstant.from_python(value) op = qlast.SetField(name=qlast.ObjectRef(module='', name=self.property), value=value) return op
def eta_expand_tuple( path: qlast.Path, stype: s_types.Tuple, *, ctx: context.ContextLevel, ) -> qlast.Expr: """η-expansion of tuples η-expansion of tuple types is straightforward and traditional: EXPAND(tuple<t, s>, p) = (EXPAND(t, p.0), EXPAND(s, p.1)) is the case for pairs. n-ary and named cases are generalized in the obvious way. The one exception is that the expansion of the empty tuple type is `p` and not `()`, to ensure that the path appears in the output. """ if not stype.get_subtypes(ctx.env.schema): return path els = [ qlast.TupleElement( name=qlast.ObjectRef(name=name), val=eta_expand(astutils.extend_path(path, name), subtype, ctx=ctx), ) for name, subtype in stype.iter_subtypes(ctx.env.schema) ] if stype.is_named(ctx.env.schema): return qlast.NamedTuple(elements=els) else: return qlast.Tuple(elements=[el.val for el in els])
def visit_Tuple(self, node): if node.named: result = qlast.NamedTuple(elements=[ qlast.TupleElement(name=el.name, val=self.visit(el.val)) for el in node.elements ]) else: result = qlast.Tuple( elements=[self.visit(e.val) for e in node.elements]) return result
def _get_ast(self, schema, context): value = self.new_value new_value_empty = \ (value is None or (isinstance(value, collections.abc.Container) and not value)) old_value_empty = \ (self.old_value is None or (isinstance(self.old_value, collections.abc.Container) and not self.old_value)) parent_ctx = context.current() parent_op = parent_ctx.op field = parent_op.get_schema_metaclass().get_field(self.property) if field is None: raise errors.SchemaDefinitionError( f'{self.property!r} is not a valid field', context=self.context) if not field.allow_ddl_set: return if self.source == 'inheritance': return if new_value_empty and old_value_empty: return if isinstance(value, s_expr.Expression): value = value.qlast elif utils.is_nontrivial_container(value): value = qlast.Tuple( elements=[qlast.BaseConstant.from_python(el) for el in value]) else: value = qlast.BaseConstant.from_python(value) op = qlast.SetField(name=qlast.ObjectRef(module='', name=self.property), value=value) return op
def eta_expand_array( path: qlast.Path, stype: s_types.Array, *, ctx: context.ContextLevel, ) -> qlast.Expr: """η-expansion of arrays η-expansion of array types is is a little peculiar to edgeql and less grounded in typed lambda calculi: EXPAND(array<t>, p) = (p, array_agg(EXPAND_ORDERED(t, array_unpack(p)))).1 We use a similar approach for compiling casts. The tuple projection trick serves to make sure that we iterate over `p` *outside* of the array_agg (or else all the arrays would get aggregated together) as well as ensuring that `p` appears in the expansion in a non-fenced position (or else sorting it from outside wouldn't work). (If it wasn't for the latter requirement, we could just use a FOR. I find it a little unsatisfying that our η-expansion needs to use this trick, and the pgsql compiler needed to be hacked to make it work.) """ unpacked = qlast.FunctionCall(func=('__std__', 'array_unpack'), args=[path]) expanded = eta_expand_ordered(unpacked, stype.get_element_type(ctx.env.schema), ctx=ctx) agg_expr = qlast.FunctionCall(func=('__std__', 'array_agg'), args=[expanded]) return astutils.extend_path(qlast.Tuple(elements=[path, agg_expr]), '1')
def reduce_LPAREN_RPAREN(self, *kids): self.val = qlast.Tuple(elements=[])
def reduce_LPAREN_Expr_COMMA_OptExprList_RPAREN(self, *kids): self.val = qlast.Tuple(elements=[kids[1].val] + kids[3].val)
def _cast_array( ir_set: irast.Set, orig_stype: s_types.Type, new_stype: s_types.Type, *, srcctx: Optional[parsing.ParserContext], ctx: context.ContextLevel) -> irast.Set: assert isinstance(orig_stype, s_types.Array) direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx) if direct_cast is None: if not new_stype.is_array(): raise errors.QueryError( f'cannot cast {orig_stype.get_displayname(ctx.env.schema)!r} ' f'to {new_stype.get_displayname(ctx.env.schema)!r}', context=srcctx) assert isinstance(new_stype, s_types.Array) el_type = new_stype.get_subtypes(ctx.env.schema)[0] else: el_type = new_stype orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0] el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx) if el_cast is not None and el_cast.get_from_cast(ctx.env.schema): # Simple cast return _cast_to_ir( ir_set, el_cast, orig_stype, new_stype, ctx=ctx) else: with ctx.new() as subctx: subctx.anchors = subctx.anchors.copy() source_path = subctx.create_anchor(ir_set, 'a') unpacked = qlast.FunctionCall( func=('__std__', 'array_unpack'), args=[source_path], ) enumerated = dispatch.compile( qlast.FunctionCall( func=('__std__', 'enumerate'), args=[unpacked], ), ctx=subctx, ) enumerated_ref = subctx.create_anchor(enumerated, 'e') elements = qlast.FunctionCall( func=('__std__', 'array_agg'), args=[ qlast.SelectQuery( result=qlast.TypeCast( expr=astutils.extend_path(enumerated_ref, '1'), type=typegen.type_to_ql_typeref( el_type, ctx=subctx, ), cardinality_mod=qlast.CardinalityModifier.Required, ), orderby=[ qlast.SortExpr( path=astutils.extend_path(enumerated_ref, '0'), direction=qlast.SortOrder.Asc, ), ], ), ], ) # Force the elements to be correlated with whatever the # anchor was. (Doing it this way ensures a NULL check, # and just registering it in the scope would not.) correlated_elements = astutils.extend_path( qlast.Tuple(elements=[source_path, elements]), '1' ) if el_type.contains_json(subctx.env.schema): subctx.inhibit_implicit_limit = True array_ir = dispatch.compile(correlated_elements, ctx=subctx) assert isinstance(array_ir, irast.Set) if direct_cast is not None: ctx.env.schema, array_stype = s_types.Array.from_subtypes( ctx.env.schema, [el_type]) return _cast_to_ir( array_ir, direct_cast, array_stype, new_stype, ctx=ctx ) else: return array_ir