features.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import operator
  2. from django.db.backends.base.features import BaseDatabaseFeatures
  3. from django.utils.functional import cached_property
  4. class DatabaseFeatures(BaseDatabaseFeatures):
  5. empty_fetchmany_value = ()
  6. update_can_self_select = False
  7. allows_group_by_pk = True
  8. related_fields_match_type = True
  9. # MySQL doesn't support sliced subqueries with IN/ALL/ANY/SOME.
  10. allow_sliced_subqueries_with_in = False
  11. has_select_for_update = True
  12. supports_forward_references = False
  13. supports_regex_backreferencing = False
  14. supports_date_lookup_using_string = False
  15. can_introspect_autofield = True
  16. can_introspect_binary_field = False
  17. can_introspect_duration_field = False
  18. can_introspect_small_integer_field = True
  19. can_introspect_positive_integer_field = True
  20. introspected_boolean_field_type = 'IntegerField'
  21. supports_index_column_ordering = False
  22. supports_timezones = False
  23. requires_explicit_null_ordering_when_grouping = True
  24. allows_auto_pk_0 = False
  25. can_release_savepoints = True
  26. atomic_transactions = False
  27. can_clone_databases = True
  28. supports_temporal_subtraction = True
  29. supports_select_intersection = False
  30. supports_select_difference = False
  31. supports_slicing_ordering_in_compound = True
  32. supports_index_on_text_field = False
  33. has_case_insensitive_like = False
  34. create_test_procedure_without_params_sql = """
  35. CREATE PROCEDURE test_procedure ()
  36. BEGIN
  37. DECLARE V_I INTEGER;
  38. SET V_I = 1;
  39. END;
  40. """
  41. create_test_procedure_with_int_param_sql = """
  42. CREATE PROCEDURE test_procedure (P_I INTEGER)
  43. BEGIN
  44. DECLARE V_I INTEGER;
  45. SET V_I = P_I;
  46. END;
  47. """
  48. db_functions_convert_bytes_to_str = True
  49. # Alias MySQL's TRADITIONAL to TEXT for consistency with other backends.
  50. supported_explain_formats = {'JSON', 'TEXT', 'TRADITIONAL'}
  51. # Neither MySQL nor MariaDB support partial indexes.
  52. supports_partial_indexes = False
  53. @cached_property
  54. def _mysql_storage_engine(self):
  55. "Internal method used in Django tests. Don't rely on this from your code"
  56. with self.connection.cursor() as cursor:
  57. cursor.execute("SELECT ENGINE FROM INFORMATION_SCHEMA.ENGINES WHERE SUPPORT = 'DEFAULT'")
  58. result = cursor.fetchone()
  59. return result[0]
  60. @cached_property
  61. def can_introspect_foreign_keys(self):
  62. "Confirm support for introspected foreign keys"
  63. return self._mysql_storage_engine != 'MyISAM'
  64. @cached_property
  65. def has_zoneinfo_database(self):
  66. # Test if the time zone definitions are installed. CONVERT_TZ returns
  67. # NULL if 'UTC' timezone isn't loaded into the mysql.time_zone.
  68. with self.connection.cursor() as cursor:
  69. cursor.execute("SELECT CONVERT_TZ('2001-01-01 01:00:00', 'UTC', 'UTC')")
  70. return cursor.fetchone()[0] is not None
  71. @cached_property
  72. def is_sql_auto_is_null_enabled(self):
  73. with self.connection.cursor() as cursor:
  74. cursor.execute('SELECT @@SQL_AUTO_IS_NULL')
  75. result = cursor.fetchone()
  76. return result and result[0] == 1
  77. @cached_property
  78. def supports_over_clause(self):
  79. if self.connection.mysql_is_mariadb:
  80. return self.connection.mysql_version >= (10, 2)
  81. return self.connection.mysql_version >= (8, 0, 2)
  82. @cached_property
  83. def supports_column_check_constraints(self):
  84. if self.connection.mysql_is_mariadb:
  85. return self.connection.mysql_version >= (10, 2, 1)
  86. return self.connection.mysql_version >= (8, 0, 16)
  87. supports_table_check_constraints = property(operator.attrgetter('supports_column_check_constraints'))
  88. @cached_property
  89. def can_introspect_check_constraints(self):
  90. if self.connection.mysql_is_mariadb:
  91. version = self.connection.mysql_version
  92. return (version >= (10, 2, 22) and version < (10, 3)) or version >= (10, 3, 10)
  93. return self.connection.mysql_version >= (8, 0, 16)
  94. @cached_property
  95. def has_select_for_update_skip_locked(self):
  96. return not self.connection.mysql_is_mariadb and self.connection.mysql_version >= (8, 0, 1)
  97. @cached_property
  98. def has_select_for_update_nowait(self):
  99. if self.connection.mysql_is_mariadb:
  100. return self.connection.mysql_version >= (10, 3, 0)
  101. return self.connection.mysql_version >= (8, 0, 1)
  102. @cached_property
  103. def needs_explain_extended(self):
  104. # EXTENDED is deprecated (and not required) in MySQL 5.7.
  105. return not self.connection.mysql_is_mariadb and self.connection.mysql_version < (5, 7)
  106. @cached_property
  107. def supports_transactions(self):
  108. """
  109. All storage engines except MyISAM support transactions.
  110. """
  111. return self._mysql_storage_engine != 'MyISAM'
  112. @cached_property
  113. def ignores_table_name_case(self):
  114. with self.connection.cursor() as cursor:
  115. cursor.execute('SELECT @@LOWER_CASE_TABLE_NAMES')
  116. result = cursor.fetchone()
  117. return result and result[0] != 0
  118. @cached_property
  119. def supports_default_in_lead_lag(self):
  120. # To be added in https://jira.mariadb.org/browse/MDEV-12981.
  121. return not self.connection.mysql_is_mariadb