Пример #1
0
    def create_constr_trigger(self, table_name, constraint, proc_name):
        cmds = []

        cname = constraint.raw_constraint_name()

        ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger')
        ins_trigger = dbops.Trigger(name=ins_trigger_name,
                                    table_name=table_name,
                                    events=('insert', ),
                                    procedure=proc_name,
                                    is_constraint=True,
                                    inherit=True)
        cr_ins_trigger = dbops.CreateTrigger(ins_trigger)
        cmds.append(cr_ins_trigger)

        disable_ins_trigger = dbops.DisableTrigger(ins_trigger, self_only=True)
        cmds.append(disable_ins_trigger)

        upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger')
        condition = constraint.get_trigger_condition()

        upd_trigger = dbops.Trigger(name=upd_trigger_name,
                                    table_name=table_name,
                                    events=('update', ),
                                    procedure=proc_name,
                                    condition=condition,
                                    is_constraint=True,
                                    inherit=True)
        cr_upd_trigger = dbops.CreateTrigger(upd_trigger)
        cmds.append(cr_upd_trigger)

        disable_upd_trigger = dbops.DisableTrigger(upd_trigger, self_only=True)
        cmds.append(disable_upd_trigger)

        return cmds
Пример #2
0
def _compile_set_in_singleton_mode(
        node: irast.Set, *, ctx: context.CompilerContextLevel) -> pgast.Base:
    if isinstance(node, irast.EmptySet):
        return pgast.Constant(value=None)
    elif node.expr is not None:
        return dispatch.compile(node.expr, ctx=ctx)
    else:
        if node.rptr:
            ptrcls = node.rptr.ptrcls
            source = node.rptr.source

            if not ptrcls.is_link_property():
                if source.rptr:
                    raise RuntimeError('unexpectedly long path in simple expr')

            colref = pgast.ColumnRef(
                name=[common.edgedb_name_to_pg_name(ptrcls.shortname)])
        elif isinstance(node.scls, s_scalars.ScalarType):
            colref = pgast.ColumnRef(
                name=[common.edgedb_name_to_pg_name(node.scls.name)])
        else:
            colref = pgast.ColumnRef(
                name=[common.edgedb_name_to_pg_name(node.scls.name)])

        return colref
Пример #3
0
def _new_mapped_pointer_rvar(
        ir_ptr: irast.Pointer, nullable: bool=False, *,
        ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar:
    ptrcls = ir_ptr.ptrcls
    ptr_rvar = dbobj.range_for_pointer(ir_ptr, env=ctx.env)
    ptr_rvar.nullable = nullable
    # Set up references according to the link direction.
    if isinstance(ptrcls, s_links.Link):
        # XXX: fix this once Properties are Sources
        src_ptr_info = pg_types.get_pointer_storage_info(
            ptrcls.getptr(ctx.env.schema, 'std::source'), resolve_type=False)
        src_col = src_ptr_info.column_name
    else:
        src_col = common.edgedb_name_to_pg_name('std::source')

    source_ref = dbobj.get_column(None, src_col)

    if isinstance(ptrcls, s_links.Link):
        # XXX: fix this once Properties are Sources
        tgt_ptr_info = pg_types.get_pointer_storage_info(
            ptrcls.getptr(ctx.env.schema, 'std::target'), resolve_type=False)
        tgt_col = tgt_ptr_info.column_name
    else:
        tgt_col = common.edgedb_name_to_pg_name('std::target')

    target_ref = dbobj.get_column(None, tgt_col)

    if ir_ptr.direction == s_pointers.PointerDirection.Inbound:
        near_ref = target_ref
        far_ref = source_ref
    else:
        near_ref = source_ref
        far_ref = target_ref

    ptr_rvar.query.path_id = ir_ptr.target.path_id.ptr_path()
    ptr_rvar.path_scope.add(ptr_rvar.query.path_id)

    src_pid = ir_ptr.source.path_id
    tgt_pid = ir_ptr.target.path_id
    ptr_rvar.path_scope.add(src_pid)

    pathctx.put_rvar_path_output(ptr_rvar, src_pid, aspect='identity',
                                 var=near_ref, env=ctx.env)
    pathctx.put_rvar_path_output(ptr_rvar, src_pid, aspect='value',
                                 var=near_ref, env=ctx.env)
    pathctx.put_rvar_path_output(ptr_rvar, tgt_pid, aspect='value',
                                 var=far_ref, env=ctx.env)

    if tgt_pid.is_objtype_path():
        ptr_rvar.path_scope.add(tgt_pid)
        pathctx.put_rvar_path_output(ptr_rvar, tgt_pid, aspect='identity',
                                     var=far_ref, env=ctx.env)

    return ptr_rvar
Пример #4
0
    def get_id(self):
        raw_name = self.constraint.raw_constraint_name()
        name = common.edgedb_name_to_pg_name('{}#{}'.format(
            raw_name, self.index))
        name = common.quote_ident(name)

        return '{} ON {} {}'.format(name, self.constraint.get_subject_type(),
                                    self.constraint.get_subject_name())
Пример #5
0
def process_linkprop_update(ir_stmt: irast.MutatingStmt, ir_expr: irast.Base,
                            wrapper: pgast.Query,
                            dml_cte: pgast.CommonTableExpr, *,
                            ctx: context.CompilerContextLevel) -> None:
    """Perform link property updates to a link relation.

    :param ir_stmt:
        IR of the statement.
    :param ir_expr:
        IR of the UPDATE body element.
    :param wrapper:
        Top-level SQL query.
    :param dml_cte:
        CTE representing the SQL UPDATE to the main relation of the Object.
    """
    toplevel = ctx.toplevel_stmt

    rptr = ir_expr.rptr
    ptrcls = rptr.ptrcls

    target_tab = dbobj.range_for_ptrcls(ptrcls,
                                        '>',
                                        include_overlays=False,
                                        env=ctx.env)

    dml_cte_rvar = pgast.RangeVar(
        relation=dml_cte,
        alias=pgast.Alias(aliasname=ctx.env.aliases.get('m')))

    cond = astutils.new_binop(
        pathctx.get_rvar_path_identity_var(dml_cte_rvar,
                                           ir_stmt.subject.path_id,
                                           env=ctx.env),
        dbobj.get_column(target_tab, 'std::source', nullable=False),
        ast.ops.EQ)

    targets = []
    for prop_el in ir_expr.shape:
        ptrname = prop_el.rptr.ptrcls.shortname
        with ctx.new() as input_rel_ctx:
            input_rel_ctx.expr_exposed = False
            input_rel = dispatch.compile(prop_el.expr, ctx=input_rel_ctx)
            targets.append(
                pgast.UpdateTarget(name=common.edgedb_name_to_pg_name(ptrname),
                                   val=input_rel))

    updstmt = pgast.UpdateStmt(relation=target_tab,
                               where_clause=cond,
                               targets=targets,
                               from_clause=[dml_cte_rvar])

    updcte = pgast.CommonTableExpr(query=updstmt,
                                   name=ctx.env.aliases.get(
                                       ptrcls.shortname.name))

    toplevel.ctes.append(updcte)
Пример #6
0
def cols_for_pointer(pointer: s_pointers.Pointer, *,
                     env: context.Environment) -> typing.List[str]:
    cols = ['ptr_item_id']

    if isinstance(pointer, s_links.Link):
        for ptr in pointer.pointers.values():
            cols.append(common.edgedb_name_to_pg_name(ptr.shortname))
    else:
        cols.extend(('std::source', 'std::target'))

    return cols
Пример #7
0
def new_static_class_rvar(
        ir_set: irast.Set, *,
        lateral: bool=True,
        ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar:
    set_rvar = new_root_rvar(ir_set, ctx=ctx)
    clsname = pgast.Constant(val=ir_set.rptr.source.scls.material_type().name)
    nameref = dbobj.get_column(
        set_rvar, common.edgedb_name_to_pg_name('schema::name'))
    condition = astutils.new_binop(nameref, clsname, op='=')
    substmt = pgast.SelectStmt()
    include_rvar(substmt, set_rvar, ir_set.path_id, ctx=ctx)
    substmt.where_clause = astutils.extend_binop(
        substmt.where_clause, condition)
    return new_rel_rvar(ir_set, substmt, ctx=ctx)
Пример #8
0
def _compile_set_in_singleton_mode(
        node: irast.Set, *, ctx: context.CompilerContextLevel) -> pgast.Base:
    if isinstance(node, irast.EmptySet):
        return pgast.NullConstant()
    elif node.expr is not None:
        return dispatch.compile(node.expr, ctx=ctx)
    else:
        if node.rptr:
            ptrcls = node.rptr.ptrcls
            source = node.rptr.source

            if not ptrcls.is_link_property(ctx.env.schema):
                if source.rptr:
                    raise RuntimeError(
                        'unexpectedly long path in simple expr')

            ptr_stor_info = pg_types.get_pointer_storage_info(
                ptrcls, schema=ctx.env.schema, resolve_type=False)

            colref = pgast.ColumnRef(name=[ptr_stor_info.column_name])
        elif isinstance(node.stype, s_scalars.ScalarType):
            colref = pgast.ColumnRef(
                name=[
                    common.edgedb_name_to_pg_name(
                        node.stype.get_name(ctx.env.schema))
                ]
            )
        else:
            colref = pgast.ColumnRef(
                name=[
                    common.edgedb_name_to_pg_name(
                        node.stype.get_name(ctx.env.schema))
                ]
            )

        return colref
Пример #9
0
    def drop_constr_trigger(self, table_name, constraint):
        cname = constraint.raw_constraint_name()

        ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger')
        ins_trigger = dbops.Trigger(name=ins_trigger_name,
                                    table_name=table_name,
                                    events=('insert', ),
                                    procedure='null',
                                    is_constraint=True,
                                    inherit=True)

        drop_ins_trigger = dbops.DropTrigger(ins_trigger)

        upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger')
        upd_trigger = dbops.Trigger(name=upd_trigger_name,
                                    table_name=table_name,
                                    events=('update', ),
                                    procedure='null',
                                    is_constraint=True,
                                    inherit=True)

        drop_upd_trigger = dbops.DropTrigger(upd_trigger)

        return [drop_ins_trigger, drop_upd_trigger]
Пример #10
0
    def rename_constr_trigger(self, table_name):
        constraint = self._constraint
        new_constr = self._new_constraint

        cname = constraint.raw_constraint_name()
        ncname = new_constr.raw_constraint_name()

        ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger')
        new_ins_trg_name = common.edgedb_name_to_pg_name(ncname +
                                                         '_instrigger')

        ins_trigger = dbops.Trigger(name=ins_trigger_name,
                                    table_name=table_name,
                                    events=('insert', ),
                                    procedure='null',
                                    is_constraint=True,
                                    inherit=True)

        rn_ins_trigger = dbops.AlterTriggerRenameTo(ins_trigger,
                                                    new_name=new_ins_trg_name)

        upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger')
        new_upd_trg_name = common.edgedb_name_to_pg_name(ncname +
                                                         '_updtrigger')

        upd_trigger = dbops.Trigger(name=upd_trigger_name,
                                    table_name=table_name,
                                    events=('update', ),
                                    procedure='null',
                                    is_constraint=True,
                                    inherit=True)

        rn_upd_trigger = dbops.AlterTriggerRenameTo(upd_trigger,
                                                    new_name=new_upd_trg_name)

        return (rn_ins_trigger, rn_upd_trigger)
Пример #11
0
    def __init__(self,
                 *,
                 scls,
                 field,
                 expr,
                 conditions=None,
                 neg_conditions=None,
                 priority=0):
        super().__init__(conditions=conditions,
                         neg_conditions=neg_conditions,
                         priority=priority)

        self.name = scls.name
        self.table = metaschema.get_metaclass_table(scls.__class__)
        self.field = common.edgedb_name_to_pg_name(field)
        self.expr = expr
Пример #12
0
    def drop_constraint(self, constraint):
        if not constraint.is_natively_inherited():
            self.add_commands(self.drop_constr_trigger(self.name, constraint))

            # Drop trigger function
            #
            proc_name = constraint.raw_constraint_name() + '_trigproc'
            proc_name = self.name[0], common.edgedb_name_to_pg_name(proc_name)

            self.add_commands(self.drop_constr_trigger_function(proc_name))

        # Drop the constraint normally from our table
        #
        my_alter = dbops.AlterTable(self.name)

        drop_constr = AlterTableDropMultiConstraint(constraint=constraint)
        my_alter.add_command(drop_constr)

        self.add_command(my_alter)
Пример #13
0
    async def creation_code(self, context):
        link_map = await context.get_class_map()

        ids = tuple(sorted(list(link_map[n] for n in self.link_names)))
        id_str = '_'.join(str(i) for i in ids)

        name = '%s_%s_%s_cardinality_idx' % (self.name_prefix, id_str,
                                             self.cardinality)
        name = common.edgedb_name_to_pg_name(name)
        predicate = 'ptr_item_id IN (%s)' % ', '.join(str(id) for id in ids)

        code = '''
            CREATE {unique} INDEX {name} ON {table}s ({cols}) {predicate}
        '''.format(unique='UNIQUE',
                   name=common.qname(name),
                   table=common.qname(*self.table_name),
                   cols=', '.join(common.quote_ident(c) for c in self.columns),
                   predicate=('WHERE {}'.format(predicate)))

        return code
Пример #14
0
def compile_FunctionCall(expr: irast.Base, *,
                         ctx: context.CompilerContextLevel) -> pgast.Base:
    funcobj = expr.func

    if funcobj.aggregate:
        raise RuntimeError(
            'aggregate functions are not supported in simple expressions')

    if funcobj.set_returning:
        raise RuntimeError(
            'set returning functions are not supported in simple expressions')

    args = [dispatch.compile(a, ctx=ctx) for a in expr.args]

    if funcobj.from_function:
        name = (funcobj.from_function, )
    else:
        name = (common.edgedb_module_name_to_schema_name(
            funcobj.shortname.module),
                common.edgedb_name_to_pg_name(funcobj.shortname.name))

    result = pgast.FuncCall(name=name, args=args)

    return result
Пример #15
0
 def constraint_name(self, quote=True):
     name = self.raw_constraint_name()
     name = common.edgedb_name_to_pg_name(name)
     return common.quote_ident(name) if quote else name
Пример #16
0
 def get_trigger_procname(self):
     schema = common.edgedb_module_name_to_schema_name(
         self.schema_constraint_name().module)
     proc_name = common.edgedb_name_to_pg_name(self.raw_constraint_name() +
                                               '_trigproc')
     return schema, proc_name
Пример #17
0
def init_dml_stmt(
        ir_stmt: irast.MutatingStmt, dml_stmt: pgast.DML, *,
        ctx: context.CompilerContextLevel,
        parent_ctx: context.CompilerContextLevel) \
        -> typing.Tuple[pgast.Query, pgast.CommonTableExpr,
                        pgast.CommonTableExpr]:
    """Prepare the common structure of the query representing a DML stmt.

    :param ir_stmt:
        IR of the statement.
    :param dml_stmt:
        SQL DML node instance.

    :return:
        A (*wrapper*, *dml_cte*, *range_cte*) tuple, where *wrapper* the
        the wrapping SQL statement, *dml_cte* is the CTE representing the
        SQL DML operation in the main relation of the Object, and
        *range_cte* is the CTE for the subset affected by the statement.
        *range_cte* is None for INSERT statmenets.
    """
    wrapper = ctx.rel

    clauses.init_stmt(ir_stmt, ctx, parent_ctx)

    target_ir_set = ir_stmt.subject

    dml_stmt.relation = dbobj.range_for_set(ir_stmt.subject,
                                            include_overlays=False,
                                            env=ctx.env)
    pathctx.put_path_value_rvar(dml_stmt,
                                target_ir_set.path_id,
                                dml_stmt.relation,
                                env=ctx.env)
    pathctx.put_path_source_rvar(dml_stmt,
                                 target_ir_set.path_id,
                                 dml_stmt.relation,
                                 env=ctx.env)
    dml_stmt.path_scope.add(target_ir_set.path_id)

    dml_cte = pgast.CommonTableExpr(query=dml_stmt,
                                    name=ctx.env.aliases.get(hint='m'))

    if isinstance(ir_stmt, (irast.UpdateStmt, irast.DeleteStmt)):
        # UPDATE and DELETE operate over a range, so generate
        # the corresponding CTE and connect it to the DML query.
        range_cte = get_dml_range(ir_stmt, dml_stmt, ctx=ctx)

        range_rvar = pgast.RangeVar(
            relation=range_cte,
            alias=pgast.Alias(aliasname=ctx.env.aliases.get(hint='range')))

        relctx.pull_path_namespace(target=dml_stmt, source=range_rvar, ctx=ctx)

        # Auxillary relations are always joined via the WHERE
        # clause due to the structure of the UPDATE/DELETE SQL statments.
        id_col = common.edgedb_name_to_pg_name('std::id')
        dml_stmt.where_clause = astutils.new_binop(
            lexpr=pgast.ColumnRef(
                name=[dml_stmt.relation.alias.aliasname, id_col]),
            op=ast.ops.EQ,
            rexpr=pathctx.get_rvar_path_identity_var(range_rvar,
                                                     target_ir_set.path_id,
                                                     env=ctx.env))

        # UPDATE has "FROM", while DELETE has "USING".
        if hasattr(dml_stmt, 'from_clause'):
            dml_stmt.from_clause.append(range_rvar)
        else:
            dml_stmt.using_clause.append(range_rvar)

    else:
        range_cte = None

    # Due to the fact that DML statements are structured
    # as a flat list of CTEs instead of nested range vars,
    # the top level path scope must be empty.  The necessary
    # range vars will be injected explicitly in all rels that
    # need them.
    ctx.path_scope.clear()

    pathctx.put_path_value_rvar(dml_stmt,
                                ir_stmt.subject.path_id,
                                dml_stmt.relation,
                                env=ctx.env)

    pathctx.put_path_source_rvar(dml_stmt,
                                 ir_stmt.subject.path_id,
                                 dml_stmt.relation,
                                 env=ctx.env)

    dml_rvar = pgast.RangeVar(
        relation=dml_cte,
        alias=pgast.Alias(aliasname=parent_ctx.env.aliases.get('d')))

    relctx.include_rvar(wrapper, dml_rvar, ir_stmt.subject.path_id, ctx=ctx)

    pathctx.put_path_bond(wrapper, ir_stmt.subject.path_id)

    return wrapper, dml_cte, dml_rvar, range_cte
Пример #18
0
 def numbered_constraint_name(self, i, quote=True):
     raw_name = self.raw_constraint_name()
     name = common.edgedb_name_to_pg_name('{}#{}'.format(raw_name, i))
     return common.quote_ident(name) if quote else name
Пример #19
0
def process_link_values(
        ir_stmt, ir_expr, target_tab, tab_cols, col_data,
        dml_rvar, sources, props_only, target_is_scalar, iterator_cte, *,
        ctx=context.CompilerContext) -> \
        typing.Tuple[pgast.CommonTableExpr, typing.List[str]]:
    """Unpack data from an update expression into a series of selects.

    :param ir_expr:
        IR of the INSERT/UPDATE body element.
    :param target_tab:
        The link table being updated.
    :param tab_cols:
        A sequence of columns in the table being updated.
    :param col_data:
        Expressions used to populate well-known columns of the link
        table such as std::source and std::__type__.
    :param sources:
        A list of relations which must be joined into the data query
        to resolve expressions in *col_data*.
    :param props_only:
        Whether this link update only touches link properties.
    :param target_is_scalar:
        Whether the link target is an ScalarType.
    :param iterator_cte:
        CTE representing the iterator range in the FOR clause of the
        EdgeQL DML statement.
    """
    with ctx.newscope() as newscope, newscope.newrel() as subrelctx:
        row_query = subrelctx.rel

        relctx.include_rvar(row_query,
                            dml_rvar,
                            path_id=ir_stmt.subject.path_id,
                            ctx=subrelctx)
        subrelctx.path_scope[ir_stmt.subject.path_id] = row_query

        if iterator_cte is not None:
            iterator_rvar = dbobj.rvar_for_rel(iterator_cte,
                                               lateral=True,
                                               env=subrelctx.env)
            relctx.include_rvar(row_query,
                                iterator_rvar,
                                path_id=iterator_cte.query.path_id,
                                ctx=subrelctx)

        with subrelctx.newscope() as sctx, sctx.subrel() as input_rel_ctx:
            input_rel = input_rel_ctx.rel
            if iterator_cte is not None:
                input_rel_ctx.path_scope[iterator_cte.query.path_id] = \
                    row_query
            input_rel_ctx.expr_exposed = False
            input_rel_ctx.shape_format = context.ShapeFormat.FLAT
            input_rel_ctx.volatility_ref = pathctx.get_path_identity_var(
                row_query, ir_stmt.subject.path_id, env=input_rel_ctx.env)
            dispatch.compile(ir_expr, ctx=input_rel_ctx)

    input_stmt = input_rel

    input_rvar = pgast.RangeSubselect(
        subquery=input_rel,
        lateral=True,
        alias=pgast.Alias(aliasname=ctx.env.aliases.get('val')))

    source_data = {}

    if input_stmt.op is not None:
        # UNION
        input_stmt = input_stmt.rarg

    path_id = ir_expr.path_id

    output = pathctx.get_path_value_output(input_stmt, path_id, env=ctx.env)

    if isinstance(output, pgast.TupleVar):
        for element in output.elements:
            name = element.path_id.rptr_name()
            if name is None:
                name = element.path_id[-1].name
            colname = common.edgedb_name_to_pg_name(name)
            source_data.setdefault(colname,
                                   dbobj.get_column(input_rvar, element.name))
    else:
        if target_is_scalar:
            target_ref = pathctx.get_rvar_path_value_var(input_rvar,
                                                         path_id,
                                                         env=ctx.env)
        else:
            target_ref = pathctx.get_rvar_path_identity_var(input_rvar,
                                                            path_id,
                                                            env=ctx.env)

        source_data['std::target'] = target_ref

    if not target_is_scalar and 'std::target' not in source_data:
        target_ref = pathctx.get_rvar_path_identity_var(input_rvar,
                                                        path_id,
                                                        env=ctx.env)
        source_data['std::target'] = target_ref

    specified_cols = []
    for col in tab_cols:
        expr = col_data.get(col)
        if expr is None:
            expr = source_data.get(col)

        if expr is not None:
            row_query.target_list.append(pgast.ResTarget(val=expr, name=col))
            specified_cols.append(col)

    row_query.from_clause += list(sources) + [input_rvar]

    link_rows = pgast.CommonTableExpr(query=row_query,
                                      name=ctx.env.aliases.get(hint='r'))

    return link_rows, specified_cols
Пример #20
0
 def get(self, hint=None):
     alias = super().get(hint)
     return common.edgedb_name_to_pg_name(alias)