snowflake-sqlalchemy 1.7.0__tar.gz → 1.7.2__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.7.0 → snowflake_sqlalchemy-1.7.2}/DESCRIPTION.md +13 -2
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/PKG-INFO +229 -6
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/README.md +224 -3
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/pyproject.toml +3 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/base.py +40 -9
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/custom_commands.py +7 -2
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/custom_types.py +33 -3
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/parser/custom_type_parser.py +78 -23
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/snowdialect.py +136 -105
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/version.py +1 -1
- snowflake_sqlalchemy-1.7.2/tests/__snapshots__/test_structured_datatypes.ambr +246 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/conftest.py +27 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_compiler.py +16 -1
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_copy.py +48 -17
- snowflake_sqlalchemy-1.7.2/tests/test_imports.py +64 -0
- snowflake_sqlalchemy-1.7.2/tests/test_index_reflection.py +68 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_quote.py +23 -0
- snowflake_sqlalchemy-1.7.2/tests/test_quote_identifiers.py +43 -0
- snowflake_sqlalchemy-1.7.2/tests/test_structured_datatypes.py +582 -0
- snowflake_sqlalchemy-1.7.2/tests/test_transactions.py +157 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_structured_types.py +10 -2
- snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_structured_datatypes.ambr +0 -90
- snowflake_sqlalchemy-1.7.0/tests/test_index_reflection.py +0 -34
- snowflake_sqlalchemy-1.7.0/tests/test_structured_datatypes.py +0 -271
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/.gitignore +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/.gitmodules +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/.pre-commit-config.yaml +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/LICENSE.txt +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/MANIFEST.in +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/build.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/build_docker.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/docker/sqlalchemy_build/Dockerfile +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/docker/sqlalchemy_build/scripts/entrypoint.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/set_base_image.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/test.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/test_docker.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/ci/test_linux.sh +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/license_header.txt +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/setup.cfg +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/snyk/requirements.txt +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/snyk/requiremtnts.txt +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/snyk/update_requirements.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/_constants.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/compat.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/exc.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/functions.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/provision.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/requirements.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/util.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_310.reqs +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_37.reqs +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_38.reqs +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_39.reqs +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/README.rst +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_compile_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_core.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_orm.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_reflect_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_unit_structured_types.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_iceberg_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_iceberg_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_generic_options.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_reflect_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_reflect_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_generic_options.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/data/users.txt +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/README.md +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/conftest.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/test_suite.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/test_suite_20.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_core.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_create.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_custom_functions.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_custom_types.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_geography.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_geometry.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_multivalues_insert.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_orm.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_pandas.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_qmark.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_semi_structured_datatypes.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_sequence.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_timestamp.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_core.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_cte.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_types.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_url.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tests/util.py +0 -0
- {snowflake_sqlalchemy-1.7.0 → snowflake_sqlalchemy-1.7.2}/tox.ini +0 -0
|
@@ -9,8 +9,19 @@ Source code is also available at:
|
|
|
9
9
|
|
|
10
10
|
# Release Notes
|
|
11
11
|
|
|
12
|
-
- v1.7.
|
|
13
|
-
|
|
12
|
+
- v1.7.2(December 18, 2024)
|
|
13
|
+
- Fix quoting of `_` as column name
|
|
14
|
+
- Fix index columns was not being reflected
|
|
15
|
+
- Fix index reflection cache not working
|
|
16
|
+
- Add support for structured OBJECT datatype
|
|
17
|
+
- Add support for structured ARRAY datatype
|
|
18
|
+
|
|
19
|
+
- v1.7.1(December 02, 2024)
|
|
20
|
+
- Add support for partition by to copy into <location>
|
|
21
|
+
- Fix BOOLEAN type not found in snowdialect
|
|
22
|
+
- Add support for autocommit Isolation Level
|
|
23
|
+
|
|
24
|
+
- v1.7.0(November 21, 2024)
|
|
14
25
|
- Add support for dynamic tables and required options
|
|
15
26
|
- Add support for hybrid tables
|
|
16
27
|
- Fixed SAWarning when registering functions with existing name in default namespace
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: snowflake-sqlalchemy
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.2
|
|
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,7 +8,8 @@ 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: Apache-2.0
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
License-File: LICENSE.txt
|
|
12
13
|
Keywords: Snowflake,analytics,cloud,database,db,warehouse
|
|
13
14
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
15
|
Classifier: Environment :: Console
|
|
@@ -45,6 +46,7 @@ Requires-Dist: pytest-cov; extra == 'development'
|
|
|
45
46
|
Requires-Dist: pytest-rerunfailures; extra == 'development'
|
|
46
47
|
Requires-Dist: pytest-timeout; extra == 'development'
|
|
47
48
|
Requires-Dist: pytz; extra == 'development'
|
|
49
|
+
Requires-Dist: setuptools; extra == 'development'
|
|
48
50
|
Requires-Dist: syrupy==4.6.1; extra == 'development'
|
|
49
51
|
Provides-Extra: pandas
|
|
50
52
|
Requires-Dist: snowflake-connector-python[pandas]; extra == 'pandas'
|
|
@@ -60,6 +62,11 @@ Description-Content-Type: text/markdown
|
|
|
60
62
|
|
|
61
63
|
Snowflake SQLAlchemy runs on the top of the Snowflake Connector for Python as a [dialect](http://docs.sqlalchemy.org/en/latest/dialects/) to bridge a Snowflake database and SQLAlchemy applications.
|
|
62
64
|
|
|
65
|
+
|
|
66
|
+
| :exclamation: | For production-affecting or urgent issues related to the connector, please [create a case with Snowflake Support](https://community.snowflake.com/s/article/How-To-Submit-a-Support-Case-in-Snowflake-Lodge). |
|
|
67
|
+
|---------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
68
|
+
|
|
69
|
+
|
|
63
70
|
## Prerequisites
|
|
64
71
|
|
|
65
72
|
### Snowflake Connector for Python
|
|
@@ -256,7 +263,7 @@ finally:
|
|
|
256
263
|
|
|
257
264
|
# Best
|
|
258
265
|
try:
|
|
259
|
-
with engine.
|
|
266
|
+
with engine.connect() as connection:
|
|
260
267
|
connection.execute(text(<SQL>))
|
|
261
268
|
# or
|
|
262
269
|
connection.exec_driver_sql(<SQL>)
|
|
@@ -277,11 +284,43 @@ t = Table('mytable', metadata,
|
|
|
277
284
|
|
|
278
285
|
### Object Name Case Handling
|
|
279
286
|
|
|
280
|
-
Snowflake stores all case-insensitive object names in uppercase text. In contrast, SQLAlchemy considers all lowercase object names to be case-insensitive. Snowflake SQLAlchemy converts the object name case during schema-level communication, i.e. during table and index reflection. If you use uppercase object names, SQLAlchemy assumes they are case-sensitive and encloses the names with quotes. This behavior will cause mismatches
|
|
287
|
+
Snowflake stores all case-insensitive object names in uppercase text. In contrast, SQLAlchemy considers all lowercase object names to be case-insensitive. Snowflake SQLAlchemy converts the object name case during schema-level communication, i.e. during table and index reflection. If you use uppercase object names, SQLAlchemy assumes they are case-sensitive and encloses the names with quotes. This behavior will cause mismatches against data dictionary data received from Snowflake, so unless identifier names have been truly created as case sensitive using quotes, e.g., `"TestDb"`, all lowercase names should be used on the SQLAlchemy side.
|
|
281
288
|
|
|
282
289
|
### Index Support
|
|
283
290
|
|
|
284
|
-
|
|
291
|
+
Indexes are supported only for Hybrid Tables in Snowflake SQLAlchemy. For more details on limitations and use cases, refer to the [Create Index documentation](https://docs.snowflake.com/en/sql-reference/constraints-indexes.html). You can create an index using the following methods:
|
|
292
|
+
|
|
293
|
+
#### Single Column Index
|
|
294
|
+
|
|
295
|
+
You can create a single column index by setting the `index=True` parameter on the column or by explicitly defining an `Index` object.
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
hybrid_test_table_1 = HybridTable(
|
|
299
|
+
"table_name",
|
|
300
|
+
metadata,
|
|
301
|
+
Column("column1", Integer, primary_key=True),
|
|
302
|
+
Column("column2", String, index=True),
|
|
303
|
+
Index("index_1", "column1", "column2")
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
metadata.create_all(engine_testaccount)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### Multi-Column Index
|
|
310
|
+
|
|
311
|
+
For multi-column indexes, you define the `Index` object specifying the columns that should be indexed.
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
hybrid_test_table_1 = HybridTable(
|
|
315
|
+
"table_name",
|
|
316
|
+
metadata,
|
|
317
|
+
Column("column1", Integer, primary_key=True),
|
|
318
|
+
Column("column2", String),
|
|
319
|
+
Index("index_1", "column1", "column2")
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
metadata.create_all(engine_testaccount)
|
|
323
|
+
```
|
|
285
324
|
|
|
286
325
|
### Numpy Data Type Support
|
|
287
326
|
|
|
@@ -382,6 +421,79 @@ data_object = json.loads(row[1])
|
|
|
382
421
|
data_array = json.loads(row[2])
|
|
383
422
|
```
|
|
384
423
|
|
|
424
|
+
### Structured Data Types Support
|
|
425
|
+
|
|
426
|
+
This module defines custom SQLAlchemy types for Snowflake structured data, specifically for **Iceberg tables**.
|
|
427
|
+
The types —**MAP**, **OBJECT**, and **ARRAY**— allow you to store complex data structures in your SQLAlchemy models.
|
|
428
|
+
For detailed information, refer to the Snowflake [Structured data types](https://docs.snowflake.com/en/sql-reference/data-types-structured) documentation.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
#### MAP
|
|
433
|
+
|
|
434
|
+
The `MAP` type represents a collection of key-value pairs, where each key and value can have different types.
|
|
435
|
+
|
|
436
|
+
- **Key Type**: The type of the keys (e.g., `TEXT`, `NUMBER`).
|
|
437
|
+
- **Value Type**: The type of the values (e.g., `TEXT`, `NUMBER`).
|
|
438
|
+
- **Not Null**: Whether `NULL` values are allowed (default is `False`).
|
|
439
|
+
|
|
440
|
+
*Example Usage*
|
|
441
|
+
|
|
442
|
+
```python
|
|
443
|
+
IcebergTable(
|
|
444
|
+
table_name,
|
|
445
|
+
metadata,
|
|
446
|
+
Column("id", Integer, primary_key=True),
|
|
447
|
+
Column("map_col", MAP(NUMBER(10, 0), TEXT(16777216))),
|
|
448
|
+
external_volume="external_volume",
|
|
449
|
+
base_location="base_location",
|
|
450
|
+
)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### OBJECT
|
|
454
|
+
|
|
455
|
+
The `OBJECT` type represents a semi-structured object with named fields. Each field can have a specific type, and you can also specify whether each field is nullable.
|
|
456
|
+
|
|
457
|
+
- **Items Types**: A dictionary of field names and their types. The type can optionally include a nullable flag (`True` for not nullable, `False` for nullable, default is `False`).
|
|
458
|
+
|
|
459
|
+
*Example Usage*
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
IcebergTable(
|
|
463
|
+
table_name,
|
|
464
|
+
metadata,
|
|
465
|
+
Column("id", Integer, primary_key=True),
|
|
466
|
+
Column(
|
|
467
|
+
"object_col",
|
|
468
|
+
OBJECT(key1=(TEXT(16777216), False), key2=(NUMBER(10, 0), False)),
|
|
469
|
+
OBJECT(key1=TEXT(16777216), key2=NUMBER(10, 0)), # Without nullable flag
|
|
470
|
+
),
|
|
471
|
+
external_volume="external_volume",
|
|
472
|
+
base_location="base_location",
|
|
473
|
+
)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### ARRAY
|
|
477
|
+
|
|
478
|
+
The `ARRAY` type represents an ordered list of values, where each element has the same type. The type of the elements is defined when creating the array.
|
|
479
|
+
|
|
480
|
+
- **Value Type**: The type of the elements in the array (e.g., `TEXT`, `NUMBER`).
|
|
481
|
+
- **Not Null**: Whether `NULL` values are allowed (default is `False`).
|
|
482
|
+
|
|
483
|
+
*Example Usage*
|
|
484
|
+
|
|
485
|
+
```python
|
|
486
|
+
IcebergTable(
|
|
487
|
+
table_name,
|
|
488
|
+
metadata,
|
|
489
|
+
Column("id", Integer, primary_key=True),
|
|
490
|
+
Column("array_col", ARRAY(TEXT(16777216))),
|
|
491
|
+
external_volume="external_volume",
|
|
492
|
+
base_location="base_location",
|
|
493
|
+
)
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
|
|
385
497
|
### CLUSTER BY Support
|
|
386
498
|
|
|
387
499
|
Snowflake SQLAchemy supports the `CLUSTER BY` parameter for tables. For information about the parameter, see :doc:`/sql-reference/sql/create-table`.
|
|
@@ -508,6 +620,117 @@ copy_into = CopyIntoStorage(from_=users,
|
|
|
508
620
|
connection.execute(copy_into)
|
|
509
621
|
```
|
|
510
622
|
|
|
623
|
+
### Iceberg Table with Snowflake Catalog support
|
|
624
|
+
|
|
625
|
+
Snowflake SQLAlchemy supports Iceberg Tables with the Snowflake Catalog, along with various related parameters. For detailed information about Iceberg Tables, refer to the Snowflake [CREATE ICEBERG](https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table-snowflake) documentation.
|
|
626
|
+
|
|
627
|
+
To create an Iceberg Table using Snowflake SQLAlchemy, you can define the table using the SQLAlchemy Core syntax as follows:
|
|
628
|
+
|
|
629
|
+
```python
|
|
630
|
+
table = IcebergTable(
|
|
631
|
+
"myuser",
|
|
632
|
+
metadata,
|
|
633
|
+
Column("id", Integer, primary_key=True),
|
|
634
|
+
Column("name", String),
|
|
635
|
+
external_volume=external_volume_name,
|
|
636
|
+
base_location="my_iceberg_table",
|
|
637
|
+
as_query="SELECT * FROM table"
|
|
638
|
+
)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
Alternatively, you can define the table using a declarative approach:
|
|
642
|
+
|
|
643
|
+
```python
|
|
644
|
+
class MyUser(Base):
|
|
645
|
+
__tablename__ = "myuser"
|
|
646
|
+
|
|
647
|
+
@classmethod
|
|
648
|
+
def __table_cls__(cls, name, metadata, *arg, **kw):
|
|
649
|
+
return IcebergTable(name, metadata, *arg, **kw)
|
|
650
|
+
|
|
651
|
+
__table_args__ = {
|
|
652
|
+
"external_volume": "my_external_volume",
|
|
653
|
+
"base_location": "my_iceberg_table",
|
|
654
|
+
"as_query": "SELECT * FROM table",
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
id = Column(Integer, primary_key=True)
|
|
658
|
+
name = Column(String)
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Hybrid Table support
|
|
662
|
+
|
|
663
|
+
Snowflake SQLAlchemy supports Hybrid Tables with indexes. For detailed information, refer to the Snowflake [CREATE HYBRID TABLE](https://docs.snowflake.com/en/sql-reference/sql/create-hybrid-table) documentation.
|
|
664
|
+
|
|
665
|
+
To create a Hybrid Table and add an index, you can use the SQLAlchemy Core syntax as follows:
|
|
666
|
+
|
|
667
|
+
```python
|
|
668
|
+
table = HybridTable(
|
|
669
|
+
"myuser",
|
|
670
|
+
metadata,
|
|
671
|
+
Column("id", Integer, primary_key=True),
|
|
672
|
+
Column("name", String),
|
|
673
|
+
Index("idx_name", "name")
|
|
674
|
+
)
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Alternatively, you can define the table using the declarative approach:
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
class MyUser(Base):
|
|
681
|
+
__tablename__ = "myuser"
|
|
682
|
+
|
|
683
|
+
@classmethod
|
|
684
|
+
def __table_cls__(cls, name, metadata, *arg, **kw):
|
|
685
|
+
return HybridTable(name, metadata, *arg, **kw)
|
|
686
|
+
|
|
687
|
+
__table_args__ = (
|
|
688
|
+
Index("idx_name", "name"),
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
id = Column(Integer, primary_key=True)
|
|
692
|
+
name = Column(String)
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Dynamic Tables support
|
|
696
|
+
|
|
697
|
+
Snowflake SQLAlchemy supports Dynamic Tables. For detailed information, refer to the Snowflake [CREATE DYNAMIC TABLE](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table) documentation.
|
|
698
|
+
|
|
699
|
+
To create a Dynamic Table, you can use the SQLAlchemy Core syntax as follows:
|
|
700
|
+
|
|
701
|
+
```python
|
|
702
|
+
dynamic_test_table_1 = DynamicTable(
|
|
703
|
+
"dynamic_MyUser",
|
|
704
|
+
metadata,
|
|
705
|
+
Column("id", Integer),
|
|
706
|
+
Column("name", String),
|
|
707
|
+
target_lag=(1, TimeUnit.HOURS), # Additionally, you can use SnowflakeKeyword.DOWNSTREAM
|
|
708
|
+
warehouse='test_wh',
|
|
709
|
+
refresh_mode=SnowflakeKeyword.FULL,
|
|
710
|
+
as_query="SELECT id, name from MyUser;"
|
|
711
|
+
)
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Alternatively, you can define a table without columns using the SQLAlchemy `select()` construct:
|
|
715
|
+
|
|
716
|
+
```python
|
|
717
|
+
dynamic_test_table_1 = DynamicTable(
|
|
718
|
+
"dynamic_MyUser",
|
|
719
|
+
metadata,
|
|
720
|
+
target_lag=(1, TimeUnit.HOURS),
|
|
721
|
+
warehouse='test_wh',
|
|
722
|
+
refresh_mode=SnowflakeKeyword.FULL,
|
|
723
|
+
as_query=select(MyUser.id, MyUser.name)
|
|
724
|
+
)
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Notes
|
|
728
|
+
|
|
729
|
+
- Defining a primary key in a Dynamic Table is not supported, meaning declarative tables don’t support Dynamic Tables.
|
|
730
|
+
- When using the `as_query` parameter with a string, you must explicitly define the columns. However, if you use the SQLAlchemy `select()` construct, you don’t need to explicitly define the columns.
|
|
731
|
+
- Direct data insertion into Dynamic Tables is not supported.
|
|
732
|
+
|
|
733
|
+
|
|
511
734
|
## Support
|
|
512
735
|
|
|
513
736
|
Feel free to file an issue or submit a PR here for general cases. For official support, contact Snowflake support at:
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
Snowflake SQLAlchemy runs on the top of the Snowflake Connector for Python as a [dialect](http://docs.sqlalchemy.org/en/latest/dialects/) to bridge a Snowflake database and SQLAlchemy applications.
|
|
10
10
|
|
|
11
|
+
|
|
12
|
+
| :exclamation: | For production-affecting or urgent issues related to the connector, please [create a case with Snowflake Support](https://community.snowflake.com/s/article/How-To-Submit-a-Support-Case-in-Snowflake-Lodge). |
|
|
13
|
+
|---------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
## Prerequisites
|
|
12
17
|
|
|
13
18
|
### Snowflake Connector for Python
|
|
@@ -204,7 +209,7 @@ finally:
|
|
|
204
209
|
|
|
205
210
|
# Best
|
|
206
211
|
try:
|
|
207
|
-
with engine.
|
|
212
|
+
with engine.connect() as connection:
|
|
208
213
|
connection.execute(text(<SQL>))
|
|
209
214
|
# or
|
|
210
215
|
connection.exec_driver_sql(<SQL>)
|
|
@@ -225,11 +230,43 @@ t = Table('mytable', metadata,
|
|
|
225
230
|
|
|
226
231
|
### Object Name Case Handling
|
|
227
232
|
|
|
228
|
-
Snowflake stores all case-insensitive object names in uppercase text. In contrast, SQLAlchemy considers all lowercase object names to be case-insensitive. Snowflake SQLAlchemy converts the object name case during schema-level communication, i.e. during table and index reflection. If you use uppercase object names, SQLAlchemy assumes they are case-sensitive and encloses the names with quotes. This behavior will cause mismatches
|
|
233
|
+
Snowflake stores all case-insensitive object names in uppercase text. In contrast, SQLAlchemy considers all lowercase object names to be case-insensitive. Snowflake SQLAlchemy converts the object name case during schema-level communication, i.e. during table and index reflection. If you use uppercase object names, SQLAlchemy assumes they are case-sensitive and encloses the names with quotes. This behavior will cause mismatches against data dictionary data received from Snowflake, so unless identifier names have been truly created as case sensitive using quotes, e.g., `"TestDb"`, all lowercase names should be used on the SQLAlchemy side.
|
|
229
234
|
|
|
230
235
|
### Index Support
|
|
231
236
|
|
|
232
|
-
|
|
237
|
+
Indexes are supported only for Hybrid Tables in Snowflake SQLAlchemy. For more details on limitations and use cases, refer to the [Create Index documentation](https://docs.snowflake.com/en/sql-reference/constraints-indexes.html). You can create an index using the following methods:
|
|
238
|
+
|
|
239
|
+
#### Single Column Index
|
|
240
|
+
|
|
241
|
+
You can create a single column index by setting the `index=True` parameter on the column or by explicitly defining an `Index` object.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
hybrid_test_table_1 = HybridTable(
|
|
245
|
+
"table_name",
|
|
246
|
+
metadata,
|
|
247
|
+
Column("column1", Integer, primary_key=True),
|
|
248
|
+
Column("column2", String, index=True),
|
|
249
|
+
Index("index_1", "column1", "column2")
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
metadata.create_all(engine_testaccount)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### Multi-Column Index
|
|
256
|
+
|
|
257
|
+
For multi-column indexes, you define the `Index` object specifying the columns that should be indexed.
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
hybrid_test_table_1 = HybridTable(
|
|
261
|
+
"table_name",
|
|
262
|
+
metadata,
|
|
263
|
+
Column("column1", Integer, primary_key=True),
|
|
264
|
+
Column("column2", String),
|
|
265
|
+
Index("index_1", "column1", "column2")
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
metadata.create_all(engine_testaccount)
|
|
269
|
+
```
|
|
233
270
|
|
|
234
271
|
### Numpy Data Type Support
|
|
235
272
|
|
|
@@ -330,6 +367,79 @@ data_object = json.loads(row[1])
|
|
|
330
367
|
data_array = json.loads(row[2])
|
|
331
368
|
```
|
|
332
369
|
|
|
370
|
+
### Structured Data Types Support
|
|
371
|
+
|
|
372
|
+
This module defines custom SQLAlchemy types for Snowflake structured data, specifically for **Iceberg tables**.
|
|
373
|
+
The types —**MAP**, **OBJECT**, and **ARRAY**— allow you to store complex data structures in your SQLAlchemy models.
|
|
374
|
+
For detailed information, refer to the Snowflake [Structured data types](https://docs.snowflake.com/en/sql-reference/data-types-structured) documentation.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
#### MAP
|
|
379
|
+
|
|
380
|
+
The `MAP` type represents a collection of key-value pairs, where each key and value can have different types.
|
|
381
|
+
|
|
382
|
+
- **Key Type**: The type of the keys (e.g., `TEXT`, `NUMBER`).
|
|
383
|
+
- **Value Type**: The type of the values (e.g., `TEXT`, `NUMBER`).
|
|
384
|
+
- **Not Null**: Whether `NULL` values are allowed (default is `False`).
|
|
385
|
+
|
|
386
|
+
*Example Usage*
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
IcebergTable(
|
|
390
|
+
table_name,
|
|
391
|
+
metadata,
|
|
392
|
+
Column("id", Integer, primary_key=True),
|
|
393
|
+
Column("map_col", MAP(NUMBER(10, 0), TEXT(16777216))),
|
|
394
|
+
external_volume="external_volume",
|
|
395
|
+
base_location="base_location",
|
|
396
|
+
)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
#### OBJECT
|
|
400
|
+
|
|
401
|
+
The `OBJECT` type represents a semi-structured object with named fields. Each field can have a specific type, and you can also specify whether each field is nullable.
|
|
402
|
+
|
|
403
|
+
- **Items Types**: A dictionary of field names and their types. The type can optionally include a nullable flag (`True` for not nullable, `False` for nullable, default is `False`).
|
|
404
|
+
|
|
405
|
+
*Example Usage*
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
IcebergTable(
|
|
409
|
+
table_name,
|
|
410
|
+
metadata,
|
|
411
|
+
Column("id", Integer, primary_key=True),
|
|
412
|
+
Column(
|
|
413
|
+
"object_col",
|
|
414
|
+
OBJECT(key1=(TEXT(16777216), False), key2=(NUMBER(10, 0), False)),
|
|
415
|
+
OBJECT(key1=TEXT(16777216), key2=NUMBER(10, 0)), # Without nullable flag
|
|
416
|
+
),
|
|
417
|
+
external_volume="external_volume",
|
|
418
|
+
base_location="base_location",
|
|
419
|
+
)
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### ARRAY
|
|
423
|
+
|
|
424
|
+
The `ARRAY` type represents an ordered list of values, where each element has the same type. The type of the elements is defined when creating the array.
|
|
425
|
+
|
|
426
|
+
- **Value Type**: The type of the elements in the array (e.g., `TEXT`, `NUMBER`).
|
|
427
|
+
- **Not Null**: Whether `NULL` values are allowed (default is `False`).
|
|
428
|
+
|
|
429
|
+
*Example Usage*
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
IcebergTable(
|
|
433
|
+
table_name,
|
|
434
|
+
metadata,
|
|
435
|
+
Column("id", Integer, primary_key=True),
|
|
436
|
+
Column("array_col", ARRAY(TEXT(16777216))),
|
|
437
|
+
external_volume="external_volume",
|
|
438
|
+
base_location="base_location",
|
|
439
|
+
)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
|
|
333
443
|
### CLUSTER BY Support
|
|
334
444
|
|
|
335
445
|
Snowflake SQLAchemy supports the `CLUSTER BY` parameter for tables. For information about the parameter, see :doc:`/sql-reference/sql/create-table`.
|
|
@@ -456,6 +566,117 @@ copy_into = CopyIntoStorage(from_=users,
|
|
|
456
566
|
connection.execute(copy_into)
|
|
457
567
|
```
|
|
458
568
|
|
|
569
|
+
### Iceberg Table with Snowflake Catalog support
|
|
570
|
+
|
|
571
|
+
Snowflake SQLAlchemy supports Iceberg Tables with the Snowflake Catalog, along with various related parameters. For detailed information about Iceberg Tables, refer to the Snowflake [CREATE ICEBERG](https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table-snowflake) documentation.
|
|
572
|
+
|
|
573
|
+
To create an Iceberg Table using Snowflake SQLAlchemy, you can define the table using the SQLAlchemy Core syntax as follows:
|
|
574
|
+
|
|
575
|
+
```python
|
|
576
|
+
table = IcebergTable(
|
|
577
|
+
"myuser",
|
|
578
|
+
metadata,
|
|
579
|
+
Column("id", Integer, primary_key=True),
|
|
580
|
+
Column("name", String),
|
|
581
|
+
external_volume=external_volume_name,
|
|
582
|
+
base_location="my_iceberg_table",
|
|
583
|
+
as_query="SELECT * FROM table"
|
|
584
|
+
)
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
Alternatively, you can define the table using a declarative approach:
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
class MyUser(Base):
|
|
591
|
+
__tablename__ = "myuser"
|
|
592
|
+
|
|
593
|
+
@classmethod
|
|
594
|
+
def __table_cls__(cls, name, metadata, *arg, **kw):
|
|
595
|
+
return IcebergTable(name, metadata, *arg, **kw)
|
|
596
|
+
|
|
597
|
+
__table_args__ = {
|
|
598
|
+
"external_volume": "my_external_volume",
|
|
599
|
+
"base_location": "my_iceberg_table",
|
|
600
|
+
"as_query": "SELECT * FROM table",
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
id = Column(Integer, primary_key=True)
|
|
604
|
+
name = Column(String)
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Hybrid Table support
|
|
608
|
+
|
|
609
|
+
Snowflake SQLAlchemy supports Hybrid Tables with indexes. For detailed information, refer to the Snowflake [CREATE HYBRID TABLE](https://docs.snowflake.com/en/sql-reference/sql/create-hybrid-table) documentation.
|
|
610
|
+
|
|
611
|
+
To create a Hybrid Table and add an index, you can use the SQLAlchemy Core syntax as follows:
|
|
612
|
+
|
|
613
|
+
```python
|
|
614
|
+
table = HybridTable(
|
|
615
|
+
"myuser",
|
|
616
|
+
metadata,
|
|
617
|
+
Column("id", Integer, primary_key=True),
|
|
618
|
+
Column("name", String),
|
|
619
|
+
Index("idx_name", "name")
|
|
620
|
+
)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Alternatively, you can define the table using the declarative approach:
|
|
624
|
+
|
|
625
|
+
```python
|
|
626
|
+
class MyUser(Base):
|
|
627
|
+
__tablename__ = "myuser"
|
|
628
|
+
|
|
629
|
+
@classmethod
|
|
630
|
+
def __table_cls__(cls, name, metadata, *arg, **kw):
|
|
631
|
+
return HybridTable(name, metadata, *arg, **kw)
|
|
632
|
+
|
|
633
|
+
__table_args__ = (
|
|
634
|
+
Index("idx_name", "name"),
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
id = Column(Integer, primary_key=True)
|
|
638
|
+
name = Column(String)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### Dynamic Tables support
|
|
642
|
+
|
|
643
|
+
Snowflake SQLAlchemy supports Dynamic Tables. For detailed information, refer to the Snowflake [CREATE DYNAMIC TABLE](https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table) documentation.
|
|
644
|
+
|
|
645
|
+
To create a Dynamic Table, you can use the SQLAlchemy Core syntax as follows:
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
dynamic_test_table_1 = DynamicTable(
|
|
649
|
+
"dynamic_MyUser",
|
|
650
|
+
metadata,
|
|
651
|
+
Column("id", Integer),
|
|
652
|
+
Column("name", String),
|
|
653
|
+
target_lag=(1, TimeUnit.HOURS), # Additionally, you can use SnowflakeKeyword.DOWNSTREAM
|
|
654
|
+
warehouse='test_wh',
|
|
655
|
+
refresh_mode=SnowflakeKeyword.FULL,
|
|
656
|
+
as_query="SELECT id, name from MyUser;"
|
|
657
|
+
)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Alternatively, you can define a table without columns using the SQLAlchemy `select()` construct:
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
dynamic_test_table_1 = DynamicTable(
|
|
664
|
+
"dynamic_MyUser",
|
|
665
|
+
metadata,
|
|
666
|
+
target_lag=(1, TimeUnit.HOURS),
|
|
667
|
+
warehouse='test_wh',
|
|
668
|
+
refresh_mode=SnowflakeKeyword.FULL,
|
|
669
|
+
as_query=select(MyUser.id, MyUser.name)
|
|
670
|
+
)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Notes
|
|
674
|
+
|
|
675
|
+
- Defining a primary key in a Dynamic Table is not supported, meaning declarative tables don’t support Dynamic Tables.
|
|
676
|
+
- When using the `as_query` parameter with a string, you must explicitly define the columns. However, if you use the SQLAlchemy `select()` construct, you don’t need to explicitly define the columns.
|
|
677
|
+
- Direct data insertion into Dynamic Tables is not supported.
|
|
678
|
+
|
|
679
|
+
|
|
459
680
|
## Support
|
|
460
681
|
|
|
461
682
|
Feel free to file an issue or submit a PR here for general cases. For official support, contact Snowflake support at:
|
|
@@ -47,6 +47,7 @@ path = "src/snowflake/sqlalchemy/version.py"
|
|
|
47
47
|
development = [
|
|
48
48
|
"pre-commit",
|
|
49
49
|
"pytest",
|
|
50
|
+
"setuptools",
|
|
50
51
|
"pytest-cov",
|
|
51
52
|
"pytest-timeout",
|
|
52
53
|
"pytest-rerunfailures",
|
|
@@ -74,6 +75,8 @@ exclude = ["/.github"]
|
|
|
74
75
|
packages = ["src/snowflake"]
|
|
75
76
|
|
|
76
77
|
[tool.hatch.envs.default]
|
|
78
|
+
path = ".venv"
|
|
79
|
+
type = "virtual"
|
|
77
80
|
extra-dependencies = ["SQLAlchemy>=1.4.19,<2.1.0"]
|
|
78
81
|
features = ["development", "pandas"]
|
|
79
82
|
python = "3.8"
|