snowflake-sqlalchemy 1.5.3__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.
Files changed (126) hide show
  1. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/.pre-commit-config.yaml +8 -0
  2. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/DESCRIPTION.md +26 -2
  3. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/PKG-INFO +26 -15
  4. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/README.md +22 -11
  5. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/build.sh +7 -5
  6. snowflake_sqlalchemy-1.7.0/ci/test_linux.sh +25 -0
  7. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/pyproject.toml +23 -3
  8. snowflake_sqlalchemy-1.7.0/snyk/requirements.txt +2 -0
  9. snowflake_sqlalchemy-1.7.0/snyk/requiremtnts.txt +2 -0
  10. snowflake_sqlalchemy-1.7.0/snyk/update_requirements.py +17 -0
  11. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/__init__.py +52 -6
  12. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/_constants.py +2 -0
  13. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/base.py +82 -30
  14. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/compat.py +36 -0
  15. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/custom_commands.py +2 -1
  16. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/custom_types.py +20 -0
  17. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/exc.py +82 -0
  18. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/functions.py +16 -0
  19. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/parser/custom_type_parser.py +190 -0
  20. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/requirements.py +16 -0
  21. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/snowdialect.py +249 -172
  22. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/__init__.py +9 -0
  23. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +37 -0
  24. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +127 -0
  25. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +13 -0
  26. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +117 -0
  27. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +62 -0
  28. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +102 -0
  29. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +33 -0
  30. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +63 -0
  31. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +58 -0
  32. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +63 -0
  33. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +25 -0
  34. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +65 -0
  35. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +14 -0
  36. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +67 -0
  37. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +84 -0
  38. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +94 -0
  39. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +70 -0
  40. snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +54 -0
  41. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/util.py +10 -2
  42. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/version.py +1 -1
  43. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_compile_dynamic_table.ambr +13 -0
  44. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_core.ambr +4 -0
  45. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_orm.ambr +4 -0
  46. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_reflect_dynamic_table.ambr +4 -0
  47. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_structured_datatypes.ambr +90 -0
  48. snowflake_sqlalchemy-1.7.0/tests/__snapshots__/test_unit_structured_types.ambr +4 -0
  49. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/conftest.py +39 -23
  50. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__init__.py +2 -0
  51. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_dynamic_table.ambr +40 -0
  52. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_hybrid_table.ambr +7 -0
  53. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_iceberg_table.ambr +19 -0
  54. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_compile_snowflake_table.ambr +35 -0
  55. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_dynamic_table.ambr +7 -0
  56. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_hybrid_table.ambr +7 -0
  57. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_iceberg_table.ambr +14 -0
  58. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_create_snowflake_table.ambr +4 -0
  59. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_generic_options.ambr +13 -0
  60. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_reflect_hybrid_table.ambr +4 -0
  61. snowflake_sqlalchemy-1.7.0/tests/custom_tables/__snapshots__/test_reflect_snowflake_table.ambr +29 -0
  62. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_dynamic_table.py +271 -0
  63. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_hybrid_table.py +52 -0
  64. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_iceberg_table.py +116 -0
  65. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_compile_snowflake_table.py +180 -0
  66. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_dynamic_table.py +124 -0
  67. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_hybrid_table.py +95 -0
  68. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_iceberg_table.py +43 -0
  69. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_create_snowflake_table.py +66 -0
  70. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_generic_options.py +83 -0
  71. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_dynamic_table.py +88 -0
  72. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_hybrid_table.py +65 -0
  73. snowflake_sqlalchemy-1.7.0/tests/custom_tables/test_reflect_snowflake_table.py +92 -0
  74. snowflake_sqlalchemy-1.7.0/tests/sqlalchemy_test_suite/__init__.py +3 -0
  75. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/conftest.py +7 -0
  76. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/test_suite.py +4 -0
  77. snowflake_sqlalchemy-1.7.0/tests/sqlalchemy_test_suite/test_suite_20.py +205 -0
  78. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_compiler.py +1 -1
  79. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_core.py +114 -135
  80. snowflake_sqlalchemy-1.7.0/tests/test_custom_functions.py +25 -0
  81. snowflake_sqlalchemy-1.7.0/tests/test_custom_types.py +67 -0
  82. snowflake_sqlalchemy-1.7.0/tests/test_index_reflection.py +34 -0
  83. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_orm.py +199 -28
  84. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_pandas.py +7 -6
  85. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_qmark.py +2 -2
  86. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_sequence.py +26 -0
  87. snowflake_sqlalchemy-1.7.0/tests/test_structured_datatypes.py +271 -0
  88. snowflake_sqlalchemy-1.7.0/tests/test_unit_structured_types.py +73 -0
  89. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/util.py +2 -0
  90. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tox.ini +4 -8
  91. snowflake_sqlalchemy-1.5.3/ci/test_linux.sh +0 -25
  92. snowflake_sqlalchemy-1.5.3/tests/test_custom_types.py +0 -36
  93. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/.gitignore +0 -0
  94. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/.gitmodules +0 -0
  95. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/LICENSE.txt +0 -0
  96. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/MANIFEST.in +0 -0
  97. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/build_docker.sh +0 -0
  98. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/docker/sqlalchemy_build/Dockerfile +0 -0
  99. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/docker/sqlalchemy_build/scripts/entrypoint.sh +0 -0
  100. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/set_base_image.sh +0 -0
  101. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/test.sh +0 -0
  102. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/ci/test_docker.sh +0 -0
  103. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/license_header.txt +0 -0
  104. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/setup.cfg +0 -0
  105. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/src/snowflake/sqlalchemy/provision.py +0 -0
  106. {snowflake_sqlalchemy-1.5.3/tests → snowflake_sqlalchemy-1.7.0/src/snowflake/sqlalchemy/sql}/__init__.py +0 -0
  107. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_310.reqs +0 -0
  108. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_37.reqs +0 -0
  109. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_38.reqs +0 -0
  110. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tested_requirements/requirements_39.reqs +0 -0
  111. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/README.rst +0 -0
  112. {snowflake_sqlalchemy-1.5.3/tests/sqlalchemy_test_suite → snowflake_sqlalchemy-1.7.0/tests}/__init__.py +0 -0
  113. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/data/users.txt +0 -0
  114. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/sqlalchemy_test_suite/README.md +0 -0
  115. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_copy.py +0 -0
  116. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_create.py +0 -0
  117. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_geography.py +0 -0
  118. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_geometry.py +0 -0
  119. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_multivalues_insert.py +0 -0
  120. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_quote.py +0 -0
  121. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_semi_structured_datatypes.py +0 -0
  122. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_timestamp.py +0 -0
  123. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_core.py +0 -0
  124. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_cte.py +0 -0
  125. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_types.py +0 -0
  126. {snowflake_sqlalchemy-1.5.3 → snowflake_sqlalchemy-1.7.0}/tests/test_unit_url.py +0 -0
@@ -4,6 +4,7 @@ repos:
4
4
  rev: v4.5.0
5
5
  hooks:
6
6
  - id: trailing-whitespace
7
+ exclude: '\.ambr$'
7
8
  - id: end-of-file-fixer
8
9
  - id: check-yaml
9
10
  exclude: .github/repo_meta.yaml
@@ -44,3 +45,10 @@ repos:
44
45
  - id: flake8
45
46
  additional_dependencies:
46
47
  - flake8-bugbear
48
+ - repo: local
49
+ hooks:
50
+ - id: requirements-update
51
+ name: "Update dependencies from pyproject.toml to snyk/requirements.txt"
52
+ language: system
53
+ entry: python snyk/update_requirements.py
54
+ files: ^pyproject.toml$
@@ -9,7 +9,31 @@ Source code is also available at:
9
9
 
10
10
  # Release Notes
11
11
 
12
- - v1.5.3(Unrelased)
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
+
23
+ - v1.6.1(July 9, 2024)
24
+
25
+ - Update internal project workflow with pypi publishing
26
+
27
+ - v1.6.0(July 8, 2024)
28
+
29
+ - support for installing with SQLAlchemy 2.0.x
30
+ - use `hatch` & `uv` for managing project virtual environments
31
+
32
+ - v1.5.4
33
+
34
+ - Add ability to set ORDER / NOORDER sequence on columns with IDENTITY
35
+
36
+ - v1.5.3(April 16, 2024)
13
37
 
14
38
  - Limit SQLAlchemy to < 2.0.0 before releasing version compatible with 2.0
15
39
 
@@ -20,7 +44,7 @@ Source code is also available at:
20
44
 
21
45
  - v1.5.1(November 03, 2023)
22
46
 
23
- - 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>.
24
48
  - Fixed credentials with `externalbrowser` authentication not caching due to incorrect parsing of boolean query parameters.
25
49
  - This fixes other boolean parameter passing to driver as well.
26
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: snowflake-sqlalchemy
3
- Version: 1.5.3
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-Expression: Apache-2.0
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
@@ -36,7 +35,7 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
36
35
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
37
36
  Requires-Python: >=3.8
38
37
  Requires-Dist: snowflake-connector-python<4.0.0
39
- Requires-Dist: sqlalchemy<2.0.0,>=1.4.19
38
+ Requires-Dist: sqlalchemy>=1.4.19
40
39
  Provides-Extra: development
41
40
  Requires-Dist: mock; extra == 'development'
42
41
  Requires-Dist: numpy; extra == 'development'
@@ -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
@@ -153,6 +153,7 @@ containing special characters need to be URL encoded to be parsed correctly. Thi
153
153
  characters could lead to authentication failure.
154
154
 
155
155
  The encoding for the password can be generated using `urllib.parse`:
156
+
156
157
  ```python
157
158
  import urllib.parse
158
159
  urllib.parse.quote("kx@% jj5/g")
@@ -163,6 +164,7 @@ urllib.parse.quote("kx@% jj5/g")
163
164
 
164
165
  To create an engine with the proper encodings, either manually constructing the url string by formatting
165
166
  or taking advantage of the `snowflake.sqlalchemy.URL` helper method:
167
+
166
168
  ```python
167
169
  import urllib.parse
168
170
  from snowflake.sqlalchemy import URL
@@ -243,14 +245,23 @@ engine = create_engine(...)
243
245
  engine.execute(<SQL>)
244
246
  engine.dispose()
245
247
 
246
- # Do this.
248
+ # Better.
247
249
  engine = create_engine(...)
248
250
  connection = engine.connect()
249
251
  try:
250
- connection.execute(<SQL>)
252
+ connection.execute(text(<SQL>))
251
253
  finally:
252
254
  connection.close()
253
255
  engine.dispose()
256
+
257
+ # Best
258
+ try:
259
+ with engine.connext() as connection:
260
+ connection.execute(text(<SQL>))
261
+ # or
262
+ connection.exec_driver_sql(<SQL>)
263
+ finally:
264
+ engine.dispose()
254
265
  ```
255
266
 
256
267
  ### Auto-increment Behavior
@@ -294,14 +305,14 @@ engine = create_engine(URL(
294
305
 
295
306
  specific_date = np.datetime64('2016-03-04T12:03:05.123456789Z')
296
307
 
297
- connection = engine.connect()
298
- connection.execute(
299
- "CREATE OR REPLACE TABLE ts_tbl(c1 TIMESTAMP_NTZ)")
300
- connection.execute(
301
- "INSERT INTO ts_tbl(c1) values(%s)", (specific_date,)
302
- )
303
- df = pd.read_sql_query("SELECT * FROM ts_tbl", engine)
304
- assert df.c1.values[0] == specific_date
308
+ with engine.connect() as connection:
309
+ connection.exec_driver_sql(
310
+ "CREATE OR REPLACE TABLE ts_tbl(c1 TIMESTAMP_NTZ)")
311
+ connection.exec_driver_sql(
312
+ "INSERT INTO ts_tbl(c1) values(%s)", (specific_date,)
313
+ )
314
+ df = pd.read_sql_query("SELECT * FROM ts_tbl", connection)
315
+ assert df.c1.values[0] == specific_date
305
316
  ```
306
317
 
307
318
  The following `NumPy` data types are supported:
@@ -381,7 +392,7 @@ This example shows how to create a table with two columns, `id` and `name`, as t
381
392
  t = Table('myuser', metadata,
382
393
  Column('id', Integer, primary_key=True),
383
394
  Column('name', String),
384
- snowflake_clusterby=['id', 'name'], ...
395
+ snowflake_clusterby=['id', 'name', text('id > 5')], ...
385
396
  )
386
397
  metadata.create_all(engine)
387
398
  ```
@@ -101,6 +101,7 @@ containing special characters need to be URL encoded to be parsed correctly. Thi
101
101
  characters could lead to authentication failure.
102
102
 
103
103
  The encoding for the password can be generated using `urllib.parse`:
104
+
104
105
  ```python
105
106
  import urllib.parse
106
107
  urllib.parse.quote("kx@% jj5/g")
@@ -111,6 +112,7 @@ urllib.parse.quote("kx@% jj5/g")
111
112
 
112
113
  To create an engine with the proper encodings, either manually constructing the url string by formatting
113
114
  or taking advantage of the `snowflake.sqlalchemy.URL` helper method:
115
+
114
116
  ```python
115
117
  import urllib.parse
116
118
  from snowflake.sqlalchemy import URL
@@ -191,14 +193,23 @@ engine = create_engine(...)
191
193
  engine.execute(<SQL>)
192
194
  engine.dispose()
193
195
 
194
- # Do this.
196
+ # Better.
195
197
  engine = create_engine(...)
196
198
  connection = engine.connect()
197
199
  try:
198
- connection.execute(<SQL>)
200
+ connection.execute(text(<SQL>))
199
201
  finally:
200
202
  connection.close()
201
203
  engine.dispose()
204
+
205
+ # Best
206
+ try:
207
+ with engine.connext() as connection:
208
+ connection.execute(text(<SQL>))
209
+ # or
210
+ connection.exec_driver_sql(<SQL>)
211
+ finally:
212
+ engine.dispose()
202
213
  ```
203
214
 
204
215
  ### Auto-increment Behavior
@@ -242,14 +253,14 @@ engine = create_engine(URL(
242
253
 
243
254
  specific_date = np.datetime64('2016-03-04T12:03:05.123456789Z')
244
255
 
245
- connection = engine.connect()
246
- connection.execute(
247
- "CREATE OR REPLACE TABLE ts_tbl(c1 TIMESTAMP_NTZ)")
248
- connection.execute(
249
- "INSERT INTO ts_tbl(c1) values(%s)", (specific_date,)
250
- )
251
- df = pd.read_sql_query("SELECT * FROM ts_tbl", engine)
252
- assert df.c1.values[0] == specific_date
256
+ with engine.connect() as connection:
257
+ connection.exec_driver_sql(
258
+ "CREATE OR REPLACE TABLE ts_tbl(c1 TIMESTAMP_NTZ)")
259
+ connection.exec_driver_sql(
260
+ "INSERT INTO ts_tbl(c1) values(%s)", (specific_date,)
261
+ )
262
+ df = pd.read_sql_query("SELECT * FROM ts_tbl", connection)
263
+ assert df.c1.values[0] == specific_date
253
264
  ```
254
265
 
255
266
  The following `NumPy` data types are supported:
@@ -329,7 +340,7 @@ This example shows how to create a table with two columns, `id` and `name`, as t
329
340
  t = Table('myuser', metadata,
330
341
  Column('id', Integer, primary_key=True),
331
342
  Column('name', String),
332
- snowflake_clusterby=['id', 'name'], ...
343
+ snowflake_clusterby=['id', 'name', text('id > 5')], ...
333
344
  )
334
345
  metadata.create_all(engine)
335
346
  ```
@@ -3,7 +3,7 @@
3
3
  # Build snowflake-sqlalchemy
4
4
  set -o pipefail
5
5
 
6
- PYTHON="python3.7"
6
+ PYTHON="python3.8"
7
7
  THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
8
  SQLALCHEMY_DIR="$(dirname "${THIS_DIR}")"
9
9
  DIST_DIR="${SQLALCHEMY_DIR}/dist"
@@ -11,14 +11,16 @@ DIST_DIR="${SQLALCHEMY_DIR}/dist"
11
11
  cd "$SQLALCHEMY_DIR"
12
12
  # Clean up previously built DIST_DIR
13
13
  if [ -d "${DIST_DIR}" ]; then
14
- echo "[WARN] ${DIST_DIR} already existing, deleting it..."
15
- rm -rf "${DIST_DIR}"
14
+ echo "[WARN] ${DIST_DIR} already existing, deleting it..."
15
+ rm -rf "${DIST_DIR}"
16
16
  fi
17
17
 
18
18
  # Constants and setup
19
+ export PATH=$PATH:$HOME/.local/bin
19
20
 
20
21
  echo "[Info] Building snowflake-sqlalchemy with $PYTHON"
21
22
  # Clean up possible build artifacts
22
23
  rm -rf build generated_version.py
23
- ${PYTHON} -m pip install --upgrade pip setuptools wheel build
24
- ${PYTHON} -m build --outdir ${DIST_DIR} .
24
+ export UV_NO_CACHE=true
25
+ ${PYTHON} -m pip install uv hatch
26
+ ${PYTHON} -m hatch build
@@ -0,0 +1,25 @@
1
+ #!/bin/bash -e
2
+ #
3
+ # Test Snowflake SQLAlchemy in Linux
4
+ # NOTES:
5
+ # - Versions to be tested should be passed in as the first argument, e.g: "3.7 3.8". If omitted 3.7-3.11 will be assumed.
6
+ # - This script assumes that ../dist/repaired_wheels has the wheel(s) built for all versions to be tested
7
+ # - This is the script that test_docker.sh runs inside of the docker container
8
+
9
+ PYTHON_VERSIONS="${1:-3.8 3.9 3.10 3.11}"
10
+ THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ SQLALCHEMY_DIR="$(dirname "${THIS_DIR}")"
12
+
13
+ # Install one copy of tox
14
+ python3 -m pip install -U tox
15
+
16
+ # Run tests
17
+ cd $SQLALCHEMY_DIR
18
+ for PYTHON_VERSION in ${PYTHON_VERSIONS}; do
19
+ echo "[Info] Testing with ${PYTHON_VERSION}"
20
+ SHORT_VERSION=$(python3 -c "print('${PYTHON_VERSION}'.replace('.', ''))")
21
+ SQLALCHEMY_WHL=$(ls $SQLALCHEMY_DIR/dist/snowflake_sqlalchemy-*-py3-none-any.whl | sort -r | head -n 1)
22
+ TEST_ENVLIST=fix_lint,py${SHORT_VERSION}-ci,py${SHORT_VERSION}-coverage
23
+ echo "[Info] Running tox for ${TEST_ENVLIST}"
24
+ python3 -m tox -e ${TEST_ENVLIST} --installpkg ${SQLALCHEMY_WHL}
25
+ done
@@ -38,7 +38,7 @@ classifiers = [
38
38
  "Topic :: Software Development :: Libraries :: Application Frameworks",
39
39
  "Topic :: Software Development :: Libraries :: Python Modules",
40
40
  ]
41
- dependencies = ["snowflake-connector-python<4.0.0", "SQLAlchemy>=1.4.19,<2.0.0"]
41
+ dependencies = ["SQLAlchemy>=1.4.19", "snowflake-connector-python<4.0.0"]
42
42
 
43
43
  [tool.hatch.version]
44
44
  path = "src/snowflake/sqlalchemy/version.py"
@@ -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
 
@@ -73,6 +74,13 @@ exclude = ["/.github"]
73
74
  packages = ["src/snowflake"]
74
75
 
75
76
  [tool.hatch.envs.default]
77
+ extra-dependencies = ["SQLAlchemy>=1.4.19,<2.1.0"]
78
+ features = ["development", "pandas"]
79
+ python = "3.8"
80
+ installer = "uv"
81
+
82
+ [tool.hatch.envs.sa14]
83
+ extra-dependencies = ["SQLAlchemy>=1.4.19,<2.0.0"]
76
84
  features = ["development", "pandas"]
77
85
  python = "3.8"
78
86
 
@@ -82,10 +90,19 @@ SQLACHEMY_WARN_20 = "1"
82
90
 
83
91
  [tool.hatch.envs.default.scripts]
84
92
  check = "pre-commit run --all-files"
85
- test-dialect = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml --ignore=tests/sqlalchemy_test_suite"
93
+ test-dialect = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml --ignore=tests/sqlalchemy_test_suite tests/"
86
94
  test-dialect-compatibility = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml tests/sqlalchemy_test_suite"
87
- test-run_v20 = "pytest -ra -vvv --tb=short --cov snowflake.sqlalchemy --cov-append --junitxml ./junit.xml --ignore=tests/sqlalchemy_test_suite --run_v20_sqlalchemy"
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/"
88
96
  gh-cache-sum = "python -VV | sha256sum | cut -d' ' -f1"
97
+ check-import = "python -c 'import snowflake.sqlalchemy; print(snowflake.sqlalchemy.__version__)'"
98
+
99
+ [[tool.hatch.envs.release.matrix]]
100
+ python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
101
+ features = ["development", "pandas"]
102
+
103
+ [tool.hatch.envs.release.scripts]
104
+ test-dialect = "pytest -ra -vvv --tb=short --ignore=tests/sqlalchemy_test_suite tests/"
105
+ test-compatibility = "pytest -ra -vvv --tb=short tests/sqlalchemy_test_suite tests/"
89
106
 
90
107
  [tool.ruff]
91
108
  line-length = 88
@@ -94,6 +111,7 @@ line-length = 88
94
111
  line-length = 88
95
112
 
96
113
  [tool.pytest.ini_options]
114
+ addopts = "-m 'not feature_max_lob_size and not aws and not requires_external_volume'"
97
115
  markers = [
98
116
  # Optional dependency groups markers
99
117
  "lambda: AWS lambda tests",
@@ -110,5 +128,7 @@ markers = [
110
128
  # Other markers
111
129
  "timeout: tests that need a timeout time",
112
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",
113
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",
114
134
  ]
@@ -0,0 +1,2 @@
1
+ SQLAlchemy>=1.4.19
2
+ snowflake-connector-python<4.0.0
@@ -0,0 +1,2 @@
1
+ snowflake-connector-python<4.0.0
2
+ SQLAlchemy>=1.4.19,<2.1.0
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+ import tomlkit
4
+
5
+
6
+ def sync():
7
+ pyproject = tomlkit.loads(Path("pyproject.toml").read_text())
8
+ snyk_reqiurements = Path("snyk/requirements.txt")
9
+ dependencies = pyproject.get("project", {}).get("dependencies", [])
10
+
11
+ with snyk_reqiurements.open("w") as fh:
12
+ fh.write("\n".join(dependencies))
13
+ fh.write("\n")
14
+
15
+
16
+ if __name__ == "__main__":
17
+ sync()
@@ -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 .util import _url as URL
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
- __all__ = (
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
+ )
@@ -10,3 +10,5 @@ PARAM_INTERNAL_APPLICATION_VERSION = "internal_application_version"
10
10
 
11
11
  APPLICATION_NAME = "SnowflakeSQLAlchemy"
12
12
  SNOWFLAKE_SQLALCHEMY_VERSION = VERSION
13
+ DIALECT_NAME = "snowflake"
14
+ NOT_NULL = "NOT NULL"