| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- # coding=utf-8
- """
- @project: maxkb
- @Author:虎
- @file: compiler.py
- @date:2023/10/7 10:53
- @desc:
- """
- from django.core.exceptions import EmptyResultSet, FullResultSet
- from django.db import NotSupportedError
- from django.db.models.sql.compiler import SQLCompiler
- from django.db.transaction import TransactionManagementError
- class AppSQLCompiler(SQLCompiler):
- def __init__(self, query, connection, using, elide_empty=True, field_replace_dict=None):
- super().__init__(query, connection, using, elide_empty)
- if field_replace_dict is None:
- field_replace_dict = {}
- self.field_replace_dict = field_replace_dict
- def get_query_str(self, with_limits=True, with_table_name=False, with_col_aliases=False):
- refcounts_before = self.query.alias_refcount.copy()
- try:
- combinator = self.query.combinator
- extra_select, order_by, group_by = self.pre_sql_setup(
- with_col_aliases=with_col_aliases or bool(combinator),
- )
- for_update_part = None
- # Is a LIMIT/OFFSET clause needed?
- with_limit_offset = with_limits and self.query.is_sliced
- combinator = self.query.combinator
- features = self.connection.features
- if combinator:
- if not getattr(features, "supports_select_{}".format(combinator)):
- raise NotSupportedError(
- "{} is not supported on this database backend.".format(
- combinator
- )
- )
- result, params = self.get_combinator_sql(
- combinator, self.query.combinator_all
- )
- elif self.qualify:
- result, params = self.get_qualify_sql()
- order_by = None
- else:
- distinct_fields, distinct_params = self.get_distinct()
- # This must come after 'select', 'ordering', and 'distinct'
- # (see docstring of get_from_clause() for details).
- from_, f_params = self.get_from_clause()
- try:
- where, w_params = (
- self.compile(self.where) if self.where is not None else ("", [])
- )
- except EmptyResultSet:
- if self.elide_empty:
- raise
- # Use a predicate that's always False.
- where, w_params = "0 = 1", []
- except FullResultSet:
- where, w_params = "", []
- try:
- having, h_params = (
- self.compile(self.having)
- if self.having is not None
- else ("", [])
- )
- except FullResultSet:
- having, h_params = "", []
- result = []
- params = []
- if self.query.distinct:
- distinct_result, distinct_params = self.connection.ops.distinct_sql(
- distinct_fields,
- distinct_params,
- )
- result += distinct_result
- params += distinct_params
- out_cols = []
- for _, (s_sql, s_params), alias in self.select + extra_select:
- if alias:
- s_sql = "%s AS %s" % (
- s_sql,
- self.connection.ops.quote_name(alias),
- )
- params.extend(s_params)
- out_cols.append(s_sql)
- params.extend(f_params)
- if self.query.select_for_update and features.has_select_for_update:
- if (
- self.connection.get_autocommit()
- # Don't raise an exception when database doesn't
- # support transactions, as it's a noop.
- and features.supports_transactions
- ):
- raise TransactionManagementError(
- "select_for_update cannot be used outside of a transaction."
- )
- if (
- with_limit_offset
- and not features.supports_select_for_update_with_limit
- ):
- raise NotSupportedError(
- "LIMIT/OFFSET is not supported with "
- "select_for_update on this database backend."
- )
- nowait = self.query.select_for_update_nowait
- skip_locked = self.query.select_for_update_skip_locked
- of = self.query.select_for_update_of
- no_key = self.query.select_for_no_key_update
- # If it's a NOWAIT/SKIP LOCKED/OF/NO KEY query but the
- # backend doesn't support it, raise NotSupportedError to
- # prevent a possible deadlock.
- if nowait and not features.has_select_for_update_nowait:
- raise NotSupportedError(
- "NOWAIT is not supported on this database backend."
- )
- elif skip_locked and not features.has_select_for_update_skip_locked:
- raise NotSupportedError(
- "SKIP LOCKED is not supported on this database backend."
- )
- elif of and not features.has_select_for_update_of:
- raise NotSupportedError(
- "FOR UPDATE OF is not supported on this database backend."
- )
- elif no_key and not features.has_select_for_no_key_update:
- raise NotSupportedError(
- "FOR NO KEY UPDATE is not supported on this "
- "database backend."
- )
- for_update_part = self.connection.ops.for_update_sql(
- nowait=nowait,
- skip_locked=skip_locked,
- of=self.get_select_for_update_of_arguments(),
- no_key=no_key,
- )
- if for_update_part and features.for_update_after_from:
- result.append(for_update_part)
- if where:
- result.append("WHERE %s" % where)
- params.extend(w_params)
- grouping = []
- for g_sql, g_params in group_by:
- grouping.append(g_sql)
- params.extend(g_params)
- if grouping:
- if distinct_fields:
- raise NotImplementedError(
- "annotate() + distinct(fields) is not implemented."
- )
- order_by = order_by or self.connection.ops.force_no_ordering()
- result.append("GROUP BY %s" % ", ".join(grouping))
- if self._meta_ordering:
- order_by = None
- if having:
- result.append("HAVING %s" % having)
- params.extend(h_params)
- if self.query.explain_info:
- result.insert(
- 0,
- self.connection.ops.explain_query_prefix(
- self.query.explain_info.format,
- **self.query.explain_info.options,
- ),
- )
- if order_by:
- ordering = []
- for _, (o_sql, o_params, _) in order_by:
- ordering.append(o_sql)
- params.extend(o_params)
- order_by_sql = "ORDER BY %s" % ", ".join(ordering)
- if combinator and features.requires_compound_order_by_subquery:
- result = ["SELECT * FROM (", *result, ")", order_by_sql]
- else:
- result.append(order_by_sql)
- if with_limit_offset:
- result.append(
- self.connection.ops.limit_offset_sql(
- self.query.low_mark, self.query.high_mark
- )
- )
- if for_update_part and not features.for_update_after_from:
- result.append(for_update_part)
- from_, f_params = self.get_from_clause()
- sql = " ".join(result)
- if not with_table_name:
- for table_name in from_:
- sql = sql.replace(table_name + ".", "")
- for key in self.field_replace_dict.keys():
- value = self.field_replace_dict.get(key)
- sql = sql.replace(key, value)
- return sql, tuple(params)
- finally:
- # Finally do cleanup - get rid of the joins we created above.
- self.query.reset_refcounts(refcounts_before)
- def as_sql(self, with_limits=True, with_col_aliases=False, select_string=None):
- if select_string is None:
- return super().as_sql(with_limits, with_col_aliases)
- else:
- sql, params = self.get_query_str(with_table_name=False)
- return (select_string + " " + sql), params
|