snowflake-sqlalchemy 1.5.0__tar.gz → 1.5.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.
Files changed (73) hide show
  1. snowflake_sqlalchemy-1.5.2/.gitignore +112 -0
  2. snowflake_sqlalchemy-1.5.2/.gitmodules +0 -0
  3. snowflake_sqlalchemy-1.5.2/.pre-commit-config.yaml +46 -0
  4. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/DESCRIPTION.md +11 -0
  5. snowflake_sqlalchemy-1.5.2/PKG-INFO +503 -0
  6. snowflake_sqlalchemy-1.5.2/ci/build.sh +24 -0
  7. snowflake_sqlalchemy-1.5.2/ci/build_docker.sh +31 -0
  8. snowflake_sqlalchemy-1.5.2/ci/docker/sqlalchemy_build/Dockerfile +19 -0
  9. snowflake_sqlalchemy-1.5.2/ci/docker/sqlalchemy_build/scripts/entrypoint.sh +13 -0
  10. snowflake_sqlalchemy-1.5.2/ci/set_base_image.sh +17 -0
  11. snowflake_sqlalchemy-1.5.2/ci/test.sh +27 -0
  12. snowflake_sqlalchemy-1.5.2/ci/test_docker.sh +53 -0
  13. snowflake_sqlalchemy-1.5.2/ci/test_linux.sh +25 -0
  14. snowflake_sqlalchemy-1.5.2/license_header.txt +2 -0
  15. snowflake_sqlalchemy-1.5.2/pyproject.toml +119 -0
  16. snowflake_sqlalchemy-1.5.2/setup.cfg +3 -0
  17. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/_constants.py +1 -1
  18. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/base.py +422 -18
  19. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/custom_commands.py +4 -2
  20. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/snowdialect.py +60 -22
  21. snowflake_sqlalchemy-1.5.2/src/snowflake/sqlalchemy/util.py +336 -0
  22. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/version.py +1 -1
  23. snowflake_sqlalchemy-1.5.2/tested_requirements/requirements_310.reqs +19 -0
  24. snowflake_sqlalchemy-1.5.2/tested_requirements/requirements_37.reqs +22 -0
  25. snowflake_sqlalchemy-1.5.2/tested_requirements/requirements_38.reqs +19 -0
  26. snowflake_sqlalchemy-1.5.2/tested_requirements/requirements_39.reqs +19 -0
  27. snowflake_sqlalchemy-1.5.2/tests/README.rst +51 -0
  28. snowflake_sqlalchemy-1.5.2/tests/__init__.py +3 -0
  29. snowflake_sqlalchemy-1.5.2/tests/conftest.py +265 -0
  30. snowflake_sqlalchemy-1.5.2/tests/data/users.txt +8 -0
  31. snowflake_sqlalchemy-1.5.2/tests/sqlalchemy_test_suite/README.md +21 -0
  32. snowflake_sqlalchemy-1.5.2/tests/sqlalchemy_test_suite/__init__.py +3 -0
  33. snowflake_sqlalchemy-1.5.2/tests/sqlalchemy_test_suite/conftest.py +62 -0
  34. snowflake_sqlalchemy-1.5.2/tests/sqlalchemy_test_suite/test_suite.py +151 -0
  35. snowflake_sqlalchemy-1.5.2/tests/test_compiler.py +122 -0
  36. snowflake_sqlalchemy-1.5.2/tests/test_copy.py +417 -0
  37. snowflake_sqlalchemy-1.5.2/tests/test_core.py +1886 -0
  38. snowflake_sqlalchemy-1.5.2/tests/test_create.py +132 -0
  39. snowflake_sqlalchemy-1.5.2/tests/test_custom_types.py +36 -0
  40. snowflake_sqlalchemy-1.5.2/tests/test_geography.py +69 -0
  41. snowflake_sqlalchemy-1.5.2/tests/test_geometry.py +67 -0
  42. snowflake_sqlalchemy-1.5.2/tests/test_multivalues_insert.py +43 -0
  43. snowflake_sqlalchemy-1.5.2/tests/test_orm.py +413 -0
  44. snowflake_sqlalchemy-1.5.2/tests/test_pandas.py +398 -0
  45. snowflake_sqlalchemy-1.5.2/tests/test_qmark.py +39 -0
  46. snowflake_sqlalchemy-1.5.2/tests/test_quote.py +40 -0
  47. snowflake_sqlalchemy-1.5.2/tests/test_semi_structured_datatypes.py +111 -0
  48. snowflake_sqlalchemy-1.5.2/tests/test_sequence.py +137 -0
  49. snowflake_sqlalchemy-1.5.2/tests/test_timestamp.py +83 -0
  50. snowflake_sqlalchemy-1.5.2/tests/test_unit_core.py +169 -0
  51. snowflake_sqlalchemy-1.5.2/tests/test_unit_cte.py +34 -0
  52. snowflake_sqlalchemy-1.5.2/tests/test_unit_types.py +23 -0
  53. snowflake_sqlalchemy-1.5.2/tests/test_unit_url.py +134 -0
  54. snowflake_sqlalchemy-1.5.2/tests/util.py +93 -0
  55. snowflake_sqlalchemy-1.5.2/tox.ini +144 -0
  56. snowflake-sqlalchemy-1.5.0/PKG-INFO +0 -295
  57. snowflake-sqlalchemy-1.5.0/setup.cfg +0 -84
  58. snowflake-sqlalchemy-1.5.0/setup.py +0 -17
  59. snowflake-sqlalchemy-1.5.0/src/snowflake/sqlalchemy/util.py +0 -106
  60. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/PKG-INFO +0 -295
  61. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/SOURCES.txt +0 -23
  62. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/dependency_links.txt +0 -1
  63. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/entry_points.txt +0 -2
  64. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/not-zip-safe +0 -1
  65. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/requires.txt +0 -17
  66. snowflake-sqlalchemy-1.5.0/src/snowflake_sqlalchemy.egg-info/top_level.txt +0 -1
  67. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/LICENSE.txt +0 -0
  68. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/MANIFEST.in +0 -0
  69. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/README.md +0 -0
  70. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/__init__.py +0 -0
  71. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/custom_types.py +0 -0
  72. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/provision.py +0 -0
  73. {snowflake-sqlalchemy-1.5.0 → snowflake_sqlalchemy-1.5.2}/src/snowflake/sqlalchemy/requirements.py +0 -0
@@ -0,0 +1,112 @@
1
+ tests/parameters*.py
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ env/
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ #*.spec # used to build snowsql
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *,cover
48
+ .hypothesis/
49
+
50
+ # Translations
51
+ *.mo
52
+ *.pot
53
+
54
+ # Django stuff:
55
+ *.log
56
+ local_settings.py
57
+
58
+ # Flask stuff:
59
+ instance/
60
+ .webassets-cache
61
+
62
+ # Scrapy stuff:
63
+ .scrapy
64
+
65
+ # Sphinx documentation
66
+ docs/_build/
67
+
68
+ # PyBuilder
69
+ target/
70
+
71
+ # IPython Notebook
72
+ .ipynb_checkpoints
73
+
74
+ # pyenv
75
+ .python-version
76
+
77
+ # celery beat schedule file
78
+ celerybeat-schedule
79
+
80
+ # dotenv
81
+ .env
82
+
83
+ # virtualenv
84
+ venv*/
85
+ ENV/
86
+
87
+ # Spyder project settings
88
+ .spyderproject
89
+
90
+ # Rope project settings
91
+ .ropeproject
92
+
93
+ # vim
94
+ *.swp
95
+
96
+ # others
97
+ .tox/
98
+ generated_version.py
99
+ *coverage.xml
100
+ .DS_Store
101
+
102
+ # Editor specific
103
+ .idea/
104
+ .vscode/
105
+ *.code-workspace
106
+
107
+ # WhiteSource Scan
108
+ wss-*agent.config
109
+ wss-unified-agent.jar
110
+ whitesource/
111
+ .idea
112
+ Python
File without changes
@@ -0,0 +1,46 @@
1
+ exclude: '^(.*egg.info.*|.*/parameters.py).*$'
2
+ repos:
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v4.5.0
5
+ hooks:
6
+ - id: trailing-whitespace
7
+ - id: end-of-file-fixer
8
+ - id: check-yaml
9
+ exclude: .github/repo_meta.yaml
10
+ - id: debug-statements
11
+ - repo: https://github.com/PyCQA/isort
12
+ rev: 5.13.2
13
+ hooks:
14
+ - id: isort
15
+ - repo: https://github.com/asottile/pyupgrade
16
+ rev: v3.15.1
17
+ hooks:
18
+ - id: pyupgrade
19
+ args: [--py37-plus]
20
+ - repo: https://github.com/psf/black
21
+ rev: 24.2.0
22
+ hooks:
23
+ - id: black
24
+ args:
25
+ - --safe
26
+ language_version: python3
27
+ - repo: https://github.com/Lucas-C/pre-commit-hooks.git
28
+ rev: v1.5.5
29
+ hooks:
30
+ - id: insert-license
31
+ name: insert-py-license
32
+ files: >
33
+ (?x)^(
34
+ src/snowflake/sqlalchemy/.*\.py|
35
+ tests/.*\.py|
36
+ setup.py
37
+ )$
38
+ args:
39
+ - --license-filepath
40
+ - license_header.txt
41
+ - repo: https://github.com/pycqa/flake8
42
+ rev: 7.0.0
43
+ hooks:
44
+ - id: flake8
45
+ additional_dependencies:
46
+ - flake8-bugbear
@@ -9,6 +9,17 @@ Source code is also available at:
9
9
 
10
10
  # Release Notes
11
11
 
12
+ - v1.5.2(April 11, 2024)
13
+
14
+ - Bump min SQLAlchemy to 1.4.19 for outer lateral join
15
+ - Add support for sequence ordering in tests
16
+
17
+ - v1.5.1(November 03, 2023)
18
+
19
+ - 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.
20
+ - Fixed credentials with `externalbrowser` authentication not caching due to incorrect parsing of boolean query parameters.
21
+ - This fixes other boolean parameter passing to driver as well.
22
+
12
23
  - v1.5.0(Aug 23, 2023)
13
24
 
14
25
  - Added option to create a temporary stage command.
@@ -0,0 +1,503 @@
1
+ Metadata-Version: 2.3
2
+ Name: snowflake-sqlalchemy
3
+ Version: 1.5.2
4
+ Summary: Snowflake SQLAlchemy Dialect
5
+ Project-URL: Changelog, https://github.com/snowflakedb/snowflake-sqlalchemy/blob/main/DESCRIPTION.md
6
+ Project-URL: Documentation, https://docs.snowflake.com/en/user-guide/sqlalchemy.html
7
+ Project-URL: Homepage, https://www.snowflake.com/
8
+ Project-URL: Issues, https://github.com/snowflakedb/snowflake-sqlalchemy/issues
9
+ Project-URL: Source, https://github.com/snowflakedb/snowflake-sqlalchemy
10
+ Author-email: "Snowflake Inc." <triage-snowpark-python-api-dl@snowflake.com>
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE.txt
13
+ Keywords: Snowflake,analytics,cloud,database,db,warehouse
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Environment :: Console
16
+ Classifier: Environment :: Other Environment
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Intended Audience :: Education
19
+ Classifier: Intended Audience :: Information Technology
20
+ Classifier: Intended Audience :: System Administrators
21
+ Classifier: License :: OSI Approved :: Apache Software License
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Programming Language :: Python :: 3
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Programming Language :: Python :: 3.8
26
+ Classifier: Programming Language :: Python :: 3.9
27
+ Classifier: Programming Language :: Python :: 3.10
28
+ Classifier: Programming Language :: Python :: 3.11
29
+ Classifier: Programming Language :: Python :: 3.12
30
+ Classifier: Programming Language :: SQL
31
+ Classifier: Topic :: Database
32
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
33
+ Classifier: Topic :: Software Development
34
+ Classifier: Topic :: Software Development :: Libraries
35
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
36
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
37
+ Requires-Python: >=3.8
38
+ Requires-Dist: snowflake-connector-python
39
+ Requires-Dist: sqlalchemy
40
+ Provides-Extra: development
41
+ Requires-Dist: mock; extra == 'development'
42
+ Requires-Dist: numpy; extra == 'development'
43
+ Requires-Dist: pre-commit; extra == 'development'
44
+ Requires-Dist: pytest; extra == 'development'
45
+ Requires-Dist: pytest-cov; extra == 'development'
46
+ Requires-Dist: pytest-rerunfailures; extra == 'development'
47
+ Requires-Dist: pytest-timeout; extra == 'development'
48
+ Requires-Dist: pytz; extra == 'development'
49
+ Provides-Extra: pandas
50
+ Requires-Dist: snowflake-connector-python[pandas]; extra == 'pandas'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # Snowflake SQLAlchemy
54
+
55
+ [![Build and Test](https://github.com/snowflakedb/snowflake-sqlalchemy/actions/workflows/build_test.yml/badge.svg)](https://github.com/snowflakedb/snowflake-sqlalchemy/actions/workflows/build_test.yml)
56
+ [![codecov](https://codecov.io/gh/snowflakedb/snowflake-sqlalchemy/branch/main/graph/badge.svg)](https://codecov.io/gh/snowflakedb/snowflake-sqlalchemy)
57
+ [![PyPi](https://img.shields.io/pypi/v/snowflake-sqlalchemy.svg)](https://pypi.python.org/pypi/snowflake-sqlalchemy/)
58
+ [![License Apache-2.0](https://img.shields.io/:license-Apache%202-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt)
59
+ [![Codestyle Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
60
+
61
+ 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
+
63
+ ## Prerequisites
64
+
65
+ ### Snowflake Connector for Python
66
+
67
+ The only requirement for Snowflake SQLAlchemy is the Snowflake Connector for Python; however, the connector does not need to be installed because installing Snowflake SQLAlchemy automatically installs the connector.
68
+
69
+ ### Data Analytics and Web Application Frameworks (Optional)
70
+
71
+ Snowflake SQLAlchemy can be used with [Pandas](http://pandas.pydata.org/), [Jupyter](http://jupyter.org/) and [Pyramid](http://www.pylonsproject.org/), which provide higher levels of application frameworks for data analytics and web applications. However, building a working environment from scratch is not a trivial task, particularly for novice users. Installing the frameworks requires C compilers and tools, and choosing the right tools and versions is a hurdle that might deter users from using Python applications.
72
+
73
+ An easier way to build an environment is through [Anaconda](https://www.continuum.io/why-anaconda), which provides a complete, precompiled technology stack for all users, including non-Python experts such as data analysts and students. For Anaconda installation instructions, see the [Anaconda install documentation](https://docs.continuum.io/anaconda/install). The Snowflake SQLAlchemy package can then be installed on top of Anaconda using [pip](https://pypi.python.org/pypi/pip).
74
+
75
+ ## Installing Snowflake SQLAlchemy
76
+
77
+ The Snowflake SQLAlchemy package can be installed from the public PyPI repository using `pip`:
78
+
79
+ ```shell
80
+ pip install --upgrade snowflake-sqlalchemy
81
+ ```
82
+
83
+ `pip` automatically installs all required modules, including the Snowflake Connector for Python.
84
+
85
+ ## Verifying Your Installation
86
+
87
+ 1. Create a file (e.g. `validate.py`) that contains the following Python sample code,
88
+ which connects to Snowflake and displays the Snowflake version:
89
+
90
+ ```python
91
+ from sqlalchemy import create_engine
92
+
93
+ engine = create_engine(
94
+ 'snowflake://{user}:{password}@{account}/'.format(
95
+ user='<your_user_login_name>',
96
+ password='<your_password>',
97
+ account='<your_account_name>',
98
+ )
99
+ )
100
+ try:
101
+ connection = engine.connect()
102
+ results = connection.execute('select current_version()').fetchone()
103
+ print(results[0])
104
+ finally:
105
+ connection.close()
106
+ engine.dispose()
107
+ ```
108
+
109
+ 2. Replace `<your_user_login_name>`, `<your_password>`, and `<your_account_name>` with the appropriate values for your Snowflake account and user.
110
+
111
+ For more details, see [Connection Parameters](#connection-parameters).
112
+
113
+ 3. Execute the sample code. For example, if you created a file named `validate.py`:
114
+
115
+ ```shell
116
+ python validate.py
117
+ ```
118
+
119
+ The Snowflake version (e.g. `1.48.0`) should be displayed.
120
+
121
+ ## Parameters and Behavior
122
+
123
+ As much as possible, Snowflake SQLAlchemy provides compatible functionality for SQLAlchemy applications. For information on using SQLAlchemy, see the [SQLAlchemy documentation](http://docs.sqlalchemy.org/en/latest/).
124
+
125
+ However, Snowflake SQLAlchemy also provides Snowflake-specific parameters and behavior, which are described in the following sections.
126
+
127
+ ### Connection Parameters
128
+
129
+ Snowflake SQLAlchemy uses the following syntax for the connection string used to connect to Snowflake and initiate a session:
130
+
131
+ ```python
132
+ 'snowflake://<user_login_name>:<password>@<account_name>'
133
+ ```
134
+
135
+ Where:
136
+
137
+ - `<user_login_name>` is the login name for your Snowflake user.
138
+ - `<password>` is the password for your Snowflake user.
139
+ - `<account_name>` is the name of your Snowflake account.
140
+
141
+ Include the region in the `<account_name>` if applicable, more info is available [here](https://docs.snowflake.com/en/user-guide/connecting.html#your-snowflake-account-name).
142
+
143
+ You can optionally specify the initial database and schema for the Snowflake session by including them at the end of the connection string, separated by `/`. You can also specify the initial warehouse and role for the session as a parameter string at the end of the connection string:
144
+
145
+ ```python
146
+ 'snowflake://<user_login_name>:<password>@<account_name>/<database_name>/<schema_name>?warehouse=<warehouse_name>&role=<role_name>'
147
+ ```
148
+
149
+ #### Escaping Special Characters such as `%, @` signs in Passwords
150
+
151
+ As pointed out in [SQLAlchemy](https://docs.sqlalchemy.org/en/14/core/engines.html#escaping-special-characters-such-as-signs-in-passwords), URLs
152
+ containing special characters need to be URL encoded to be parsed correctly. This includes the `%, @` signs. Unescaped password containing special
153
+ characters could lead to authentication failure.
154
+
155
+ The encoding for the password can be generated using `urllib.parse`:
156
+ ```python
157
+ import urllib.parse
158
+ urllib.parse.quote("kx@% jj5/g")
159
+ 'kx%40%25%20jj5/g'
160
+ ```
161
+
162
+ **Note**: `urllib.parse.quote_plus` may also be used if there is no space in the string, as `urllib.parse.quote_plus` will replace space with `+`.
163
+
164
+ To create an engine with the proper encodings, either manually constructing the url string by formatting
165
+ or taking advantage of the `snowflake.sqlalchemy.URL` helper method:
166
+ ```python
167
+ import urllib.parse
168
+ from snowflake.sqlalchemy import URL
169
+ from sqlalchemy import create_engine
170
+
171
+ quoted_password = urllib.parse.quote("kx@% jj5/g")
172
+
173
+ # 1. manually constructing an url string
174
+ url = f'snowflake://testuser1:{quoted_password}@abc123/testdb/public?warehouse=testwh&role=myrole'
175
+ engine = create_engine(url)
176
+
177
+ # 2. using the snowflake.sqlalchemy.URL helper method
178
+ engine = create_engine(URL(
179
+ account = 'abc123',
180
+ user = 'testuser1',
181
+ password = quoted_password,
182
+ database = 'testdb',
183
+ schema = 'public',
184
+ warehouse = 'testwh',
185
+ role='myrole',
186
+ ))
187
+ ```
188
+
189
+ **Note**:
190
+ After login, the initial database, schema, warehouse and role specified in the connection string can always be changed for the session.
191
+
192
+ The following example calls the `create_engine` method with the user name `testuser1`, password `0123456`, account name `abc123`, database `testdb`, schema `public`, warehouse `testwh`, and role `myrole`:
193
+
194
+ ```python
195
+ from sqlalchemy import create_engine
196
+ engine = create_engine(
197
+ 'snowflake://testuser1:0123456@abc123/testdb/public?warehouse=testwh&role=myrole'
198
+ )
199
+ ```
200
+
201
+ Other parameters, such as `timezone`, can also be specified as a URI parameter or in `connect_args` parameters. For example:
202
+
203
+ ```python
204
+ from sqlalchemy import create_engine
205
+ engine = create_engine(
206
+ 'snowflake://testuser1:0123456@abc123/testdb/public?warehouse=testwh&role=myrole',
207
+ connect_args={
208
+ 'timezone': 'America/Los_Angeles',
209
+ }
210
+ )
211
+ ```
212
+
213
+ For convenience, you can use the `snowflake.sqlalchemy.URL` method to construct the connection string and connect to the database. The following example constructs the same connection string from the previous example:
214
+
215
+ ```python
216
+ from snowflake.sqlalchemy import URL
217
+ from sqlalchemy import create_engine
218
+
219
+ engine = create_engine(URL(
220
+ account = 'abc123',
221
+ user = 'testuser1',
222
+ password = '0123456',
223
+ database = 'testdb',
224
+ schema = 'public',
225
+ warehouse = 'testwh',
226
+ role='myrole',
227
+ timezone = 'America/Los_Angeles',
228
+ ))
229
+ ```
230
+
231
+ #### Using a proxy server
232
+
233
+ Use the supported environment variables, `HTTPS_PROXY`, `HTTP_PROXY` and `NO_PROXY` to configure a proxy server.
234
+
235
+ ### Opening and Closing Connection
236
+
237
+ Open a connection by executing `engine.connect()`; avoid using `engine.execute()`. Make certain to close the connection by executing `connection.close()` before
238
+ `engine.dispose()`; otherwise, the Python Garbage collector removes the resources required to communicate with Snowflake, preventing the Python connector from closing the session properly.
239
+
240
+ ```python
241
+ # Avoid this.
242
+ engine = create_engine(...)
243
+ engine.execute(<SQL>)
244
+ engine.dispose()
245
+
246
+ # Do this.
247
+ engine = create_engine(...)
248
+ connection = engine.connect()
249
+ try:
250
+ connection.execute(<SQL>)
251
+ finally:
252
+ connection.close()
253
+ engine.dispose()
254
+ ```
255
+
256
+ ### Auto-increment Behavior
257
+
258
+ Auto-incrementing a value requires the `Sequence` object. Include the `Sequence` object in the primary key column to automatically increment the value as each new record is inserted. For example:
259
+
260
+ ```python
261
+ t = Table('mytable', metadata,
262
+ Column('id', Integer, Sequence('id_seq'), primary_key=True),
263
+ Column(...), ...
264
+ )
265
+ ```
266
+
267
+ ### Object Name Case Handling
268
+
269
+ 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 agaisnt 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.
270
+
271
+ ### Index Support
272
+
273
+ Snowflake does not utilize indexes, so neither does Snowflake SQLAlchemy.
274
+
275
+ ### Numpy Data Type Support
276
+
277
+ Snowflake SQLAlchemy supports binding and fetching `NumPy` data types. Binding is always supported. To enable fetching `NumPy` data types, add `numpy=True` to the connection parameters.
278
+
279
+ The following example shows the round trip of `numpy.datetime64` data:
280
+
281
+ ```python
282
+ import numpy as np
283
+ import pandas as pd
284
+ engine = create_engine(URL(
285
+ account = 'abc123',
286
+ user = 'testuser1',
287
+ password = 'pass',
288
+ database = 'db',
289
+ schema = 'public',
290
+ warehouse = 'testwh',
291
+ role='myrole',
292
+ numpy=True,
293
+ ))
294
+
295
+ specific_date = np.datetime64('2016-03-04T12:03:05.123456789Z')
296
+
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
305
+ ```
306
+
307
+ The following `NumPy` data types are supported:
308
+
309
+ - numpy.int64
310
+ - numpy.float64
311
+ - numpy.datatime64
312
+
313
+ ### Cache Column Metadata
314
+
315
+ SQLAlchemy provides [the runtime inspection API](http://docs.sqlalchemy.org/en/latest/core/inspection.html) to get the runtime information about the various objects. One of the common use case is get all tables and their column metadata in a schema in order to construct a schema catalog. For example, [alembic](http://alembic.zzzcomputing.com/) on top of SQLAlchemy manages database schema migrations. A pseudo code flow is as follows:
316
+
317
+ ```python
318
+ inspector = inspect(engine)
319
+ schema = inspector.default_schema_name
320
+ for table_name in inspector.get_table_names(schema):
321
+ column_metadata = inspector.get_columns(table_name, schema)
322
+ primary_keys = inspector.get_pk_constraint(table_name, schema)
323
+ foreign_keys = inspector.get_foreign_keys(table_name, schema)
324
+ ...
325
+ ```
326
+
327
+ In this flow, a potential problem is it may take quite a while as queries run on each table. The results are cached but getting column metadata is expensive.
328
+
329
+ To mitigate the problem, Snowflake SQLAlchemy takes a flag `cache_column_metadata=True` such that all of column metadata for all tables are cached when `get_table_names` is called and the rest of `get_columns`, `get_primary_keys` and `get_foreign_keys` can take advantage of the cache.
330
+
331
+ ```python
332
+ engine = create_engine(URL(
333
+ account = 'abc123',
334
+ user = 'testuser1',
335
+ password = 'pass',
336
+ database = 'db',
337
+ schema = 'public',
338
+ warehouse = 'testwh',
339
+ role='myrole',
340
+ cache_column_metadata=True,
341
+ ))
342
+ ```
343
+
344
+ Note that this flag has been deprecated, as our caching now uses the built-in SQLAlchemy reflection cache, the flag has been removed, but caching has been improved and if possible extra data will be fetched and cached.
345
+
346
+ ### VARIANT, ARRAY and OBJECT Support
347
+
348
+ Snowflake SQLAlchemy supports fetching `VARIANT`, `ARRAY` and `OBJECT` data types. All types are converted into `str` in Python so that you can convert them to native data types using `json.loads`.
349
+
350
+ This example shows how to create a table including `VARIANT`, `ARRAY`, and `OBJECT` data type columns.
351
+
352
+ ```python
353
+ from snowflake.sqlalchemy import (VARIANT, ARRAY, OBJECT)
354
+
355
+ t = Table('my_semi_strucutred_datatype_table', metadata,
356
+ Column('va', VARIANT),
357
+ Column('ob', OBJECT),
358
+ Column('ar', ARRAY))
359
+ metdata.create_all(engine)
360
+ ```
361
+
362
+ In order to retrieve `VARIANT`, `ARRAY`, and `OBJECT` data type columns and convert them to the native Python data types, fetch data and call the `json.loads` method as follows:
363
+
364
+ ```python
365
+ import json
366
+ connection = engine.connect()
367
+ results = connection.execute(select([t])
368
+ row = results.fetchone()
369
+ data_variant = json.loads(row[0])
370
+ data_object = json.loads(row[1])
371
+ data_array = json.loads(row[2])
372
+ ```
373
+
374
+ ### CLUSTER BY Support
375
+
376
+ Snowflake SQLAchemy supports the `CLUSTER BY` parameter for tables. For information about the parameter, see :doc:`/sql-reference/sql/create-table`.
377
+
378
+ This example shows how to create a table with two columns, `id` and `name`, as the clustering keys:
379
+
380
+ ```python
381
+ t = Table('myuser', metadata,
382
+ Column('id', Integer, primary_key=True),
383
+ Column('name', String),
384
+ snowflake_clusterby=['id', 'name'], ...
385
+ )
386
+ metadata.create_all(engine)
387
+ ```
388
+
389
+ ### Alembic Support
390
+
391
+ [Alembic](http://alembic.zzzcomputing.com) is a database migration tool on top of `SQLAlchemy`. Snowflake SQLAlchemy works by adding the following code to `alembic/env.py` so that Alembic can recognize Snowflake SQLAlchemy.
392
+
393
+ ```python
394
+ from alembic.ddl.impl import DefaultImpl
395
+
396
+ class SnowflakeImpl(DefaultImpl):
397
+ __dialect__ = 'snowflake'
398
+ ```
399
+
400
+ See [Alembic Documentation](http://alembic.zzzcomputing.com/) for general usage.
401
+
402
+ ### Key Pair Authentication Support
403
+
404
+ Snowflake SQLAlchemy supports key pair authentication by leveraging its Snowflake Connector for Python underpinnings. See [Using Key Pair Authentication](https://docs.snowflake.net/manuals/user-guide/python-connector-example.html#using-key-pair-authentication) for steps to create the private and public keys.
405
+
406
+ The private key parameter is passed through `connect_args` as follows:
407
+
408
+ ```python
409
+ ...
410
+ from snowflake.sqlalchemy import URL
411
+ from sqlalchemy import create_engine
412
+
413
+ from cryptography.hazmat.backends import default_backend
414
+ from cryptography.hazmat.primitives.asymmetric import rsa
415
+ from cryptography.hazmat.primitives.asymmetric import dsa
416
+ from cryptography.hazmat.primitives import serialization
417
+
418
+ with open("rsa_key.p8", "rb") as key:
419
+ p_key= serialization.load_pem_private_key(
420
+ key.read(),
421
+ password=os.environ['PRIVATE_KEY_PASSPHRASE'].encode(),
422
+ backend=default_backend()
423
+ )
424
+
425
+ pkb = p_key.private_bytes(
426
+ encoding=serialization.Encoding.DER,
427
+ format=serialization.PrivateFormat.PKCS8,
428
+ encryption_algorithm=serialization.NoEncryption())
429
+
430
+ engine = create_engine(URL(
431
+ account='abc123',
432
+ user='testuser1',
433
+ ),
434
+ connect_args={
435
+ 'private_key': pkb,
436
+ },
437
+ )
438
+ ```
439
+
440
+ Where `PRIVATE_KEY_PASSPHRASE` is a passphrase to decrypt the private key file, `rsa_key.p8`.
441
+
442
+ Currently a private key parameter is not accepted by the `snowflake.sqlalchemy.URL` method.
443
+
444
+ ### Merge Command Support
445
+
446
+ Snowflake SQLAlchemy supports upserting with its `MergeInto` custom expression.
447
+ See [Merge](https://docs.snowflake.net/manuals/sql-reference/sql/merge.html) for full documentation.
448
+
449
+ Use it as follows:
450
+
451
+ ```python
452
+ from sqlalchemy.orm import sessionmaker
453
+ from sqlalchemy import MetaData, create_engine
454
+ from snowflake.sqlalchemy import MergeInto
455
+
456
+ engine = create_engine(db.url, echo=False)
457
+ session = sessionmaker(bind=engine)()
458
+ connection = engine.connect()
459
+
460
+ meta = MetaData()
461
+ meta.reflect(bind=session.bind)
462
+ t1 = meta.tables['t1']
463
+ t2 = meta.tables['t2']
464
+
465
+ merge = MergeInto(target=t1, source=t2, on=t1.c.t1key == t2.c.t2key)
466
+ merge.when_matched_then_delete().where(t2.c.marked == 1)
467
+ merge.when_matched_then_update().where(t2.c.isnewstatus == 1).values(val = t2.c.newval, status=t2.c.newstatus)
468
+ merge.when_matched_then_update().values(val=t2.c.newval)
469
+ merge.when_not_matched_then_insert().values(val=t2.c.newval, status=t2.c.newstatus)
470
+ connection.execute(merge)
471
+ ```
472
+
473
+ ### CopyIntoStorage Support
474
+
475
+ Snowflake SQLAlchemy supports saving tables/query results into different stages, as well as into Azure Containers and
476
+ AWS buckets with its custom `CopyIntoStorage` expression. See [Copy into](https://docs.snowflake.net/manuals/sql-reference/sql/copy-into-location.html)
477
+ for full documentation.
478
+
479
+ Use it as follows:
480
+
481
+ ```python
482
+ from sqlalchemy.orm import sessionmaker
483
+ from sqlalchemy import MetaData, create_engine
484
+ from snowflake.sqlalchemy import CopyIntoStorage, AWSBucket, CSVFormatter
485
+
486
+ engine = create_engine(db.url, echo=False)
487
+ session = sessionmaker(bind=engine)()
488
+ connection = engine.connect()
489
+
490
+ meta = MetaData()
491
+ meta.reflect(bind=session.bind)
492
+ users = meta.tables['users']
493
+
494
+ copy_into = CopyIntoStorage(from_=users,
495
+ into=AWSBucket.from_uri('s3://my_private_backup').encryption_aws_sse_kms('1234abcd-12ab-34cd-56ef-1234567890ab'),
496
+ formatter=CSVFormatter().null_if(['null', 'Null']))
497
+ connection.execute(copy_into)
498
+ ```
499
+
500
+ ## Support
501
+
502
+ Feel free to file an issue or submit a PR here for general cases. For official support, contact Snowflake support at:
503
+ <https://community.snowflake.com/s/article/How-To-Submit-a-Support-Case-in-Snowflake-Lodge>