text: from django.db import ProgrammingError
  from django.utils.functional import cached_property
  
  
  class BaseDatabaseFeatures:
      gis_enabled = False
      allows_group_by_pk = False
      allows_group_by_selected_pks = False
      empty_fetchmany_value = []
      update_can_self_select = True
  
      # Does the backend distinguish between '' and None?
      interprets_empty_strings_as_nulls = False
  
      # Does the backend allow inserting duplicate NULL rows in a nullable
      # unique field? All core backends implement this correctly, but other
      # databases such as SQL Server do not.
      supports_nullable_unique_constraints = True
  
      # Does the backend allow inserting duplicate rows when a unique_together
      # constraint exists and some fields are nullable but not all of them?
      supports_partially_nullable_unique_constraints = True
  
      can_use_chunked_reads = True
      can_return_columns_from_insert = False
      can_return_rows_from_bulk_insert = False
      has_bulk_insert = True
      uses_savepoints = True
      can_release_savepoints = False
  
      # If True, don't use integer foreign keys referring to, e.g., positive
      # integer primary keys.
      related_fields_match_type = False
      allow_sliced_subqueries_with_in = True
      has_select_for_update = False
      has_select_for_update_nowait = False
      has_select_for_update_skip_locked = False
      has_select_for_update_of = False
      # Does the database's SELECT FOR UPDATE OF syntax require a column rather
      # than a table?
      select_for_update_of_column = False
  
      # Does the default test database allow multiple connections?
      # Usually an indication that the test database is in-memory
      test_db_allows_multiple_connections = True
  
      # Can an object be saved without an explicit primary key?
      supports_unspecified_pk = False
  
      # Can a fixture contain forward references? i.e., are
      # FK constraints checked at the end of transaction, or
      # at the end of each save operation?
      supports_forward_references = True
  
      # Does the backend truncate names properly when they are too long?
      truncates_names = False
  
      # Is there a REAL datatype in addition to floats/doubles?
      has_real_datatype = False
      supports_subqueries_in_group_by = True
  
      # Is there a true datatype for uuid?
      has_native_uuid_field = False
  
      # Is there a true datatype for timedeltas?
      has_native_duration_field = False
  
      # Does the database driver supports same type temporal data subtraction
      # by returning the type used to store duration field?
      supports_temporal_subtraction = False
  
      # Does the __regex lookup support backreferencing and grouping?
      supports_regex_backreferencing = True
  
      # Can date/datetime lookups be performed using a string?
      supports_date_lookup_using_string = True
  
      # Can datetimes with timezones be used?
      supports_timezones = True
  
      # Does the database have a copy of the zoneinfo database?
      has_zoneinfo_database = True
  
      # When performing a GROUP BY, is an ORDER BY NULL required
      # to remove any ordering?
      requires_explicit_null_ordering_when_grouping = False
  
      # Does the backend order NULL values as largest or smallest?
      nulls_order_largest = False
  
      # Does the backend support NULLS FIRST and NULLS LAST in ORDER BY?
      supports_order_by_nulls_modifier = True
  
      # The database's limit on the number of query parameters.
      max_query_params = None
  
      # Can an object have an autoincrement primary key of 0? MySQL says No.
      allows_auto_pk_0 = True
  
      # Do we need to NULL a ForeignKey out, or can the constraint check be
      # deferred
      can_defer_constraint_checks = False
  
      # date_interval_sql can properly handle mixed Date/DateTime fields and timedeltas
      supports_mixed_date_datetime_comparisons = True
  
      # Does the backend support tablespaces? Default to False because it isn't
      # in the SQL standard.
      supports_tablespaces = False
  
      # Does the backend reset sequences between tests?
      supports_sequence_reset = True
  
      # Can the backend introspect the default value of a column?
      can_introspect_default = True
  
      # Confirm support for introspected foreign keys
      # Every database can do this reliably, except MySQL,
      # which can't do it for MyISAM tables
      can_introspect_foreign_keys = True
  
      # Can the backend introspect an AutoField, instead of an IntegerField?
      can_introspect_autofield = False
  
      # Can the backend introspect a BigIntegerField, instead of an IntegerField?
      can_introspect_big_integer_field = True
  
      # Can the backend introspect an BinaryField, instead of an TextField?
      can_introspect_binary_field = True
  
      # Can the backend introspect an DecimalField, instead of an FloatField?
      can_introspect_decimal_field = True
  
      # Can the backend introspect a DurationField, instead of a BigIntegerField?
      can_introspect_duration_field = True
  
      # Can the backend introspect an IPAddressField, instead of an CharField?
      can_introspect_ip_address_field = False
  
      # Can the backend introspect a PositiveIntegerField, instead of an IntegerField?
      can_introspect_positive_integer_field = False
  
      # Can the backend introspect a SmallIntegerField, instead of an IntegerField?
      can_introspect_small_integer_field = False
  
      # Can the backend introspect a TimeField, instead of a DateTimeField?
      can_introspect_time_field = True
  
      # Some backends may not be able to differentiate BigAutoField or
      # SmallAutoField from other fields such as AutoField.
      introspected_big_auto_field_type = 'BigAutoField'
      introspected_small_auto_field_type = 'SmallAutoField'
  
      # Some backends may not be able to differentiate BooleanField from other
      # fields such as IntegerField.
      introspected_boolean_field_type = 'BooleanField'
  
      # Can the backend introspect the column order (ASC/DESC) for indexes?
      supports_index_column_ordering = True
  
      # Does the backend support introspection of materialized views?
      can_introspect_materialized_views = False
  
      # Support for the DISTINCT ON clause
      can_distinct_on_fields = False
  
      # Does the backend prevent running SQL queries in broken transactions?
      atomic_transactions = True
  
      # Can we roll back DDL in a transaction?
      can_rollback_ddl = False
  
      # Does it support operations requiring references rename in a transaction?
      supports_atomic_references_rename = True
  
      # Can we issue more than one ALTER COLUMN clause in an ALTER TABLE?
      supports_combined_alters = False
  
      # Does it support foreign keys?
      supports_foreign_keys = True
  
      # Can it create foreign key constraints inline when adding columns?
      can_create_inline_fk = True
  
      # Does it support CHECK constraints?
      supports_column_check_constraints = True
      supports_table_check_constraints = True
      # Does the backend support introspection of CHECK constraints?
      can_introspect_check_constraints = True
  
      # Does the backend support 'pyformat' style ("... %(name)s ...", {'name': value})
      # parameter passing? Note this can be provided by the backend even if not
      # supported by the Python driver
      supports_paramstyle_pyformat = True
  
      # Does the backend require literal defaults, rather than parameterized ones?
      requires_literal_defaults = False
  
      # Does the backend require a connection reset after each material schema change?
      connection_persists_old_columns = False
  
      # What kind of error does the backend throw when accessing closed cursor?
      closed_cursor_error_class = ProgrammingError
  
      # Does 'a' LIKE 'A' match?
      has_case_insensitive_like = True
  
      # Suffix for backends that don't support "SELECT xxx;" queries.
      bare_select_suffix = ''
  
      # If NULL is implied on columns without needing to be explicitly specified
      implied_column_null = False
  
      # Does the backend support "select for update" queries with limit (and offset)?
      supports_select_for_update_with_limit = True
  
      # Does the backend ignore null expressions in GREATEST and LEAST queries unless
      # every expression is null?
      greatest_least_ignores_nulls = False
  
      # Can the backend clone databases for parallel test execution?
      # Defaults to False to allow third-party backends to opt-in.
      can_clone_databases = False
  
      # Does the backend consider table names with different casing to
      # be equal?
      ignores_table_name_case = False
  
      # Place FOR UPDATE right after FROM clause. Used on MSSQL.
      for_update_after_from = False
  
      # Combinatorial flags
      supports_select_union = True
      supports_select_intersection = True
      supports_select_difference = True
      supports_slicing_ordering_in_compound = False
      supports_parentheses_in_compound = True
  
      # Does the database support SQL 2003 FILTER (WHERE ...) in aggregate
      # expressions?
      supports_aggregate_filter_clause = False
  
      # Does the backend support indexing a TextField?
      supports_index_on_text_field = True
  
      # Does the backend support window expressions (expression OVER (...))?
      supports_over_clause = False
      supports_frame_range_fixed_distance = False
      only_supports_unbounded_with_preceding_and_following = False
  
      # Does the backend support CAST with precision?
      supports_cast_with_precision = True
  
      # How many second decimals does the database return when casting a value to
      # a type with time?
      time_cast_precision = 6
  
      # SQL to create a procedure for use by the Django test suite. The
      # functionality of the procedure isn't important.
      create_test_procedure_without_params_sql = None
      create_test_procedure_with_int_param_sql = None
  
      # Does the backend support keyword parameters for cursor.callproc()?
      supports_callproc_kwargs = False
  
      # Convert CharField results from bytes to str in database functions.
      db_functions_convert_bytes_to_str = False
  
      # What formats does the backend EXPLAIN syntax support?
      supported_explain_formats = set()
  
      # Does DatabaseOperations.explain_query_prefix() raise ValueError if
      # unknown kwargs are passed to QuerySet.explain()?
      validates_explain_options = True
  
      # Does the backend support the default parameter in lead() and lag()?
      supports_default_in_lead_lag = True
  
      # Does the backend support ignoring constraint or uniqueness errors during
      # INSERT?
      supports_ignore_conflicts = True
  
      # Does this backend require casting the results of CASE expressions used
      # in UPDATE statements to ensure the expression has the correct type?
      requires_casted_case_in_updates = False
  
      # Does the backend support partial indexes (CREATE INDEX ... WHERE ...)?
      supports_partial_indexes = True
      supports_functions_in_partial_indexes = True
  
      # Does the database allow more than one constraint or index on the same
      # field(s)?
      allows_multiple_constraints_on_same_fields = True
  
      # Does the backend support boolean expressions in the SELECT clause?
      supports_boolean_expr_in_select_clause = True
  
      def __init__(self, connection):
          self.connection = connection
  
      @cached_property
      def supports_explaining_query_execution(self):
          """Does this backend support explaining query execution?"""
          return self.connection.ops.explain_prefix is not None
  
      @cached_property
      def supports_transactions(self):
          """Confirm support for transactions."""
          with self.connection.cursor() as cursor:
              cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
              self.connection.set_autocommit(False)
              cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
              self.connection.rollback()
              self.connection.set_autocommit(True)
              cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
              count, = cursor.fetchone()
              cursor.execute('DROP TABLE ROLLBACK_TEST')
          return count == 0
  
      def allows_group_by_selected_pks_on_model(self, model):
          if not self.allows_group_by_selected_pks:
              return False
          return model._meta.managed
  