snowflake-sqlalchemy 1.6.1__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/.pre-commit-config.yaml +1 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/DESCRIPTION.md +13 -2
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/PKG-INFO +4 -4
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/README.md +1 -1
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/pyproject.toml +5 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/__init__.py +52 -6
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/_constants.py +2 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/base.py +58 -9
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/custom_types.py +20 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/exc.py +82 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/parser/custom_type_parser.py +190 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/snowdialect.py +212 -142
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/__init__.py +9 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +37 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +127 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +13 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +117 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +62 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +102 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +33 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +63 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +58 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +63 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +25 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +65 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +14 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +67 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +84 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +94 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +70 -0
- snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +54 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/version.py +1 -1
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_compile_dynamic_table.ambr +13 -0
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_core.ambr +4 -0
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_orm.ambr +4 -0
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_reflect_dynamic_table.ambr +4 -0
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_structured_datatypes.ambr +90 -0
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_unit_structured_types.ambr +4 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/conftest.py +30 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__init__.py +2 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_dynamic_table.ambr +40 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_hybrid_table.ambr +7 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_iceberg_table.ambr +19 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_snowflake_table.ambr +35 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_dynamic_table.ambr +7 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_hybrid_table.ambr +7 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_iceberg_table.ambr +14 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_snowflake_table.ambr +4 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_generic_options.ambr +13 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_reflect_hybrid_table.ambr +4 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_reflect_snowflake_table.ambr +29 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_dynamic_table.py +271 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_hybrid_table.py +52 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_iceberg_table.py +116 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_snowflake_table.py +180 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_dynamic_table.py +124 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_hybrid_table.py +95 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_iceberg_table.py +43 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_snowflake_table.py +66 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_generic_options.py +83 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_dynamic_table.py +88 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_hybrid_table.py +65 -0
- snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_snowflake_table.py +92 -0
- snowflake_sqlalchemy-1.7.0/tests/sqlalchemy_test_suite/__init__.py +3 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_core.py +89 -89
- snowflake_sqlalchemy-1.7.0/tests/test_custom_types.py +67 -0
- snowflake_sqlalchemy-1.7.0/tests/test_index_reflection.py +34 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_orm.py +178 -9
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_pandas.py +1 -1
- snowflake_sqlalchemy-1.7.0/tests/test_structured_datatypes.py +271 -0
- snowflake_sqlalchemy-1.7.0/tests/test_unit_structured_types.py +73 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/util.py +2 -0
- snowflake_sqlalchemy-1.6.1/tests/test_custom_types.py +0 -36
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/.gitignore +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/.gitmodules +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/LICENSE.txt +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/MANIFEST.in +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/build.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/build_docker.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/docker/sqlalchemy_build/Dockerfile +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/docker/sqlalchemy_build/scripts/entrypoint.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/set_base_image.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/test.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/test_docker.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/ci/test_linux.sh +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/license_header.txt +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/setup.cfg +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/snyk/requirements.txt +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/snyk/requiremtnts.txt +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/snyk/update_requirements.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/compat.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/custom_commands.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/functions.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/provision.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/requirements.py +0 -0
- {snowflake_sqlalchemy-1.6.1/tests → snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql}/__init__.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/util.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_310.reqs +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_37.reqs +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_38.reqs +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_39.reqs +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/README.rst +0 -0
- {snowflake_sqlalchemy-1.6.1/tests/sqlalchemy_test_suite → snowflake_sqlalchemy-1.7.0/tests}/__init__.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/data/users.txt +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/README.md +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/conftest.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/test_suite.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/test_suite_20.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_compiler.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_copy.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_create.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_custom_functions.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_geography.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_geometry.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_multivalues_insert.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_qmark.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_quote.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_semi_structured_datatypes.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_sequence.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_timestamp.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_core.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_cte.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_types.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_url.py +0 -0
- {snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/tox.ini +0 -0
|
@@ -9,6 +9,17 @@ Source code is also available at:
|
|
|
9
9
|
|
|
10
10
|
# Release Notes
|
|
11
11
|
|
|
12
|
+
- v1.7.0(November 22, 2024)
|
|
13
|
+
|
|
14
|
+
- Add support for dynamic tables and required options
|
|
15
|
+
- Add support for hybrid tables
|
|
16
|
+
- Fixed SAWarning when registering functions with existing name in default namespace
|
|
17
|
+
- Update options to be defined in key arguments instead of arguments.
|
|
18
|
+
- Add support for refresh_mode option in DynamicTable
|
|
19
|
+
- Add support for iceberg table with Snowflake Catalog
|
|
20
|
+
- Fix cluster by option to support explicit expressions
|
|
21
|
+
- Add support for MAP datatype
|
|
22
|
+
|
|
12
23
|
- v1.6.1(July 9, 2024)
|
|
13
24
|
|
|
14
25
|
- Update internal project workflow with pypi publishing
|
|
@@ -24,7 +35,7 @@ Source code is also available at:
|
|
|
24
35
|
|
|
25
36
|
- v1.5.3(April 16, 2024)
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
- Limit SQLAlchemy to < 2.0.0 before releasing version compatible with 2.0
|
|
28
39
|
|
|
29
40
|
- v1.5.2(April 11, 2024)
|
|
30
41
|
|
|
@@ -33,7 +44,7 @@ Source code is also available at:
|
|
|
33
44
|
|
|
34
45
|
- v1.5.1(November 03, 2023)
|
|
35
46
|
|
|
36
|
-
- Fixed a compatibility issue with Snowflake Behavioral Change 1057 on outer lateral join, for more details check https://docs.snowflake.com/en/release-notes/bcr-bundles/2023_04/bcr-1057
|
|
47
|
+
- Fixed a compatibility issue with Snowflake Behavioral Change 1057 on outer lateral join, for more details check <https://docs.snowflake.com/en/release-notes/bcr-bundles/2023_04/bcr-1057>.
|
|
37
48
|
- Fixed credentials with `externalbrowser` authentication not caching due to incorrect parsing of boolean query parameters.
|
|
38
49
|
- This fixes other boolean parameter passing to driver as well.
|
|
39
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: snowflake-sqlalchemy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: Snowflake SQLAlchemy Dialect
|
|
5
5
|
Project-URL: Changelog, https://github.com/snowflakedb/snowflake-sqlalchemy/blob/main/DESCRIPTION.md
|
|
6
6
|
Project-URL: Documentation, https://docs.snowflake.com/en/user-guide/sqlalchemy.html
|
|
@@ -8,8 +8,7 @@ Project-URL: Homepage, https://www.snowflake.com/
|
|
|
8
8
|
Project-URL: Issues, https://github.com/snowflakedb/snowflake-sqlalchemy/issues
|
|
9
9
|
Project-URL: Source, https://github.com/snowflakedb/snowflake-sqlalchemy
|
|
10
10
|
Author-email: "Snowflake Inc." <triage-snowpark-python-api-dl@snowflake.com>
|
|
11
|
-
License
|
|
12
|
-
License-File: LICENSE.txt
|
|
11
|
+
License: Apache-2.0
|
|
13
12
|
Keywords: Snowflake,analytics,cloud,database,db,warehouse
|
|
14
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
15
14
|
Classifier: Environment :: Console
|
|
@@ -46,6 +45,7 @@ Requires-Dist: pytest-cov; extra == 'development'
|
|
|
46
45
|
Requires-Dist: pytest-rerunfailures; extra == 'development'
|
|
47
46
|
Requires-Dist: pytest-timeout; extra == 'development'
|
|
48
47
|
Requires-Dist: pytz; extra == 'development'
|
|
48
|
+
Requires-Dist: syrupy==4.6.1; extra == 'development'
|
|
49
49
|
Provides-Extra: pandas
|
|
50
50
|
Requires-Dist: snowflake-connector-python[pandas]; extra == 'pandas'
|
|
51
51
|
Description-Content-Type: text/markdown
|
|
@@ -392,7 +392,7 @@ This example shows how to create a table with two columns, `id` and `name`, as t
|
|
|
392
392
|
t = Table('myuser', metadata,
|
|
393
393
|
Column('id', Integer, primary_key=True),
|
|
394
394
|
Column('name', String),
|
|
395
|
-
snowflake_clusterby=['id', 'name'], ...
|
|
395
|
+
snowflake_clusterby=['id', 'name', text('id > 5')], ...
|
|
396
396
|
)
|
|
397
397
|
metadata.create_all(engine)
|
|
398
398
|
```
|
|
@@ -340,7 +340,7 @@ This example shows how to create a table with two columns, `id` and `name`, as t
|
|
|
340
340
|
t = Table('myuser', metadata,
|
|
341
341
|
Column('id', Integer, primary_key=True),
|
|
342
342
|
Column('name', String),
|
|
343
|
-
snowflake_clusterby=['id', 'name'], ...
|
|
343
|
+
snowflake_clusterby=['id', 'name', text('id > 5')], ...
|
|
344
344
|
)
|
|
345
345
|
metadata.create_all(engine)
|
|
346
346
|
```
|
|
@@ -53,6 +53,7 @@ development = [
|
|
|
53
53
|
"pytz",
|
|
54
54
|
"numpy",
|
|
55
55
|
"mock",
|
|
56
|
+
"syrupy==4.6.1",
|
|
56
57
|
]
|
|
57
58
|
pandas = ["snowflake-connector-python[pandas]"]
|
|
58
59
|
|
|
@@ -91,6 +92,7 @@ SQLACHEMY_WARN_20 = "1"
|
|
|
91
92
|
check = "pre-commit run --all-files"
|
|
92
93
|
test-dialect = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml --ignore=tests/sqlalchemy_test_suite tests/"
|
|
93
94
|
test-dialect-compatibility = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml tests/sqlalchemy_test_suite"
|
|
95
|
+
test-dialect-aws = "pytest -m \"aws\" -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml --ignore=tests/sqlalchemy_test_suite tests/"
|
|
94
96
|
gh-cache-sum = "python -VV | sha256sum | cut -d' ' -f1"
|
|
95
97
|
check-import = "python -c 'import snowflake.sqlalchemy; print(snowflake.sqlalchemy.__version__)'"
|
|
96
98
|
|
|
@@ -109,6 +111,7 @@ line-length = 88
|
|
|
109
111
|
line-length = 88
|
|
110
112
|
|
|
111
113
|
[tool.pytest.ini_options]
|
|
114
|
+
addopts = "-m 'not feature_max_lob_size and not aws and not requires_external_volume'"
|
|
112
115
|
markers = [
|
|
113
116
|
# Optional dependency groups markers
|
|
114
117
|
"lambda: AWS lambda tests",
|
|
@@ -125,5 +128,7 @@ markers = [
|
|
|
125
128
|
# Other markers
|
|
126
129
|
"timeout: tests that need a timeout time",
|
|
127
130
|
"internal: tests that could but should only run on our internal CI",
|
|
131
|
+
"requires_external_volume: tests that needs a external volume to be executed",
|
|
128
132
|
"external: tests that could but should only run on our external CI",
|
|
133
|
+
"feature_max_lob_size: tests that could but should only run on our external CI",
|
|
129
134
|
]
|
{snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/__init__.py
RENAMED
|
@@ -9,7 +9,7 @@ if sys.version_info < (3, 8):
|
|
|
9
9
|
else:
|
|
10
10
|
import importlib.metadata as importlib_metadata
|
|
11
11
|
|
|
12
|
-
from sqlalchemy.types import (
|
|
12
|
+
from sqlalchemy.types import ( # noqa
|
|
13
13
|
BIGINT,
|
|
14
14
|
BINARY,
|
|
15
15
|
BOOLEAN,
|
|
@@ -27,8 +27,8 @@ from sqlalchemy.types import (
|
|
|
27
27
|
VARCHAR,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
from . import base, snowdialect
|
|
31
|
-
from .custom_commands import (
|
|
30
|
+
from . import base, snowdialect # noqa
|
|
31
|
+
from .custom_commands import ( # noqa
|
|
32
32
|
AWSBucket,
|
|
33
33
|
AzureContainer,
|
|
34
34
|
CopyFormatter,
|
|
@@ -41,7 +41,7 @@ from .custom_commands import (
|
|
|
41
41
|
MergeInto,
|
|
42
42
|
PARQUETFormatter,
|
|
43
43
|
)
|
|
44
|
-
from .custom_types import (
|
|
44
|
+
from .custom_types import ( # noqa
|
|
45
45
|
ARRAY,
|
|
46
46
|
BYTEINT,
|
|
47
47
|
CHARACTER,
|
|
@@ -50,6 +50,7 @@ from .custom_types import (
|
|
|
50
50
|
FIXED,
|
|
51
51
|
GEOGRAPHY,
|
|
52
52
|
GEOMETRY,
|
|
53
|
+
MAP,
|
|
53
54
|
NUMBER,
|
|
54
55
|
OBJECT,
|
|
55
56
|
STRING,
|
|
@@ -61,13 +62,30 @@ from .custom_types import (
|
|
|
61
62
|
VARBINARY,
|
|
62
63
|
VARIANT,
|
|
63
64
|
)
|
|
64
|
-
from .
|
|
65
|
+
from .sql.custom_schema import ( # noqa
|
|
66
|
+
DynamicTable,
|
|
67
|
+
HybridTable,
|
|
68
|
+
IcebergTable,
|
|
69
|
+
SnowflakeTable,
|
|
70
|
+
)
|
|
71
|
+
from .sql.custom_schema.options import ( # noqa
|
|
72
|
+
AsQueryOption,
|
|
73
|
+
ClusterByOption,
|
|
74
|
+
IdentifierOption,
|
|
75
|
+
KeywordOption,
|
|
76
|
+
LiteralOption,
|
|
77
|
+
SnowflakeKeyword,
|
|
78
|
+
TableOptionKey,
|
|
79
|
+
TargetLagOption,
|
|
80
|
+
TimeUnit,
|
|
81
|
+
)
|
|
82
|
+
from .util import _url as URL # noqa
|
|
65
83
|
|
|
66
84
|
base.dialect = dialect = snowdialect.dialect
|
|
67
85
|
|
|
68
86
|
__version__ = importlib_metadata.version("snowflake-sqlalchemy")
|
|
69
87
|
|
|
70
|
-
|
|
88
|
+
_custom_types = (
|
|
71
89
|
"BIGINT",
|
|
72
90
|
"BINARY",
|
|
73
91
|
"BOOLEAN",
|
|
@@ -102,6 +120,10 @@ __all__ = (
|
|
|
102
120
|
"TINYINT",
|
|
103
121
|
"VARBINARY",
|
|
104
122
|
"VARIANT",
|
|
123
|
+
"MAP",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
_custom_commands = (
|
|
105
127
|
"MergeInto",
|
|
106
128
|
"CSVFormatter",
|
|
107
129
|
"JSONFormatter",
|
|
@@ -114,3 +136,27 @@ __all__ = (
|
|
|
114
136
|
"CreateStage",
|
|
115
137
|
"CreateFileFormat",
|
|
116
138
|
)
|
|
139
|
+
|
|
140
|
+
_custom_tables = ("HybridTable", "DynamicTable", "IcebergTable", "SnowflakeTable")
|
|
141
|
+
|
|
142
|
+
_custom_table_options = (
|
|
143
|
+
"AsQueryOption",
|
|
144
|
+
"TargetLagOption",
|
|
145
|
+
"LiteralOption",
|
|
146
|
+
"IdentifierOption",
|
|
147
|
+
"KeywordOption",
|
|
148
|
+
"ClusterByOption",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
_enums = (
|
|
152
|
+
"TimeUnit",
|
|
153
|
+
"TableOptionKey",
|
|
154
|
+
"SnowflakeKeyword",
|
|
155
|
+
)
|
|
156
|
+
__all__ = (
|
|
157
|
+
*_custom_types,
|
|
158
|
+
*_custom_commands,
|
|
159
|
+
*_custom_tables,
|
|
160
|
+
*_custom_table_options,
|
|
161
|
+
*_enums,
|
|
162
|
+
)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import itertools
|
|
6
6
|
import operator
|
|
7
7
|
import re
|
|
8
|
+
from typing import List
|
|
8
9
|
|
|
9
10
|
from sqlalchemy import exc as sa_exc
|
|
10
11
|
from sqlalchemy import inspect, sql
|
|
@@ -18,9 +19,22 @@ from sqlalchemy.sql.base import CompileState
|
|
|
18
19
|
from sqlalchemy.sql.elements import quoted_name
|
|
19
20
|
from sqlalchemy.sql.selectable import Lateral, SelectState
|
|
20
21
|
|
|
21
|
-
from .
|
|
22
|
-
from .
|
|
22
|
+
from snowflake.sqlalchemy._constants import DIALECT_NAME
|
|
23
|
+
from snowflake.sqlalchemy.compat import IS_VERSION_20, args_reducer, string_types
|
|
24
|
+
from snowflake.sqlalchemy.custom_commands import (
|
|
25
|
+
AWSBucket,
|
|
26
|
+
AzureContainer,
|
|
27
|
+
ExternalStage,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from ._constants import NOT_NULL
|
|
31
|
+
from .exc import (
|
|
32
|
+
CustomOptionsAreOnlySupportedOnSnowflakeTables,
|
|
33
|
+
UnexpectedOptionTypeError,
|
|
34
|
+
)
|
|
23
35
|
from .functions import flatten
|
|
36
|
+
from .sql.custom_schema.custom_table_base import CustomTableBase
|
|
37
|
+
from .sql.custom_schema.options.table_option import TableOption
|
|
24
38
|
from .util import (
|
|
25
39
|
_find_left_clause_to_join_from,
|
|
26
40
|
_set_connection_interpolate_empty_sequences,
|
|
@@ -184,7 +198,6 @@ class SnowflakeSelectState(SelectState):
|
|
|
184
198
|
[element._from_objects for element in statement._where_criteria]
|
|
185
199
|
),
|
|
186
200
|
):
|
|
187
|
-
|
|
188
201
|
potential[from_clause] = ()
|
|
189
202
|
|
|
190
203
|
all_clauses = list(potential.keys())
|
|
@@ -879,7 +892,7 @@ class SnowflakeDDLCompiler(compiler.DDLCompiler):
|
|
|
879
892
|
|
|
880
893
|
return " ".join(colspec)
|
|
881
894
|
|
|
882
|
-
def
|
|
895
|
+
def handle_cluster_by(self, table):
|
|
883
896
|
"""
|
|
884
897
|
Handles snowflake-specific ``CREATE TABLE ... CLUSTER BY`` syntax.
|
|
885
898
|
|
|
@@ -896,7 +909,7 @@ class SnowflakeDDLCompiler(compiler.DDLCompiler):
|
|
|
896
909
|
... metadata,
|
|
897
910
|
... sa.Column('id', sa.Integer, primary_key=True),
|
|
898
911
|
... sa.Column('name', sa.String),
|
|
899
|
-
... snowflake_clusterby=['id', 'name']
|
|
912
|
+
... snowflake_clusterby=['id', 'name', text("id > 5")]
|
|
900
913
|
... )
|
|
901
914
|
>>> print(CreateTable(user).compile(engine))
|
|
902
915
|
<BLANKLINE>
|
|
@@ -904,19 +917,49 @@ class SnowflakeDDLCompiler(compiler.DDLCompiler):
|
|
|
904
917
|
id INTEGER NOT NULL AUTOINCREMENT,
|
|
905
918
|
name VARCHAR,
|
|
906
919
|
PRIMARY KEY (id)
|
|
907
|
-
) CLUSTER BY (id, name)
|
|
920
|
+
) CLUSTER BY (id, name, id > 5)
|
|
908
921
|
<BLANKLINE>
|
|
909
922
|
<BLANKLINE>
|
|
910
923
|
"""
|
|
911
924
|
text = ""
|
|
912
|
-
info = table.dialect_options[
|
|
925
|
+
info = table.dialect_options[DIALECT_NAME]
|
|
913
926
|
cluster = info.get("clusterby")
|
|
914
927
|
if cluster:
|
|
915
928
|
text += " CLUSTER BY ({})".format(
|
|
916
|
-
", ".join(
|
|
929
|
+
", ".join(
|
|
930
|
+
(
|
|
931
|
+
self.denormalize_column_name(key)
|
|
932
|
+
if isinstance(key, str)
|
|
933
|
+
else str(key)
|
|
934
|
+
)
|
|
935
|
+
for key in cluster
|
|
936
|
+
)
|
|
917
937
|
)
|
|
918
938
|
return text
|
|
919
939
|
|
|
940
|
+
def post_create_table(self, table):
|
|
941
|
+
text = self.handle_cluster_by(table)
|
|
942
|
+
options = []
|
|
943
|
+
invalid_options: List[str] = []
|
|
944
|
+
|
|
945
|
+
for key, option in table.dialect_options[DIALECT_NAME].items():
|
|
946
|
+
if isinstance(option, TableOption):
|
|
947
|
+
options.append(option)
|
|
948
|
+
elif key not in ["clusterby", "*"]:
|
|
949
|
+
invalid_options.append(key)
|
|
950
|
+
|
|
951
|
+
if len(invalid_options) > 0:
|
|
952
|
+
raise UnexpectedOptionTypeError(sorted(invalid_options))
|
|
953
|
+
|
|
954
|
+
if isinstance(table, CustomTableBase):
|
|
955
|
+
options.sort(key=lambda x: (x.priority.value, x.option_name), reverse=True)
|
|
956
|
+
for option in options:
|
|
957
|
+
text += "\t" + option.render_option(self)
|
|
958
|
+
elif len(options) > 0:
|
|
959
|
+
raise CustomOptionsAreOnlySupportedOnSnowflakeTables()
|
|
960
|
+
|
|
961
|
+
return text
|
|
962
|
+
|
|
920
963
|
def visit_create_stage(self, create_stage, **kw):
|
|
921
964
|
"""
|
|
922
965
|
This visitor will create the SQL representation for a CREATE STAGE command.
|
|
@@ -1029,6 +1072,12 @@ class SnowflakeTypeCompiler(compiler.GenericTypeCompiler):
|
|
|
1029
1072
|
def visit_VARIANT(self, type_, **kw):
|
|
1030
1073
|
return "VARIANT"
|
|
1031
1074
|
|
|
1075
|
+
def visit_MAP(self, type_, **kw):
|
|
1076
|
+
not_null = f" {NOT_NULL}" if type_.not_null else ""
|
|
1077
|
+
return (
|
|
1078
|
+
f"MAP({type_.key_type.compile()}, {type_.value_type.compile()}{not_null})"
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1032
1081
|
def visit_ARRAY(self, type_, **kw):
|
|
1033
1082
|
return "ARRAY"
|
|
1034
1083
|
|
|
@@ -1065,4 +1114,4 @@ class SnowflakeTypeCompiler(compiler.GenericTypeCompiler):
|
|
|
1065
1114
|
|
|
1066
1115
|
construct_arguments = [(Table, {"clusterby": None})]
|
|
1067
1116
|
|
|
1068
|
-
functions.register_function("flatten", flatten)
|
|
1117
|
+
functions.register_function("flatten", flatten, "snowflake")
|
{snowflake_sqlalchemy-1.6.1 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/custom_types.py
RENAMED
|
@@ -37,6 +37,26 @@ class VARIANT(SnowflakeType):
|
|
|
37
37
|
__visit_name__ = "VARIANT"
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
class StructuredType(SnowflakeType):
|
|
41
|
+
def __init__(self):
|
|
42
|
+
super().__init__()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MAP(StructuredType):
|
|
46
|
+
__visit_name__ = "MAP"
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
key_type: sqltypes.TypeEngine,
|
|
51
|
+
value_type: sqltypes.TypeEngine,
|
|
52
|
+
not_null: bool = False,
|
|
53
|
+
):
|
|
54
|
+
self.key_type = key_type
|
|
55
|
+
self.value_type = value_type
|
|
56
|
+
self.not_null = not_null
|
|
57
|
+
super().__init__()
|
|
58
|
+
|
|
59
|
+
|
|
40
60
|
class OBJECT(SnowflakeType):
|
|
41
61
|
__visit_name__ = "OBJECT"
|
|
42
62
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from sqlalchemy.exc import ArgumentError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NoPrimaryKeyError(ArgumentError):
|
|
10
|
+
def __init__(self, target: str):
|
|
11
|
+
super().__init__(f"Table {target} required primary key.")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UnsupportedPrimaryKeysAndForeignKeysError(ArgumentError):
|
|
15
|
+
def __init__(self, target: str):
|
|
16
|
+
super().__init__(f"Primary key and foreign keys are not supported in {target}.")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RequiredParametersNotProvidedError(ArgumentError):
|
|
20
|
+
def __init__(self, target: str, parameters: List[str]):
|
|
21
|
+
super().__init__(
|
|
22
|
+
f"{target} requires the following parameters: %s." % ", ".join(parameters)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class UnexpectedTableOptionKeyError(ArgumentError):
|
|
27
|
+
def __init__(self, expected: str, actual: str):
|
|
28
|
+
super().__init__(f"Expected table option {expected} but got {actual}.")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OptionKeyNotProvidedError(ArgumentError):
|
|
32
|
+
def __init__(self, target: str):
|
|
33
|
+
super().__init__(
|
|
34
|
+
f"Expected option key in {target} option but got NoneType instead."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UnexpectedOptionParameterTypeError(ArgumentError):
|
|
39
|
+
def __init__(self, parameter_name: str, target: str, types: List[str]):
|
|
40
|
+
super().__init__(
|
|
41
|
+
f"Parameter {parameter_name} of {target} requires to be one"
|
|
42
|
+
f" of following types: {', '.join(types)}."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CustomOptionsAreOnlySupportedOnSnowflakeTables(ArgumentError):
|
|
47
|
+
def __init__(self):
|
|
48
|
+
super().__init__(
|
|
49
|
+
"Identifier, Literal, TargetLag and other custom options are only supported on Snowflake tables."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UnexpectedOptionTypeError(ArgumentError):
|
|
54
|
+
def __init__(self, options: List[str]):
|
|
55
|
+
super().__init__(
|
|
56
|
+
f"The following options are either unsupported or should be defined using a Snowflake table: {', '.join(options)}."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class InvalidTableParameterTypeError(ArgumentError):
|
|
61
|
+
def __init__(self, name: str, input_type: str, expected_types: List[str]):
|
|
62
|
+
expected_types_str = "', '".join(expected_types)
|
|
63
|
+
super().__init__(
|
|
64
|
+
f"Invalid parameter type '{input_type}' provided for '{name}'. "
|
|
65
|
+
f"Expected one of the following types: '{expected_types_str}'.\n"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class MultipleErrors(ArgumentError):
|
|
70
|
+
def __init__(self, errors):
|
|
71
|
+
self.errors = errors
|
|
72
|
+
|
|
73
|
+
def __str__(self):
|
|
74
|
+
return "".join(str(e) for e in self.errors)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class StructuredTypeNotSupportedInTableColumnsError(ArgumentError):
|
|
78
|
+
def __init__(self, table_type: str, table_name: str, column_name: str):
|
|
79
|
+
super().__init__(
|
|
80
|
+
f"Column '{column_name}' is of a structured type, which is only supported on Iceberg tables. "
|
|
81
|
+
f"The table '{table_name}' is of type '{table_type}', not Iceberg."
|
|
82
|
+
)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
|
|
4
|
+
import sqlalchemy.types as sqltypes
|
|
5
|
+
from sqlalchemy.sql.type_api import TypeEngine
|
|
6
|
+
from sqlalchemy.types import (
|
|
7
|
+
BIGINT,
|
|
8
|
+
BINARY,
|
|
9
|
+
BOOLEAN,
|
|
10
|
+
CHAR,
|
|
11
|
+
DATE,
|
|
12
|
+
DATETIME,
|
|
13
|
+
DECIMAL,
|
|
14
|
+
FLOAT,
|
|
15
|
+
INTEGER,
|
|
16
|
+
REAL,
|
|
17
|
+
SMALLINT,
|
|
18
|
+
TIME,
|
|
19
|
+
TIMESTAMP,
|
|
20
|
+
VARCHAR,
|
|
21
|
+
NullType,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from ..custom_types import (
|
|
25
|
+
_CUSTOM_DECIMAL,
|
|
26
|
+
ARRAY,
|
|
27
|
+
DOUBLE,
|
|
28
|
+
GEOGRAPHY,
|
|
29
|
+
GEOMETRY,
|
|
30
|
+
MAP,
|
|
31
|
+
OBJECT,
|
|
32
|
+
TIMESTAMP_LTZ,
|
|
33
|
+
TIMESTAMP_NTZ,
|
|
34
|
+
TIMESTAMP_TZ,
|
|
35
|
+
VARIANT,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
ischema_names = {
|
|
39
|
+
"BIGINT": BIGINT,
|
|
40
|
+
"BINARY": BINARY,
|
|
41
|
+
# 'BIT': BIT,
|
|
42
|
+
"BOOLEAN": BOOLEAN,
|
|
43
|
+
"CHAR": CHAR,
|
|
44
|
+
"CHARACTER": CHAR,
|
|
45
|
+
"DATE": DATE,
|
|
46
|
+
"DATETIME": DATETIME,
|
|
47
|
+
"DEC": DECIMAL,
|
|
48
|
+
"DECIMAL": DECIMAL,
|
|
49
|
+
"DOUBLE": DOUBLE,
|
|
50
|
+
"FIXED": DECIMAL,
|
|
51
|
+
"FLOAT": FLOAT, # Snowflake FLOAT datatype doesn't has parameters
|
|
52
|
+
"INT": INTEGER,
|
|
53
|
+
"INTEGER": INTEGER,
|
|
54
|
+
"NUMBER": _CUSTOM_DECIMAL,
|
|
55
|
+
# 'OBJECT': ?
|
|
56
|
+
"REAL": REAL,
|
|
57
|
+
"BYTEINT": SMALLINT,
|
|
58
|
+
"SMALLINT": SMALLINT,
|
|
59
|
+
"STRING": VARCHAR,
|
|
60
|
+
"TEXT": VARCHAR,
|
|
61
|
+
"TIME": TIME,
|
|
62
|
+
"TIMESTAMP": TIMESTAMP,
|
|
63
|
+
"TIMESTAMP_TZ": TIMESTAMP_TZ,
|
|
64
|
+
"TIMESTAMP_LTZ": TIMESTAMP_LTZ,
|
|
65
|
+
"TIMESTAMP_NTZ": TIMESTAMP_NTZ,
|
|
66
|
+
"TINYINT": SMALLINT,
|
|
67
|
+
"VARBINARY": BINARY,
|
|
68
|
+
"VARCHAR": VARCHAR,
|
|
69
|
+
"VARIANT": VARIANT,
|
|
70
|
+
"MAP": MAP,
|
|
71
|
+
"OBJECT": OBJECT,
|
|
72
|
+
"ARRAY": ARRAY,
|
|
73
|
+
"GEOGRAPHY": GEOGRAPHY,
|
|
74
|
+
"GEOMETRY": GEOMETRY,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def extract_parameters(text: str) -> list:
|
|
79
|
+
"""
|
|
80
|
+
Extracts parameters from a comma-separated string, handling parentheses.
|
|
81
|
+
|
|
82
|
+
:param text: A string with comma-separated parameters, which may include parentheses.
|
|
83
|
+
|
|
84
|
+
:return: A list of parameters as strings.
|
|
85
|
+
|
|
86
|
+
:example:
|
|
87
|
+
For input `"a, (b, c), d"`, the output is `['a', '(b, c)', 'd']`.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
output_parameters = []
|
|
91
|
+
parameter = ""
|
|
92
|
+
open_parenthesis = 0
|
|
93
|
+
for c in text:
|
|
94
|
+
|
|
95
|
+
if c == "(":
|
|
96
|
+
open_parenthesis += 1
|
|
97
|
+
elif c == ")":
|
|
98
|
+
open_parenthesis -= 1
|
|
99
|
+
|
|
100
|
+
if open_parenthesis > 0 or c != ",":
|
|
101
|
+
parameter += c
|
|
102
|
+
elif c == ",":
|
|
103
|
+
output_parameters.append(parameter.strip(" "))
|
|
104
|
+
parameter = ""
|
|
105
|
+
if parameter != "":
|
|
106
|
+
output_parameters.append(parameter.strip(" "))
|
|
107
|
+
return output_parameters
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def parse_type(type_text: str) -> TypeEngine:
|
|
111
|
+
"""
|
|
112
|
+
Parses a type definition string and returns the corresponding SQLAlchemy type.
|
|
113
|
+
|
|
114
|
+
The function handles types with or without parameters, such as `VARCHAR(255)` or `INTEGER`.
|
|
115
|
+
|
|
116
|
+
:param type_text: A string representing a SQLAlchemy type, which may include parameters
|
|
117
|
+
in parentheses (e.g., "VARCHAR(255)" or "DECIMAL(10, 2)").
|
|
118
|
+
:return: An instance of the corresponding SQLAlchemy type class (e.g., `String`, `Integer`),
|
|
119
|
+
or `NullType` if the type is not recognized.
|
|
120
|
+
|
|
121
|
+
:example:
|
|
122
|
+
parse_type("VARCHAR(255)")
|
|
123
|
+
String(length=255)
|
|
124
|
+
"""
|
|
125
|
+
index = type_text.find("(")
|
|
126
|
+
type_name = type_text[:index] if index != -1 else type_text
|
|
127
|
+
parameters = (
|
|
128
|
+
extract_parameters(type_text[index + 1 : -1]) if type_name != type_text else []
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
col_type_class = ischema_names.get(type_name, None)
|
|
132
|
+
col_type_kw = {}
|
|
133
|
+
if col_type_class is None:
|
|
134
|
+
col_type_class = NullType
|
|
135
|
+
else:
|
|
136
|
+
if issubclass(col_type_class, sqltypes.Numeric):
|
|
137
|
+
col_type_kw = __parse_numeric_type_parameters(parameters)
|
|
138
|
+
elif issubclass(col_type_class, (sqltypes.String, sqltypes.BINARY)):
|
|
139
|
+
col_type_kw = __parse_type_with_length_parameters(parameters)
|
|
140
|
+
elif issubclass(col_type_class, MAP):
|
|
141
|
+
col_type_kw = __parse_map_type_parameters(parameters)
|
|
142
|
+
if col_type_kw is None:
|
|
143
|
+
col_type_class = NullType
|
|
144
|
+
col_type_kw = {}
|
|
145
|
+
|
|
146
|
+
return col_type_class(**col_type_kw)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def __parse_map_type_parameters(parameters):
|
|
150
|
+
if len(parameters) != 2:
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
key_type_str = parameters[0]
|
|
154
|
+
value_type_str = parameters[1]
|
|
155
|
+
not_null_str = "NOT NULL"
|
|
156
|
+
not_null = False
|
|
157
|
+
if (
|
|
158
|
+
len(value_type_str) >= len(not_null_str)
|
|
159
|
+
and value_type_str[-len(not_null_str) :] == not_null_str
|
|
160
|
+
):
|
|
161
|
+
not_null = True
|
|
162
|
+
value_type_str = value_type_str[: -len(not_null_str) - 1]
|
|
163
|
+
|
|
164
|
+
key_type: TypeEngine = parse_type(key_type_str)
|
|
165
|
+
value_type: TypeEngine = parse_type(value_type_str)
|
|
166
|
+
if isinstance(key_type, NullType) or isinstance(value_type, NullType):
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
"key_type": key_type,
|
|
171
|
+
"value_type": value_type,
|
|
172
|
+
"not_null": not_null,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def __parse_type_with_length_parameters(parameters):
|
|
177
|
+
return (
|
|
178
|
+
{"length": int(parameters[0])}
|
|
179
|
+
if len(parameters) == 1 and str.isdigit(parameters[0])
|
|
180
|
+
else {}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def __parse_numeric_type_parameters(parameters):
|
|
185
|
+
result = {}
|
|
186
|
+
if len(parameters) >= 1 and str.isdigit(parameters[0]):
|
|
187
|
+
result["precision"] = int(parameters[0])
|
|
188
|
+
if len(parameters) == 2 and str.isdigit(parameters[1]):
|
|
189
|
+
result["scale"] = int(parameters[1])
|
|
190
|
+
return result
|