|
35 | 35 | from sqlalchemy.engine import create_engine |
36 | 36 | from sqlalchemy.exc import NoSuchTableError, OperationalError |
37 | 37 | from sqlalchemy.ext import compiler |
38 | | -from sqlalchemy.orm import declarative_base |
| 38 | +from sqlalchemy.orm import declarative_base, Session, sessionmaker |
39 | 39 | from sqlalchemy.schema import ( |
40 | 40 | Column, |
41 | 41 | CreateTable, |
| 42 | + DDL, |
42 | 43 | DDLElement, |
43 | 44 | Index, |
44 | 45 | MetaData, |
|
59 | 60 | Time, |
60 | 61 | ) |
61 | 62 |
|
| 63 | +from cardinal_pythonlib.sqlalchemy.engine_func import ( |
| 64 | + get_dialect_name, |
| 65 | + SqlaDialectName, |
| 66 | +) |
62 | 67 | from cardinal_pythonlib.sqlalchemy.schema import ( |
63 | 68 | add_index, |
64 | 69 | column_creation_ddl, |
|
98 | 103 | view_exists, |
99 | 104 | ) |
100 | 105 | from cardinal_pythonlib.sqlalchemy.session import SQLITE_MEMORY_URL |
| 106 | +from cardinal_pythonlib.sqlalchemy.sqlserver import ( |
| 107 | + if_sqlserver_disable_constraints, |
| 108 | +) |
101 | 109 |
|
102 | 110 | Base = declarative_base() |
103 | 111 | log = logging.getLogger(__name__) |
@@ -485,7 +493,7 @@ def test_mssql_transaction_count(self) -> None: |
485 | 493 |
|
486 | 494 |
|
487 | 495 | class YetMoreSchemaTests(unittest.TestCase): |
488 | | - def __init__(self, *args, echo: bool = False, **kwargs) -> None: |
| 496 | + def __init__(self, *args, echo: bool = True, **kwargs) -> None: |
489 | 497 | self.echo = echo |
490 | 498 | super().__init__(*args, **kwargs) |
491 | 499 |
|
@@ -631,6 +639,69 @@ def test_execute_ddl(self) -> None: |
631 | 639 | with self.assertRaises(AssertionError): |
632 | 640 | execute_ddl(self.engine) # neither |
633 | 641 |
|
| 642 | + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 643 | + # Dialect conditionality for DDL |
| 644 | + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 645 | + @staticmethod |
| 646 | + def _present_in_log_output(_cm, msg: str) -> bool: |
| 647 | + """ |
| 648 | + Detects whether a string is present, INCLUDING AS A SUBSTRING, in |
| 649 | + log output captured from an assertLogs() context manager. |
| 650 | + """ |
| 651 | + return any(msg in line for line in _cm.output) |
| 652 | + |
| 653 | + def test_ddl_dialect_conditionality_1(self) -> None: |
| 654 | + self.engine.echo = True # will write to log at INFO level |
| 655 | + |
| 656 | + # 1. Check that logging capture works, and our _present_in_log_output |
| 657 | + # function. |
| 658 | + with self.assertLogs(level=logging.INFO) as cm: |
| 659 | + log.info("dummy call") |
| 660 | + self.assertTrue(self._present_in_log_output(cm, "dummy")) |
| 661 | + |
| 662 | + # 2. Check our dialect is as expected: SQLite. |
| 663 | + dialect_name = get_dialect_name(self.engine) |
| 664 | + self.assertEqual(dialect_name, SqlaDialectName.SQLITE) |
| 665 | + |
| 666 | + # 3. Seeing if DDL built with execute_if() will execute "directly" when |
| 667 | + # set to execute-if-SQLite. It executes - but not conditionally! |
| 668 | + ddl_yes = DDL("CREATE TABLE yesplease (a INT)").execute_if( |
| 669 | + dialect=SqlaDialectName.SQLITE |
| 670 | + ) |
| 671 | + with self.assertLogs(level=logging.INFO) as cm: |
| 672 | + execute_ddl(self.engine, ddl=ddl_yes) |
| 673 | + self.assertTrue( |
| 674 | + self._present_in_log_output(cm, "CREATE TABLE yesplease") |
| 675 | + ) |
| 676 | + |
| 677 | + # 4. Seeing if DDL built with execute_if() will execute "directly" when |
| 678 | + # set to execute-if-MySQL. It executes - therefore not conditionally! |
| 679 | + # I'd misunderstood this: it is NOT conditionally executed. |
| 680 | + ddl_no = DDL("CREATE TABLE nothanks (a INT)").execute_if( |
| 681 | + dialect=SqlaDialectName.MYSQL |
| 682 | + ) |
| 683 | + with self.assertLogs(level=logging.INFO) as cm: |
| 684 | + execute_ddl(self.engine, ddl=ddl_no) |
| 685 | + self.assertTrue( |
| 686 | + self._present_in_log_output(cm, "CREATE TABLE nothanks") |
| 687 | + ) |
| 688 | + # I'd thought this would be false, but it is true. |
| 689 | + |
| 690 | + def test_ddl_dialect_conditionality_2(self) -> None: |
| 691 | + # Therefore: |
| 692 | + self.engine.echo = True # will write to log at INFO level |
| 693 | + # The test above (test_ddl_dialect_conditionality_1) proves that |
| 694 | + # this code will log something if SQL is emitted. |
| 695 | + |
| 696 | + session = sessionmaker( |
| 697 | + bind=self.engine, future=True |
| 698 | + )() # type: Session |
| 699 | + |
| 700 | + with self.assertNoLogs(level=logging.INFO): |
| 701 | + with if_sqlserver_disable_constraints(session, tablename="person"): |
| 702 | + pass |
| 703 | + # Should do nothing, therefore emit no logs. |
| 704 | + |
634 | 705 |
|
635 | 706 | class SchemaAbstractTests(unittest.TestCase): |
636 | 707 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
0 commit comments