def get_path_var(rel: pgast.Query, path_id: irast.PathId, *, aspect: str, env: context.Environment) -> pgast.BaseExpr: """Return a value expression for a given *path_id* in a given *rel*.""" if isinstance(rel, pgast.CommonTableExpr): rel = rel.query # Check if we already have a var, before remapping the path_id. # This is useful for serialized aspect disambiguation in tuples, # since process_set_as_tuple() records serialized vars with # original path_id. if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] if rel.view_path_id_map: path_id = map_path_id(path_id, rel.view_path_id_map) if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] ptrref = path_id.rptr() is_type_intersection = path_id.is_type_intersection_path() src_path_id: Optional[irast.PathId] = None if ptrref is not None and not is_type_intersection: ptr_info = pg_types.get_ptrref_storage_info(ptrref, resolve_type=False, link_bias=False) ptr_dir = path_id.rptr_dir() is_inbound = ptr_dir == s_pointers.PointerDirection.Inbound if is_inbound: src_path_id = path_id else: src_path_id = path_id.src_path() assert src_path_id is not None src_rptr = src_path_id.rptr() if (irtyputils.is_id_ptrref(ptrref) and (src_rptr is None or not irtyputils.is_inbound_ptrref(src_rptr))): # When there is a reference to the id property of # an object which is linked to by a link stored # inline, we want to route the reference to the # inline attribute. For example, # Foo.__type__.id gets resolved to the Foo.__type__ # column. This can only be done if Foo is visible # in scope, and Foo.__type__ is not a computable. pid = src_path_id while pid.is_type_intersection_path(): # Skip type intersection step(s). src_pid = pid.src_path() if src_pid is not None: src_rptr = src_pid.rptr() pid = src_pid else: break if (src_rptr is not None and not irtyputils.is_computable_ptrref(src_rptr) and env.ptrref_source_visibility.get(src_rptr)): src_ptr_info = pg_types.get_ptrref_storage_info( src_rptr, resolve_type=False, link_bias=False) if src_ptr_info.table_type == 'ObjectType': src_path_id = src_path_id.src_path() ptr_info = src_ptr_info else: ptr_info = None ptr_dir = None var: Optional[pgast.BaseExpr] if astutils.is_set_op_query(rel): # We disable the find_path_output optimizaiton when doing # UNIONs to avoid situations where they have different numbers # of columns. cb = functools.partial(get_path_output_or_null, env=env, disable_output_fusion=True, path_id=path_id, aspect=aspect) outputs = astutils.for_each_query_in_set(rel, cb) first: Optional[pgast.OutputVar] = None optional = False all_null = True nullable = False for colref, is_null in outputs: if colref.nullable: nullable = True if first is None: first = colref if is_null: optional = True else: all_null = False if all_null: raise LookupError(f'cannot find refs for ' f'path {path_id} {aspect} in {rel}') if first is None: raise AssertionError(f'union did not produce any outputs') # Path vars produced by UNION expressions can be "optional", # i.e the record is accepted as-is when such var is NULL. # This is necessary to correctly join heterogeneous UNIONs. var = astutils.strip_output_var(first, optional=optional, nullable=optional or nullable) put_path_var(rel, path_id, var, aspect=aspect, env=env) return var if ptrref is None: if len(path_id) == 1: # This is an scalar set derived from an expression. src_path_id = path_id elif ptrref.source_ptr is not None: if ptr_info.table_type != 'link' and not is_inbound: # This is a link prop that is stored in source rel, # step back to link source rvar. _prefix_pid = path_id.src_path() assert _prefix_pid is not None src_path_id = _prefix_pid.src_path() elif (ptr_info is not None and ptr_info.table_type != 'ObjectType' and not is_inbound): # Ref is in the mapping rvar. src_path_id = path_id.ptr_path() elif is_type_intersection: src_path_id = path_id rel_rvar = maybe_get_path_rvar(rel, path_id, aspect=aspect, env=env) if rel_rvar is None: alt_aspect = get_less_specific_aspect(path_id, aspect) if alt_aspect is not None: rel_rvar = maybe_get_path_rvar(rel, path_id, aspect=alt_aspect, env=env) else: alt_aspect = None assert src_path_id is not None if rel_rvar is None: if src_path_id.is_objtype_path(): src_aspect = 'source' else: src_aspect = aspect if src_path_id.is_tuple_path(): rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect=src_aspect, env=env) if rel_rvar is None: _src_path_id_prefix = src_path_id.src_path() if _src_path_id_prefix is not None: rel_rvar = maybe_get_path_rvar(rel, _src_path_id_prefix, aspect=src_aspect, env=env) else: rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect=src_aspect, env=env) if (rel_rvar is None and src_aspect != 'source' and path_id != src_path_id): rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect='source', env=env) if rel_rvar is None and alt_aspect is not None: # There is no source range var for the requested aspect, # check if there is a cached var with less specificity. var = rel.path_namespace.get((path_id, alt_aspect)) if var is not None: put_path_var(rel, path_id, var, aspect=aspect, env=env) return var if rel_rvar is None: raise LookupError(f'there is no range var for ' f'{src_path_id} {src_aspect} in {rel}') if isinstance(rel_rvar, pgast.IntersectionRangeVar): if ((path_id.is_objtype_path() and src_path_id == path_id) or (ptrref is not None and irtyputils.is_id_ptrref(ptrref))): rel_rvar = rel_rvar.component_rvars[-1] else: # Intersection rvars are basically JOINs of the relevant # parts of the type intersection, and so we need to make # sure we pick the correct component relation of that JOIN. rel_rvar = _find_rvar_in_intersection_by_typeref( path_id, rel_rvar.component_rvars, ) source_rel = rel_rvar.query if isinstance(ptrref, irast.PointerRef) and rel_rvar.typeref is not None: actual_ptrref = irtyputils.maybe_find_actual_ptrref( rel_rvar.typeref, ptrref) if actual_ptrref is not None: ptr_info = pg_types.get_ptrref_storage_info(actual_ptrref, resolve_type=False, link_bias=False) outvar = get_path_output(source_rel, path_id, ptr_info=ptr_info, aspect=aspect, env=env) var = astutils.get_rvar_var(rel_rvar, outvar) put_path_var(rel, path_id, var, aspect=aspect, env=env) if isinstance(var, pgast.TupleVar): for element in var.elements: put_path_var_if_not_exists(rel, element.path_id, element.val, aspect=aspect, env=env) return var
def get_path_var( rel: pgast.Query, path_id: irast.PathId, *, aspect: str, env: context.Environment) -> pgast.BaseExpr: """Return a value expression for a given *path_id* in a given *rel*.""" if isinstance(rel, pgast.CommonTableExpr): rel = rel.query # Check if we already have a var, before remapping the path_id. # This is useful for serialized aspect disambiguation in tuples, # since process_set_as_tuple() records serialized vars with # original path_id. if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] if rel.view_path_id_map: path_id = map_path_id(path_id, rel.view_path_id_map) if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] if astutils.is_set_op_query(rel): return _get_path_var_in_setop(rel, path_id, aspect=aspect, env=env) ptrref = path_id.rptr() is_type_intersection = path_id.is_type_intersection_path() src_path_id: Optional[irast.PathId] = None if ptrref is not None and not is_type_intersection: ptr_info = pg_types.get_ptrref_storage_info( ptrref, resolve_type=False, link_bias=False, allow_missing=True) ptr_dir = path_id.rptr_dir() is_inbound = ptr_dir == s_pointers.PointerDirection.Inbound if is_inbound: src_path_id = path_id else: src_path_id = path_id.src_path() assert src_path_id is not None src_rptr = src_path_id.rptr() if (irtyputils.is_id_ptrref(ptrref) and (src_rptr is None or not irtyputils.is_inbound_ptrref(src_rptr))): # When there is a reference to the id property of # an object which is linked to by a link stored # inline, we want to route the reference to the # inline attribute. For example, # Foo.__type__.id gets resolved to the Foo.__type__ # column. This can only be done if Foo is visible # in scope, and Foo.__type__ is not a computable. pid = src_path_id while pid.is_type_intersection_path(): # Skip type intersection step(s). src_pid = pid.src_path() if src_pid is not None: src_rptr = src_pid.rptr() pid = src_pid else: break if (src_rptr is not None and not irtyputils.is_computable_ptrref(src_rptr) and env.ptrref_source_visibility.get(src_rptr)): src_ptr_info = pg_types.get_ptrref_storage_info( src_rptr, resolve_type=False, link_bias=False, allow_missing=True) if (src_ptr_info and src_ptr_info.table_type == 'ObjectType'): src_path_id = src_path_id.src_path() ptr_info = src_ptr_info else: ptr_info = None ptr_dir = None var: Optional[pgast.BaseExpr] if ptrref is None: if len(path_id) == 1: # This is an scalar set derived from an expression. src_path_id = path_id elif ptrref.source_ptr is not None: if ptr_info and ptr_info.table_type != 'link' and not is_inbound: # This is a link prop that is stored in source rel, # step back to link source rvar. _prefix_pid = path_id.src_path() assert _prefix_pid is not None src_path_id = _prefix_pid.src_path() elif is_type_intersection: src_path_id = path_id assert src_path_id is not None # Find which rvar will have path_id as an output src_aspect, rel_rvar, found_path_var = _find_rel_rvar( rel, path_id, src_path_id, aspect=aspect, env=env) if found_path_var: return found_path_var if rel_rvar is None: raise LookupError( f'there is no range var for ' f'{src_path_id} {src_aspect} in {rel}') if isinstance(rel_rvar, pgast.IntersectionRangeVar): if ( (path_id.is_objtype_path() and src_path_id == path_id) or (ptrref is not None and irtyputils.is_id_ptrref(ptrref)) ): rel_rvar = rel_rvar.component_rvars[-1] else: # Intersection rvars are basically JOINs of the relevant # parts of the type intersection, and so we need to make # sure we pick the correct component relation of that JOIN. rel_rvar = _find_rvar_in_intersection_by_typeref( path_id, rel_rvar.component_rvars, ) source_rel = rel_rvar.query if isinstance(ptrref, irast.PointerRef) and rel_rvar.typeref is not None: actual_ptrref = irtyputils.maybe_find_actual_ptrref( rel_rvar.typeref, ptrref) if actual_ptrref is not None: ptr_info = pg_types.get_ptrref_storage_info( actual_ptrref, resolve_type=False, link_bias=False) outvar = get_path_output( source_rel, path_id, ptr_info=ptr_info, aspect=aspect, env=env) var = astutils.get_rvar_var(rel_rvar, outvar) put_path_var(rel, path_id, var, aspect=aspect, env=env) if isinstance(var, pgast.TupleVar): for element in var.elements: put_path_var_if_not_exists(rel, element.path_id, element.val, aspect=aspect, env=env) return var