[IMP] core: make an API to get rid of Query._raw_joins
Part-of: odoo/odoo#134677
This commit is contained in:
+25
-16
@@ -2808,7 +2808,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
definition = self.get_property_definition(f"{fname}.{property_name}")
|
||||
property_sql = f""""{alias}"."{fname}" -> '{property_name}'"""
|
||||
property_type = definition.get('type')
|
||||
property_alias = tools._generate_table_alias(alias, f'{fname}_{property_name}')
|
||||
property_alias = query.make_alias(alias, f'{fname}_{property_name}')
|
||||
|
||||
# JOIN on the JSON array
|
||||
if property_type in ('tags', 'many2many'):
|
||||
@@ -2819,37 +2819,46 @@ class BaseModel(metaclass=MetaModel):
|
||||
ELSE '[]'::jsonb
|
||||
END
|
||||
'''
|
||||
join_property = f'LEFT JOIN jsonb_array_elements({property_sql}) {property_alias}'
|
||||
if property_type == 'tags':
|
||||
# ignore invalid tags
|
||||
tags = [tag[0] for tag in definition.get('tags') or []]
|
||||
# `->>0 : convert "JSON string" into string
|
||||
expr = f'{join_property} ON {property_alias}->>0 = ANY(%s::text[])'
|
||||
query._raw_joins[expr] = [tags]
|
||||
|
||||
elif property_type == 'many2many':
|
||||
condition = SQL(
|
||||
"%s->>0 = ANY(%s::text[])",
|
||||
SQL.identifier(property_alias), tags,
|
||||
)
|
||||
else:
|
||||
comodel = self.env.get(definition.get('comodel'))
|
||||
if comodel is None or comodel._transient or comodel._abstract:
|
||||
# all value are false, because the model does not exist anymore
|
||||
# (or is a transient model e.g.)
|
||||
query._raw_joins[f'{join_property} ON FALSE'] = []
|
||||
condition = SQL("FALSE")
|
||||
else:
|
||||
# check the existences of the many2many
|
||||
expr = f'{join_property} ON {property_alias}::int IN (SELECT id FROM {comodel._table})'
|
||||
query._raw_joins[expr] = []
|
||||
condition = SQL(
|
||||
"%s::int IN (SELECT id FROM %s)",
|
||||
SQL.identifier(property_alias), SQL.identifier(comodel._table),
|
||||
)
|
||||
|
||||
query.add_join(
|
||||
"LEFT JOIN",
|
||||
property_alias,
|
||||
SQL(f"jsonb_array_elements({property_sql})"),
|
||||
condition,
|
||||
)
|
||||
|
||||
return property_alias
|
||||
|
||||
elif property_type == 'selection':
|
||||
options = definition.get('selection') or []
|
||||
options = [option[0] for option in options]
|
||||
options = [option[0] for option in definition.get('selection') or ()]
|
||||
|
||||
# check the existence of the option
|
||||
expr = f'''
|
||||
LEFT JOIN (SELECT unnest(%s::text[]) {property_alias}) {property_alias}
|
||||
ON {property_sql}->>0 = {property_alias}
|
||||
'''
|
||||
query._raw_joins[expr] = [options]
|
||||
query.add_join(
|
||||
"LEFT JOIN",
|
||||
property_alias,
|
||||
SQL("(SELECT unnest(%s::text[]) %s)", options, SQL.identifier(property_alias)),
|
||||
SQL(f"{property_sql}->>0 = %s", SQL.identifier(property_alias)),
|
||||
)
|
||||
|
||||
return property_alias
|
||||
|
||||
|
||||
+25
-20
@@ -70,7 +70,6 @@ class Query(object):
|
||||
|
||||
# joins {alias: (kind(SQL), table(SQL|None), condition(SQL))}
|
||||
self._joins = {}
|
||||
self._raw_joins = {} # {query: params}
|
||||
|
||||
# holds the list of WHERE conditions (to be joined with 'AND')
|
||||
self._where_clauses = []
|
||||
@@ -83,12 +82,29 @@ class Query(object):
|
||||
# memoized result
|
||||
self._ids = None
|
||||
|
||||
def make_alias(self, alias: str, link: str) -> str:
|
||||
""" Return an alias based on ``alias`` and ``link``. """
|
||||
return _generate_table_alias(alias, link)
|
||||
|
||||
def add_table(self, alias: str, table: Union[str, SQL, None] = None):
|
||||
""" Add a table with a given alias to the from clause. """
|
||||
assert alias not in self._tables and alias not in self._joins, f"Alias {alias!r} already in {self}"
|
||||
self._tables[alias] = _sql_table(table)
|
||||
self._ids = None
|
||||
|
||||
def add_join(self, kind: str, alias: str, table: Union[str, SQL, None], condition: SQL):
|
||||
""" Add a join clause with the given alias, table and condition. """
|
||||
sql_kind = _SQL_JOINS.get(kind.upper())
|
||||
assert sql_kind is not None, f"Invalid JOIN type {kind!r}"
|
||||
assert alias not in self._tables, f"Alias {alias!r} already used"
|
||||
table = _sql_table(table)
|
||||
|
||||
if alias in self._joins:
|
||||
assert self._joins[alias] == (sql_kind, table, condition)
|
||||
else:
|
||||
self._joins[alias] = (sql_kind, table, condition)
|
||||
self._ids = None
|
||||
|
||||
def add_where(self, where_clause: Union[str, SQL], where_params=()):
|
||||
""" Add a condition to the where clause. """
|
||||
self._where_clauses.append(SQL(where_clause, *where_params))
|
||||
@@ -150,23 +166,14 @@ class Query(object):
|
||||
return self._join('LEFT JOIN', lhs_alias, lhs_column, rhs_table, rhs_column, link, extra, extra_params)
|
||||
|
||||
def _join(self, kind, lhs_alias, lhs_column, rhs_table, rhs_column, link, extra=None, extra_params=()):
|
||||
sql_kind = _SQL_JOINS.get(kind.upper())
|
||||
assert sql_kind is not None, f"Invalid JOIN type {kind!r}"
|
||||
|
||||
assert lhs_alias in self._tables or lhs_alias in self._joins, "Alias %r not in %s" % (lhs_alias, str(self))
|
||||
|
||||
rhs_alias = _generate_table_alias(lhs_alias, link)
|
||||
assert rhs_alias not in self._tables, "Alias %r already in %s" % (rhs_alias, str(self))
|
||||
|
||||
if rhs_alias not in self._joins:
|
||||
condition = SQL("%s = %s", SQL.identifier(lhs_alias, lhs_column), SQL.identifier(rhs_alias, rhs_column))
|
||||
if extra:
|
||||
extra = SQL(extra, *extra_params)
|
||||
extra = SQL(extra.code.format(lhs=lhs_alias, rhs=rhs_alias), *extra.params)
|
||||
condition = SQL("%s AND %s", condition, extra)
|
||||
self._joins[rhs_alias] = (sql_kind, _sql_table(rhs_table), condition)
|
||||
self._ids = None
|
||||
|
||||
rhs_alias = self.make_alias(lhs_alias, link)
|
||||
condition = SQL("%s = %s", SQL.identifier(lhs_alias, lhs_column), SQL.identifier(rhs_alias, rhs_column))
|
||||
if extra:
|
||||
extra = SQL(extra, *extra_params)
|
||||
extra = SQL(extra.code.format(lhs=lhs_alias, rhs=rhs_alias), *extra.params)
|
||||
condition = SQL("%s AND %s", condition, extra)
|
||||
self.add_join(kind, rhs_alias, rhs_table, condition)
|
||||
return rhs_alias
|
||||
|
||||
@property
|
||||
@@ -181,13 +188,11 @@ class Query(object):
|
||||
_sql_from_table(alias, table)
|
||||
for alias, table in self._tables.items()
|
||||
)
|
||||
if not (self._joins or self._raw_joins):
|
||||
if not self._joins:
|
||||
return tables
|
||||
items = [tables]
|
||||
for alias, (kind, table, condition) in self._joins.items():
|
||||
items.append(_sql_from_join(kind, alias, table, condition))
|
||||
for join_query, join_params in self._raw_joins.items():
|
||||
items.append(SQL(join_query, *join_params))
|
||||
return SQL(" ").join(items)
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user