codekingpro/portable-devtools
115k
1# testing/requirements.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: ignore-errors
8
9
10"""Global database feature support policy.
11
12Provides decorators to mark tests requiring specific feature support from the
13target database.
14
15External dialect test suites should subclass SuiteRequirements
16to provide specific inclusion/exclusions.
17
18"""
19
20from __future__ import annotations
21
22import os
23import platform
24
25from . import asyncio as _test_asyncio
26from . import exclusions
27from .exclusions import only_on
28from .. import create_engine
29from .. import util
30from ..pool import QueuePool
31
32
33class Requirements:
34 pass
35
36
37class SuiteRequirements(Requirements):
38 @property
39 def create_table(self):
40 """target platform can emit basic CreateTable DDL."""
41
42 return exclusions.open()
43
44 @property
45 def drop_table(self):
46 """target platform can emit basic DropTable DDL."""
47
48 return exclusions.open()
49
50 @property
51 def table_ddl_if_exists(self):
52 """target platform supports IF NOT EXISTS / IF EXISTS for tables."""
53
54 return exclusions.closed()
55
56 @property
57 def index_ddl_if_exists(self):
58 """target platform supports IF NOT EXISTS / IF EXISTS for indexes."""
59
60 return exclusions.closed()
61
62 @property
63 def uuid_data_type(self):
64 """Return databases that support the UUID datatype."""
65
66 return exclusions.closed()
67
68 @property
69 def foreign_keys(self):
70 """Target database must support foreign keys."""
71
72 return exclusions.open()
73
74 @property
75 def foreign_keys_reflect_as_index(self):
76 """Target database creates an index that's reflected for
77 foreign keys."""
78
79 return exclusions.closed()
80
81 @property
82 def unique_index_reflect_as_unique_constraints(self):
83 """Target database reflects unique indexes as unique constrains."""
84
85 return exclusions.closed()
86
87 @property
88 def unique_constraints_reflect_as_index(self):
89 """Target database reflects unique constraints as indexes."""
90
91 return exclusions.closed()
92
93 @property
94 def table_value_constructor(self):
95 """Database / dialect supports a query like:
96
97 .. sourcecode:: sql
98
99 SELECT * FROM VALUES ( (c1, c2), (c1, c2), ...)
100 AS some_table(col1, col2)
101
102 SQLAlchemy generates this with the :func:`_sql.values` function.
103
104 """
105 return exclusions.closed()
106
107 @property
108 def standard_cursor_sql(self):
109 """Target database passes SQL-92 style statements to cursor.execute()
110 when a statement like select() or insert() is run.
111
112 A very small portion of dialect-level tests will ensure that certain
113 conditions are present in SQL strings, and these tests use very basic
114 SQL that will work on any SQL-like platform in order to assert results.
115
116 It's normally a given for any pep-249 DBAPI that a statement like
117 "SELECT id, name FROM table WHERE some_table.id=5" will work.
118 However, there are dialects that don't actually produce SQL Strings
119 and instead may work with symbolic objects instead, or dialects that
120 aren't working with SQL, so for those this requirement can be marked
121 as excluded.
122
123 """
124
125 return exclusions.open()
126
127 @property
128 def on_update_cascade(self):
129 """target database must support ON UPDATE..CASCADE behavior in
130 foreign keys."""
131
132 return exclusions.open()
133
134 @property
135 def non_updating_cascade(self):
136 """target database must *not* support ON UPDATE..CASCADE behavior in
137 foreign keys."""
138 return exclusions.closed()
139
140 @property
141 def deferrable_fks(self):
142 return exclusions.closed()
143
144 @property
145 def on_update_or_deferrable_fks(self):
146 # TODO: exclusions should be composable,
147 # somehow only_if([x, y]) isn't working here, negation/conjunctions
148 # getting confused.
149 return exclusions.only_if(
150 lambda: self.on_update_cascade.enabled
151 or self.deferrable_fks.enabled
152 )
153
154 @property
155 def queue_pool(self):
156 """target database is using QueuePool"""
157
158 def go(config):
159 return isinstance(config.db.pool, QueuePool)
160
161 return exclusions.only_if(go)
162
163 @property
164 def self_referential_foreign_keys(self):
165 """Target database must support self-referential foreign keys."""
166
167 return exclusions.open()
168
169 @property
170 def foreign_key_ddl(self):
171 """Target database must support the DDL phrases for FOREIGN KEY."""
172
173 return exclusions.open()
174
175 @property
176 def named_constraints(self):
177 """target database must support names for constraints."""
178
179 return exclusions.open()
180
181 @property
182 def implicitly_named_constraints(self):
183 """target database must apply names to unnamed constraints."""
184
185 return exclusions.open()
186
187 @property
188 def unusual_column_name_characters(self):
189 """target database allows column names that have unusual characters
190 in them, such as dots, spaces, slashes, or percent signs.
191
192 The column names are as always in such a case quoted, however the
193 DB still needs to support those characters in the name somehow.
194
195 """
196 return exclusions.open()
197
198 @property
199 def subqueries(self):
200 """Target database must support subqueries."""
201
202 return exclusions.open()
203
204 @property
205 def offset(self):
206 """target database can render OFFSET, or an equivalent, in a
207 SELECT.
208 """
209
210 return exclusions.open()
211
212 @property
213 def bound_limit_offset(self):
214 """target database can render LIMIT and/or OFFSET using a bound
215 parameter
216 """
217
218 return exclusions.open()
219
220 @property
221 def sql_expression_limit_offset(self):
222 """target database can render LIMIT and/or OFFSET with a complete
223 SQL expression, such as one that uses the addition operator.
224 parameter
225 """
226
227 return exclusions.open()
228
229 @property
230 def parens_in_union_contained_select_w_limit_offset(self):
231 """Target database must support parenthesized SELECT in UNION
232 when LIMIT/OFFSET is specifically present.
233
234 E.g. (SELECT ...) UNION (SELECT ..)
235
236 This is known to fail on SQLite.
237
238 """
239 return exclusions.open()
240
241 @property
242 def parens_in_union_contained_select_wo_limit_offset(self):
243 """Target database must support parenthesized SELECT in UNION
244 when OFFSET/LIMIT is specifically not present.
245
246 E.g. (SELECT ... LIMIT ..) UNION (SELECT .. OFFSET ..)
247
248 This is known to fail on SQLite. It also fails on Oracle
249 because without LIMIT/OFFSET, there is currently no step that
250 creates an additional subquery.
251
252 """
253 return exclusions.open()
254
255 @property
256 def boolean_col_expressions(self):
257 """Target database must support boolean expressions as columns"""
258
259 return exclusions.closed()
260
261 @property
262 def nullable_booleans(self):
263 """Target database allows boolean columns to store NULL."""
264
265 return exclusions.open()
266
267 @property
268 def nullsordering(self):
269 """Target backends that support nulls ordering."""
270
271 return exclusions.closed()
272
273 @property
274 def standalone_binds(self):
275 """target database/driver supports bound parameters as column
276 expressions without being in the context of a typed column.
277 """
278 return exclusions.open()
279
280 @property
281 def standalone_null_binds_whereclause(self):
282 """target database/driver supports bound parameters with NULL in the
283 WHERE clause, in situations where it has to be typed.
284
285 """
286 return exclusions.open()
287
288 @property
289 def intersect(self):
290 """Target database must support INTERSECT or equivalent."""
291 return exclusions.closed()
292
293 @property
294 def except_(self):
295 """Target database must support EXCEPT or equivalent (i.e. MINUS)."""
296 return exclusions.closed()
297
298 @property
299 def window_functions(self):
300 """Target database must support window functions."""
301 return exclusions.closed()
302
303 @property
304 def ctes(self):
305 """Target database supports CTEs"""
306
307 return exclusions.closed()
308
309 @property
310 def ctes_with_update_delete(self):
311 """target database supports CTES that ride on top of a normal UPDATE
312 or DELETE statement which refers to the CTE in a correlated subquery.
313
314 """
315
316 return exclusions.closed()
317
318 @property
319 def ctes_with_values(self):
320 """target database supports CTES that ride on top of a VALUES
321 clause."""
322
323 return exclusions.closed()
324
325 @property
326 def ctes_on_dml(self):
327 """target database supports CTES which consist of INSERT, UPDATE
328 or DELETE *within* the CTE, e.g. WITH x AS (UPDATE....)"""
329
330 return exclusions.closed()
331
332 @property
333 def autoincrement_insert(self):
334 """target platform generates new surrogate integer primary key values
335 when insert() is executed, excluding the pk column."""
336
337 return exclusions.open()
338
339 @property
340 def fetch_rows_post_commit(self):
341 """target platform will allow cursor.fetchone() to proceed after a
342 COMMIT.
343
344 Typically this refers to an INSERT statement with RETURNING which
345 is invoked within "autocommit". If the row can be returned
346 after the autocommit, then this rule can be open.
347
348 """
349
350 return exclusions.open()
351
352 @property
353 def group_by_complex_expression(self):
354 """target platform supports SQL expressions in GROUP BY
355
356 e.g.
357
358 SELECT x + y AS somelabel FROM table GROUP BY x + y
359
360 """
361
362 return exclusions.open()
363
364 @property
365 def sane_rowcount(self):
366 return exclusions.skip_if(
367 lambda config: not config.db.dialect.supports_sane_rowcount,
368 "driver doesn't support 'sane' rowcount",
369 )
370
371 @property
372 def sane_multi_rowcount(self):
373 return exclusions.fails_if(
374 lambda config: not config.db.dialect.supports_sane_multi_rowcount,
375 "driver %(driver)s %(doesnt_support)s 'sane' multi row count",
376 )
377
378 @property
379 def sane_rowcount_w_returning(self):
380 return exclusions.fails_if(
381 lambda config: not (
382 config.db.dialect.supports_sane_rowcount_returning
383 ),
384 "driver doesn't support 'sane' rowcount when returning is on",
385 )
386
387 @property
388 def empty_inserts(self):
389 """target platform supports INSERT with no values, i.e.
390 INSERT DEFAULT VALUES or equivalent."""
391
392 return exclusions.only_if(
393 lambda config: config.db.dialect.supports_empty_insert
394 or config.db.dialect.supports_default_values
395 or config.db.dialect.supports_default_metavalue,
396 "empty inserts not supported",
397 )
398
399 @property
400 def empty_inserts_executemany(self):
401 """target platform supports INSERT with no values, i.e.
402 INSERT DEFAULT VALUES or equivalent, within executemany()"""
403
404 return self.empty_inserts
405
406 @property
407 def insert_from_select(self):
408 """target platform supports INSERT from a SELECT."""
409
410 return exclusions.open()
411
412 @property
413 def delete_returning(self):
414 """target platform supports DELETE ... RETURNING."""
415
416 return exclusions.only_if(
417 lambda config: config.db.dialect.delete_returning,
418 "%(database)s %(does_support)s 'DELETE ... RETURNING'",
419 )
420
421 @property
422 def insert_returning(self):
423 """target platform supports INSERT ... RETURNING."""
424
425 return exclusions.only_if(
426 lambda config: config.db.dialect.insert_returning,
427 "%(database)s %(does_support)s 'INSERT ... RETURNING'",
428 )
429
430 @property
431 def update_returning(self):
432 """target platform supports UPDATE ... RETURNING."""
433
434 return exclusions.only_if(
435 lambda config: config.db.dialect.update_returning,
436 "%(database)s %(does_support)s 'UPDATE ... RETURNING'",
437 )
438
439 @property
440 def insert_executemany_returning(self):
441 """target platform supports RETURNING when INSERT is used with
442 executemany(), e.g. multiple parameter sets, indicating
443 as many rows come back as do parameter sets were passed.
444
445 """
446
447 return exclusions.only_if(
448 lambda config: config.db.dialect.insert_executemany_returning,
449 "%(database)s %(does_support)s 'RETURNING of "
450 "multiple rows with INSERT executemany'",
451 )
452
453 @property
454 def insertmanyvalues(self):
455 return exclusions.only_if(
456 lambda config: config.db.dialect.supports_multivalues_insert
457 and config.db.dialect.insert_returning
458 and config.db.dialect.use_insertmanyvalues,
459 "%(database)s %(does_support)s 'insertmanyvalues functionality",
460 )
461
462 @property
463 def tuple_in(self):
464 """Target platform supports the syntax
465 "(x, y) IN ((x1, y1), (x2, y2), ...)"
466 """
467
468 return exclusions.closed()
469
470 @property
471 def tuple_in_w_empty(self):
472 """Target platform tuple IN w/ empty set"""
473 return self.tuple_in
474
475 @property
476 def duplicate_names_in_cursor_description(self):
477 """target platform supports a SELECT statement that has
478 the same name repeated more than once in the columns list."""
479
480 return exclusions.open()
481
482 @property
483 def denormalized_names(self):
484 """Target database must have 'denormalized', i.e.
485 UPPERCASE as case insensitive names."""
486
487 return exclusions.skip_if(
488 lambda config: not config.db.dialect.requires_name_normalize,
489 "Backend does not require denormalized names.",
490 )
491
492 @property
493 def multivalues_inserts(self):
494 """target database must support multiple VALUES clauses in an
495 INSERT statement."""
496
497 return exclusions.skip_if(
498 lambda config: not config.db.dialect.supports_multivalues_insert,
499 "Backend does not support multirow inserts.",
500 )
501
502 @property
503 def implements_get_lastrowid(self):
504 """target dialect implements the executioncontext.get_lastrowid()
505 method without reliance on RETURNING.
506
507 """
508 return exclusions.open()
509
510 @property
511 def arraysize(self):
512 """dialect includes the required pep-249 attribute
513 ``cursor.arraysize``"""
514
515 return exclusions.open()
516
517 @property
518 def emulated_lastrowid(self):
519 """target dialect retrieves cursor.lastrowid, or fetches
520 from a database-side function after an insert() construct executes,
521 within the get_lastrowid() method.
522
523 Only dialects that "pre-execute", or need RETURNING to get last
524 inserted id, would return closed/fail/skip for this.
525
526 """
527 return exclusions.closed()
528
529 @property
530 def emulated_lastrowid_even_with_sequences(self):
531 """target dialect retrieves cursor.lastrowid or an equivalent
532 after an insert() construct executes, even if the table has a
533 Sequence on it.
534
535 """
536 return exclusions.closed()
537
538 @property
539 def dbapi_lastrowid(self):
540 """target platform includes a 'lastrowid' accessor on the DBAPI
541 cursor object.
542
543 """
544 return exclusions.closed()
545
546 @property
547 def views(self):
548 """Target database must support VIEWs."""
549
550 return exclusions.closed()
551
552 @property
553 def schemas(self):
554 """Target database must support external schemas, and have one
555 named 'test_schema'."""
556
557 return only_on(lambda config: config.db.dialect.supports_schemas)
558
559 @property
560 def cross_schema_fk_reflection(self):
561 """target system must support reflection of inter-schema
562 foreign keys"""
563 return exclusions.closed()
564
565 @property
566 def foreign_key_constraint_name_reflection(self):
567 """Target supports reflection of FOREIGN KEY constraints and
568 will return the name of the constraint that was used in the
569 "CONSTRAINT <name> FOREIGN KEY" DDL.
570
571 MySQL prior to version 8 and MariaDB prior to version 10.5
572 don't support this.
573
574 """
575 return exclusions.closed()
576
577 @property
578 def implicit_default_schema(self):
579 """target system has a strong concept of 'default' schema that can
580 be referred to implicitly.
581
582 basically, PostgreSQL.
583
584 """
585 return exclusions.closed()
586
587 @property
588 def default_schema_name_switch(self):
589 """target dialect implements provisioning module including
590 set_default_schema_on_connection"""
591
592 return exclusions.closed()
593
594 @property
595 def server_side_cursors(self):
596 """Target dialect must support server side cursors."""
597
598 return exclusions.only_if(
599 [lambda config: config.db.dialect.supports_server_side_cursors],
600 "no server side cursors support",
601 )
602
603 @property
604 def sequences(self):
605 """Target database must support SEQUENCEs."""
606
607 return exclusions.only_if(
608 [lambda config: config.db.dialect.supports_sequences],
609 "no sequence support",
610 )
611
612 @property
613 def no_sequences(self):
614 """the opposite of "sequences", DB does not support sequences at
615 all."""
616
617 return exclusions.NotPredicate(self.sequences)
618
619 @property
620 def sequences_optional(self):
621 """Target database supports sequences, but also optionally
622 as a means of generating new PK values."""
623
624 return exclusions.only_if(
625 [
626 lambda config: config.db.dialect.supports_sequences
627 and config.db.dialect.sequences_optional
628 ],
629 "no sequence support, or sequences not optional",
630 )
631
632 @property
633 def supports_lastrowid(self):
634 """target database / driver supports cursor.lastrowid as a means
635 of retrieving the last inserted primary key value.
636
637 note that if the target DB supports sequences also, this is still
638 assumed to work. This is a new use case brought on by MariaDB 10.3.
639
640 """
641 return exclusions.only_if(
642 [lambda config: config.db.dialect.postfetch_lastrowid]
643 )
644
645 @property
646 def no_lastrowid_support(self):
647 """the opposite of supports_lastrowid"""
648 return exclusions.only_if(
649 [lambda config: not config.db.dialect.postfetch_lastrowid]
650 )
651
652 @property
653 def reflects_pk_names(self):
654 return exclusions.closed()
655
656 @property
657 def table_reflection(self):
658 """target database has general support for table reflection"""
659 return exclusions.open()
660
661 @property
662 def reflect_tables_no_columns(self):
663 """target database supports creation and reflection of tables with no
664 columns, or at least tables that seem to have no columns."""
665
666 return exclusions.closed()
667
668 @property
669 def temp_table_comment_reflection(self):
670 """indicates if database supports comments on temp tables and
671 the dialect can reflect them"""
672 return exclusions.closed()
673
674 @property
675 def comment_reflection(self):
676 """Indicates if the database support table comment reflection"""
677 return exclusions.closed()
678
679 @property
680 def comment_reflection_full_unicode(self):
681 """Indicates if the database support table comment reflection in the
682 full unicode range, including emoji etc.
683 """
684 return exclusions.closed()
685
686 @property
687 def constraint_comment_reflection(self):
688 """indicates if the database support comments on constraints
689 and their reflection"""
690 return exclusions.closed()
691
692 @property
693 def column_collation_reflection(self):
694 """Indicates if the database support column collation reflection.
695
696 This requirement also uses ``get_order_by_collation`` to get
697 an available collation.
698 """
699 return exclusions.closed()
700
701 @property
702 def view_column_reflection(self):
703 """target database must support retrieval of the columns in a view,
704 similarly to how a table is inspected.
705
706 This does not include the full CREATE VIEW definition.
707
708 """
709 return self.views
710
711 @property
712 def view_reflection(self):
713 """target database must support inspection of the full CREATE VIEW
714 definition."""
715 return self.views
716
717 @property
718 def schema_reflection(self):
719 return self.schemas
720
721 @property
722 def schema_create_delete(self):
723 """target database supports schema create and dropped with
724 'CREATE SCHEMA' and 'DROP SCHEMA'"""
725 return exclusions.closed()
726
727 @property
728 def primary_key_constraint_reflection(self):
729 return exclusions.open()
730
731 @property
732 def foreign_key_constraint_reflection(self):
733 return exclusions.open()
734
735 @property
736 def foreign_key_constraint_option_reflection_ondelete(self):
737 return exclusions.closed()
738
739 @property
740 def fk_constraint_option_reflection_ondelete_restrict(self):
741 return exclusions.closed()
742
743 @property
744 def fk_constraint_option_reflection_ondelete_noaction(self):
745 return exclusions.closed()
746
747 @property
748 def foreign_key_constraint_option_reflection_onupdate(self):
749 return exclusions.closed()
750
751 @property
752 def fk_constraint_option_reflection_onupdate_restrict(self):
753 return exclusions.closed()
754
755 @property
756 def temp_table_reflection(self):
757 return exclusions.open()
758
759 @property
760 def temp_table_reflect_indexes(self):
761 return self.temp_table_reflection
762
763 @property
764 def temp_table_names(self):
765 """target dialect supports listing of temporary table names"""
766 return exclusions.closed()
767
768 @property
769 def has_temp_table(self):
770 """target dialect supports checking a single temp table name"""
771 return exclusions.closed()
772
773 @property
774 def temporary_tables(self):
775 """target database supports temporary tables"""
776 return exclusions.open()
777
778 @property
779 def temporary_views(self):
780 """target database supports temporary views"""
781 return exclusions.closed()
782
783 @property
784 def index_reflection(self):
785 return exclusions.open()
786
787 @property
788 def index_reflects_included_columns(self):
789 return exclusions.closed()
790
791 @property
792 def indexes_with_ascdesc(self):
793 """target database supports CREATE INDEX with per-column ASC/DESC."""
794 return exclusions.open()
795
796 @property
797 def reflect_indexes_with_ascdesc(self):
798 """target database supports reflecting INDEX with per-column
799 ASC/DESC."""
800 return exclusions.open()
801
802 @property
803 def reflect_indexes_with_ascdesc_as_expression(self):
804 """target database supports reflecting INDEX with per-column
805 ASC/DESC but reflects them as expressions (like oracle)."""
806 return exclusions.closed()
807
808 @property
809 def indexes_check_column_order(self):
810 """target database supports CREATE INDEX with column order check."""
811 return exclusions.closed()
812
813 @property
814 def indexes_with_expressions(self):
815 """target database supports CREATE INDEX against SQL expressions."""
816 return exclusions.closed()
817
818 @property
819 def reflect_indexes_with_expressions(self):
820 """target database supports reflection of indexes with
821 SQL expressions."""
822 return exclusions.closed()
823
824 @property
825 def unique_constraint_reflection(self):
826 """target dialect supports reflection of unique constraints"""
827 return exclusions.open()
828
829 @property
830 def inline_check_constraint_reflection(self):
831 """target dialect supports reflection of inline check constraints"""
832 return exclusions.closed()
833
834 @property
835 def check_constraint_reflection(self):
836 """target dialect supports reflection of check constraints"""
837 return exclusions.closed()
838
839 @property
840 def duplicate_key_raises_integrity_error(self):
841 """target dialect raises IntegrityError when reporting an INSERT
842 with a primary key violation. (hint: it should)
843
844 """
845 return exclusions.open()
846
847 @property
848 def unbounded_varchar(self):
849 """Target database must support VARCHAR with no length"""
850
851 return exclusions.open()
852
853 @property
854 def nvarchar_types(self):
855 """target database supports NVARCHAR and NCHAR as an actual datatype"""
856 return exclusions.closed()
857
858 @property
859 def unicode_data_no_special_types(self):
860 """Target database/dialect can receive / deliver / compare data with
861 non-ASCII characters in plain VARCHAR, TEXT columns, without the need
862 for special "national" datatypes like NVARCHAR or similar.
863
864 """
865 return exclusions.open()
866
867 @property
868 def unicode_data(self):
869 """Target database/dialect must support Python unicode objects with
870 non-ASCII characters represented, delivered as bound parameters
871 as well as in result rows.
872
873 """
874 return exclusions.open()
875
876 @property
877 def unicode_ddl(self):
878 """Target driver must support some degree of non-ascii symbol
879 names.
880 """
881 return exclusions.closed()
882
883 @property
884 def symbol_names_w_double_quote(self):
885 """Target driver can create tables with a name like 'some " table'"""
886 return exclusions.open()
887
888 @property
889 def datetime_interval(self):
890 """target dialect supports rendering of a datetime.timedelta as a
891 literal string, e.g. via the TypeEngine.literal_processor() method.
892
893 """
894 return exclusions.closed()
895
896 @property
897 def datetime_literals(self):
898 """target dialect supports rendering of a date, time, or datetime as a
899 literal string, e.g. via the TypeEngine.literal_processor() method.
900
901 """
902
903 return exclusions.closed()
904
905 @property
906 def datetime(self):
907 """target dialect supports representation of Python
908 datetime.datetime() objects."""
909
910 return exclusions.open()
911
912 @property
913 def datetime_timezone(self):
914 """target dialect supports representation of Python
915 datetime.datetime() with tzinfo with DateTime(timezone=True)."""
916
917 return exclusions.closed()
918
919 @property
920 def time_timezone(self):
921 """target dialect supports representation of Python
922 datetime.time() with tzinfo with Time(timezone=True)."""
923
924 return exclusions.closed()
925
926 @property
927 def date_implicit_bound(self):
928 """target dialect when given a date object will bind it such
929 that the database server knows the object is a date, and not
930 a plain string.
931
932 """
933 return exclusions.open()
934
935 @property
936 def time_implicit_bound(self):
937 """target dialect when given a time object will bind it such
938 that the database server knows the object is a time, and not
939 a plain string.
940
941 """
942 return exclusions.open()
943
944 @property
945 def datetime_implicit_bound(self):
946 """target dialect when given a datetime object will bind it such
947 that the database server knows the object is a datetime, and not
948 a plain string.
949
950 """
951 return exclusions.open()
952
953 @property
954 def datetime_microseconds(self):
955 """target dialect supports representation of Python
956 datetime.datetime() with microsecond objects."""
957
958 return exclusions.open()
959
960 @property
961 def timestamp_microseconds(self):
962 """target dialect supports representation of Python
963 datetime.datetime() with microsecond objects but only
964 if TIMESTAMP is used."""
965 return exclusions.closed()
966
967 @property
968 def timestamp_microseconds_implicit_bound(self):
969 """target dialect when given a datetime object which also includes
970 a microseconds portion when using the TIMESTAMP data type
971 will bind it such that the database server knows
972 the object is a datetime with microseconds, and not a plain string.
973
974 """
975 return self.timestamp_microseconds
976
977 @property
978 def datetime_historic(self):
979 """target dialect supports representation of Python
980 datetime.datetime() objects with historic (pre 1970) values."""
981
982 return exclusions.closed()
983
984 @property
985 def date(self):
986 """target dialect supports representation of Python
987 datetime.date() objects."""
988
989 return exclusions.open()
990
991 @property
992 def date_coerces_from_datetime(self):
993 """target dialect accepts a datetime object as the target
994 of a date column."""
995
996 return exclusions.open()
997
998 @property
999 def date_historic(self):
1000 """target dialect supports representation of Python
1001 datetime.datetime() objects with historic (pre 1970) values."""
1002
1003 return exclusions.closed()
1004
1005 @property
1006 def time(self):
1007 """target dialect supports representation of Python
1008 datetime.time() objects."""
1009
1010 return exclusions.open()
1011
1012 @property
1013 def time_microseconds(self):
1014 """target dialect supports representation of Python
1015 datetime.time() with microsecond objects."""
1016
1017 return exclusions.open()
1018
1019 @property
1020 def binary_comparisons(self):
1021 """target database/driver can allow BLOB/BINARY fields to be compared
1022 against a bound parameter value.
1023 """
1024
1025 return exclusions.open()
1026
1027 @property
1028 def binary_literals(self):
1029 """target backend supports simple binary literals, e.g. an
1030 expression like:
1031
1032 .. sourcecode:: sql
1033
1034 SELECT CAST('foo' AS BINARY)
1035
1036 Where ``BINARY`` is the type emitted from :class:`.LargeBinary`,
1037 e.g. it could be ``BLOB`` or similar.
1038
1039 Basically fails on Oracle.
1040
1041 """
1042
1043 return exclusions.open()
1044
1045 @property
1046 def autocommit(self):
1047 """target dialect supports 'AUTOCOMMIT' as an isolation_level"""
1048 return exclusions.closed()
1049
1050 @property
1051 def skip_autocommit_rollback(self):
1052 """target dialect supports the detect_autocommit_setting() method and
1053 uses the default implementation of do_rollback()"""
1054
1055 return exclusions.closed()
1056
1057 @property
1058 def isolation_level(self):
1059 """target dialect supports general isolation level settings.
1060
1061 Note that this requirement, when enabled, also requires that
1062 the get_isolation_levels() method be implemented.
1063
1064 """
1065 return exclusions.closed()
1066
1067 def get_isolation_levels(self, config):
1068 """Return a structure of supported isolation levels for the current
1069 testing dialect.
1070
1071 The structure indicates to the testing suite what the expected
1072 "default" isolation should be, as well as the other values that
1073 are accepted. The dictionary has two keys, "default" and "supported".
1074 The "supported" key refers to a list of all supported levels and
1075 it should include AUTOCOMMIT if the dialect supports it.
1076
1077 If the :meth:`.DefaultRequirements.isolation_level` requirement is
1078 not open, then this method has no return value.
1079
1080 E.g.::
1081
1082 >>> testing.requirements.get_isolation_levels()
1083 {
1084 "default": "READ_COMMITTED",
1085 "supported": [
1086 "SERIALIZABLE", "READ UNCOMMITTED",
1087 "READ COMMITTED", "REPEATABLE READ",
1088 "AUTOCOMMIT"
1089 ]
1090 }
1091 """
1092 with config.db.connect() as conn:
1093 try:
1094 supported = conn.dialect.get_isolation_level_values(
1095 conn.connection.dbapi_connection
1096 )
1097 except NotImplementedError:
1098 return None
1099 else:
1100 return {
1101 "default": conn.dialect.default_isolation_level,
1102 "supported": supported,
1103 }
1104
1105 @property
1106 def get_isolation_level_values(self):
1107 """target dialect supports the
1108 :meth:`_engine.Dialect.get_isolation_level_values`
1109 method added in SQLAlchemy 2.0.
1110
1111 """
1112
1113 def go(config):
1114 with config.db.connect() as conn:
1115 try:
1116 conn.dialect.get_isolation_level_values(
1117 conn.connection.dbapi_connection
1118 )
1119 except NotImplementedError:
1120 return False
1121 else:
1122 return True
1123
1124 return exclusions.only_if(go)
1125
1126 @property
1127 def dialect_level_isolation_level_param(self):
1128 """test that the dialect allows the 'isolation_level' argument
1129 to be handled by DefaultDialect"""
1130
1131 def go(config):
1132 try:
1133 e = create_engine(
1134 config.db.url, isolation_level="READ COMMITTED"
1135 )
1136 except:
1137 return False
1138 else:
1139 return (
1140 e.dialect._on_connect_isolation_level == "READ COMMITTED"
1141 )
1142
1143 return exclusions.only_if(go)
1144
1145 @property
1146 def array_type(self):
1147 """Target platform implements a native ARRAY type"""
1148 return exclusions.closed()
1149
1150 @property
1151 def json_type(self):
1152 """target platform implements a native JSON type."""
1153
1154 return exclusions.closed()
1155
1156 @property
1157 def json_array_indexes(self):
1158 """target platform supports numeric array indexes
1159 within a JSON structure"""
1160
1161 return self.json_type
1162
1163 @property
1164 def json_index_supplementary_unicode_element(self):
1165 return exclusions.open()
1166
1167 @property
1168 def legacy_unconditional_json_extract(self):
1169 """Backend has a JSON_EXTRACT or similar function that returns a
1170 valid JSON string in all cases.
1171
1172 Used to test a legacy feature and is not needed.
1173
1174 """
1175 return exclusions.closed()
1176
1177 @property
1178 def precision_numerics_general(self):
1179 """target backend has general support for moderately high-precision
1180 numerics."""
1181 return exclusions.open()
1182
1183 @property
1184 def precision_numerics_enotation_small(self):
1185 """target backend supports Decimal() objects using E notation
1186 to represent very small values."""
1187 return exclusions.closed()
1188
1189 @property
1190 def precision_numerics_enotation_large(self):
1191 """target backend supports Decimal() objects using E notation
1192 to represent very large values."""
1193 return exclusions.open()
1194
1195 @property
1196 def precision_numerics_many_significant_digits(self):
1197 """target backend supports values with many digits on both sides,
1198 such as 319438950232418390.273596, 87673.594069654243
1199
1200 """
