|
18 | 18 | # However, if you have executed another commercial license agreement
|
19 | 19 | # with Crate these terms will supersede the license and you may use the
|
20 | 20 | # software solely pursuant to the terms of the relevant commercial agreement.
|
| 21 | +from contextlib import contextmanager |
| 22 | +from typing import Any |
| 23 | +from unittest.mock import patch |
| 24 | + |
21 | 25 | import logging
|
22 | 26 |
|
| 27 | +import sqlalchemy as sa |
| 28 | + |
| 29 | +from sqlalchemy_cratedb import SA_VERSION, SA_2_0 |
23 | 30 |
|
24 | 31 | logger = logging.getLogger(__name__)
|
25 | 32 |
|
@@ -60,3 +67,45 @@ def insert_bulk(pd_table, conn, keys, data_iter):
|
60 | 67 | cursor = conn._dbapi_connection.cursor()
|
61 | 68 | cursor.execute(sql=sql, bulk_parameters=data)
|
62 | 69 | cursor.close()
|
| 70 | + |
| 71 | + |
| 72 | +@contextmanager |
| 73 | +def table_kwargs(**kwargs): |
| 74 | + """ |
| 75 | + Context manager for adding SQLAlchemy dialect-specific table options at runtime. |
| 76 | +
|
| 77 | + In certain cases where SQLAlchemy orchestration is implemented within a |
| 78 | + framework, like at this spot [1] in pandas' `SQLTable._create_table_setup`, |
| 79 | + it is not easily possible to forward SQLAlchemy dialect options at table |
| 80 | + creation time. |
| 81 | +
|
| 82 | + In order to augment the SQL DDL statement to make it honor database-specific |
| 83 | + dialect options, the only way to work around the unfortunate situation is by |
| 84 | + monkey-patching the call to `sa.Table()` at runtime, relaying additional |
| 85 | + dialect options through corresponding keyword arguments in their original |
| 86 | + `<dialect>_<kwarg>` format [2]. |
| 87 | +
|
| 88 | + [1] https://github.com/pandas-dev/pandas/blob/v2.2.2/pandas/io/sql.py#L1282-L1285 |
| 89 | + [2] https://docs.sqlalchemy.org/en/20/core/foundation.html#sqlalchemy.sql.base.DialectKWArgs.dialect_kwargs |
| 90 | + """ |
| 91 | + |
| 92 | + if SA_VERSION < SA_2_0: |
| 93 | + _init_dist = sa.sql.schema.Table._init |
| 94 | + |
| 95 | + def _init(self, name, metadata, *args, **kwargs_effective): |
| 96 | + kwargs_effective.update(kwargs) |
| 97 | + return _init_dist(self, name, metadata, *args, **kwargs_effective) |
| 98 | + |
| 99 | + with patch("sqlalchemy.sql.schema.Table._init", _init): |
| 100 | + yield |
| 101 | + |
| 102 | + else: |
| 103 | + new_dist = sa.sql.schema.Table._new |
| 104 | + |
| 105 | + def _new(cls, *args: Any, **kw: Any) -> Any: |
| 106 | + kw.update(kwargs) |
| 107 | + table = new_dist(cls, *args, **kw) |
| 108 | + return table |
| 109 | + |
| 110 | + with patch("sqlalchemy.sql.schema.Table._new", _new): |
| 111 | + yield |
0 commit comments