Exemplo n.º 1
0
def _update_lprops(
    cmd: s_ref.ReferencedObjectCommand,  # type: ignore
    *,
    classlayout: Dict[Type[so.Object], sr_struct.SchemaTypeLayout],
    schema: s_schema.Schema,
    blocks: List[Tuple[str, Dict[str, Any]]],
    context: sd.CommandContext,
    is_internal_reflection: bool,
    stdmode: bool,
) -> None:
    mcls = cmd.get_schema_metaclass()
    refctx = cmd.get_referrer_context_or_die(context)
    refop = refctx.op
    refcls = refop.get_schema_metaclass()
    refdict = refcls.get_refdict_for_class(mcls)
    layout = classlayout[refcls][refdict.attr]
    lprops = layout.properties

    if not lprops:
        return

    if mcls.get_reflection_method() is so.ReflectionMethod.AS_LINK:
        target_link = mcls.get_reflection_link()
        assert target_link is not None
        target_field = mcls.get_field(target_link)
        target_obj = cmd.get_ddl_identity(target_link)
        if target_obj is None:
            raise AssertionError(
                f'cannot find link target in ddl_identity of a command for '
                f'schema class reflected as link: {cmd!r}'
            )
        target_ref = target_obj.get_name(schema)
        target_clsname = target_field.type.__name__
    else:
        referrer_cls = refop.get_schema_metaclass()
        target_field = referrer_cls.get_field(refdict.attr)
        if issubclass(target_field.type, so.ObjectCollection):
            target_type = target_field.type.type
        else:
            target_type = target_field.type
        target_clsname = target_type.__name__
        target_link = refdict.attr
        target_ref = cmd.classname

    shape, append_variables = _build_object_mutation_shape(
        cmd,
        classlayout=classlayout,
        lprop_fields=lprops,
        lprops_only=True,
        is_internal_reflection=is_internal_reflection,
        stdmode=stdmode,
        schema=schema,
        context=context,
    )

    if shape:
        parent_variables = {}
        parent_variables[f'__{target_link}'] = json.dumps(target_ref)

        # XXX: we have to do a -= followed by a += because
        # support for filtered nested link property updates
        # is currently broken.

        parent_update_query = f'''
            UPDATE schema::{refcls.__name__}
            FILTER .name__internal = <str>$__parent_classname
            SET {{
                {refdict.attr} -= (
                    SELECT DETACHED (schema::{target_clsname})
                    FILTER .name__internal = <str>$__{target_link}
                )
            }}
        '''

        parent_variables['__parent_classname'] = (
            json.dumps(refop.classname)
        )

        blocks.append((parent_update_query, parent_variables))

        parent_update_query = f'''
            UPDATE schema::{refcls.__name__}
            FILTER .name__internal = <str>$__parent_classname
            SET {{
                {refdict.attr} += (
                    SELECT schema::{target_clsname} {{
                        {shape}
                    }} FILTER .name__internal = <str>$__{target_link}
                )
            }}
        '''

        parent_variables.update(append_variables)
        blocks.append((parent_update_query, parent_variables))
Exemplo n.º 2
0
def _update_lprops(
    cmd: s_ref.ReferencedObjectCommand,  # type: ignore
    *,
    classlayout: Dict[Type[so.Object], sr_struct.SchemaTypeLayout],
    schema: s_schema.Schema,
    blocks: List[Tuple[str, Dict[str, Any]]],
    context: sd.CommandContext,
    internal_schema_mode: bool,
    stdmode: bool,
) -> None:
    mcls = cmd.get_schema_metaclass()
    refctx = cmd.get_referrer_context_or_die(context)
    refop = refctx.op
    refcls = refop.get_schema_metaclass()
    refdict = refcls.get_refdict_for_class(mcls)
    layout = classlayout[refcls][refdict.attr]
    lprops = layout.properties

    if not lprops:
        return

    reflect_as_link = (mcls.get_reflection_method() is
                       so.ReflectionMethod.AS_LINK)

    if reflect_as_link:
        target_link = mcls.get_reflection_link()
        assert target_link is not None
        target_field = mcls.get_field(target_link)
        target_obj = cmd.get_ddl_identity(target_link)
        if target_obj is None:
            raise AssertionError(
                f'cannot find link target in ddl_identity of a command for '
                f'schema class reflected as link: {cmd!r}')
        target_ref = target_obj.get_name(schema)
        target_clsname = target_field.type.__name__
    else:
        referrer_cls = refop.get_schema_metaclass()
        target_field = referrer_cls.get_field(refdict.attr)
        if issubclass(target_field.type, so.ObjectCollection):
            target_type = target_field.type.type
        else:
            target_type = target_field.type
        target_clsname = target_type.__name__
        target_link = refdict.attr
        target_ref = cmd.classname

    shape, append_variables = _build_object_mutation_shape(
        cmd,
        classlayout=classlayout,
        lprop_fields=lprops,
        lprops_only=True,
        internal_schema_mode=internal_schema_mode,
        stdmode=stdmode,
        schema=schema,
        context=context,
    )

    if shape:
        parent_variables = {}
        parent_variables[f'__{target_link}'] = json.dumps(str(target_ref))
        ref_name = context.get_referrer_name(refctx)
        parent_variables['__parent_classname'] = json.dumps(str(ref_name))

        # XXX: we have to do a -= followed by a += because
        # support for filtered nested link property updates
        # is currently broken.

        assignments = []

        assignments.append(
            textwrap.dedent(f'''\
            {refdict.attr} -= (
                SELECT DETACHED (schema::{target_clsname})
                FILTER .name__internal = <str>$__{target_link}
            )'''))

        if reflect_as_link:
            parent_variables[f'__{target_link}_shadow'] = (json.dumps(
                str(cmd.classname)))

            assignments.append(
                textwrap.dedent(f'''\
                {refdict.attr}__internal -= (
                    SELECT DETACHED (schema::{mcls.__name__})
                    FILTER .name__internal = <str>$__{target_link}_shadow
                )'''))

        update_shape = textwrap.indent('\n' + ',\n'.join(assignments),
                                       '    ' * 4)

        parent_update_query = textwrap.dedent(f'''\
            UPDATE schema::{refcls.__name__}
            FILTER .name__internal = <str>$__parent_classname
            SET {{{update_shape}
            }}
        ''')

        blocks.append((parent_update_query, parent_variables))

        assignments = []

        shape = textwrap.indent(f'\n{shape}', '    ' * 5)

        assignments.append(
            textwrap.dedent(f'''\
            {refdict.attr} += (
                SELECT schema::{target_clsname} {{{shape}
                }} FILTER .name__internal = <str>$__{target_link}
            )'''))

        if reflect_as_link:
            shadow_clslayout = classlayout[refcls]
            shadow_link_layout = shadow_clslayout[f'{refdict.attr}__internal']
            shadow_shape, shadow_variables = _build_object_mutation_shape(
                cmd,
                classlayout=classlayout,
                internal_schema_mode=internal_schema_mode,
                lprop_fields=shadow_link_layout.properties,
                lprops_only=True,
                stdmode=stdmode,
                var_prefix='shadow_',
                schema=schema,
                context=context,
            )

            shadow_shape = textwrap.indent(f'\n{shadow_shape}', '    ' * 6)

            assignments.append(
                textwrap.dedent(f'''\
                {refdict.attr}__internal += (
                    SELECT schema::{mcls.__name__} {{{shadow_shape}
                    }} FILTER .name__internal = <str>$__{target_link}_shadow
                )'''))

            parent_variables.update(shadow_variables)

        update_shape = textwrap.indent('\n' + ',\n'.join(assignments),
                                       '    ' * 4)

        parent_update_query = textwrap.dedent(f'''
            UPDATE schema::{refcls.__name__}
            FILTER .name__internal = <str>$__parent_classname
            SET {{{update_shape}
            }}
        ''')

        parent_variables.update(append_variables)
        blocks.append((parent_update_query, parent_variables))