snowflake-sqlalchemy 1.7.1__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.1 → snowflake_sqlalchemy-1.7.2}/DESCRIPTION.md +8 -1
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/PKG-INFO +78 -3
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/README.md +73 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/pyproject.toml +3 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/base.py +20 -3
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/custom_types.py +33 -3
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/parser/custom_type_parser.py +78 -23
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/snowdialect.py +134 -105
- {snowflake_sqlalchemy-1.7.1 → 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.1 → snowflake_sqlalchemy-1.7.2}/tests/conftest.py +27 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_compiler.py +16 -1
- snowflake_sqlalchemy-1.7.2/tests/test_index_reflection.py +68 -0
- {snowflake_sqlalchemy-1.7.1 → 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.1 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_structured_types.py +10 -2
- snowflake_sqlalchemy-1.7.1/tests/__snapshots__/test_structured_datatypes.ambr +0 -90
- snowflake_sqlalchemy-1.7.1/tests/test_index_reflection.py +0 -34
- snowflake_sqlalchemy-1.7.1/tests/test_structured_datatypes.py +0 -271
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/.gitignore +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/.gitmodules +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/.pre-commit-config.yaml +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/LICENSE.txt +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/MANIFEST.in +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/build.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/build_docker.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/docker/sqlalchemy_build/Dockerfile +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/docker/sqlalchemy_build/scripts/entrypoint.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/set_base_image.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/test.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/test_docker.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/ci/test_linux.sh +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/license_header.txt +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/setup.cfg +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/snyk/requirements.txt +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/snyk/requiremtnts.txt +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/snyk/update_requirements.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/_constants.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/compat.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/custom_commands.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/exc.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/functions.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/provision.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/requirements.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/clustered_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_base.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/custom_table_prefix.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/as_query_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/cluster_by_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/identifier_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/invalid_table_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/keyword_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/keywords.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/literal_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/table_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/options/target_lag_option.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/sql/custom_schema/table_from_query.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/util.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_310.reqs +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_37.reqs +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_38.reqs +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tested_requirements/requirements_39.reqs +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/README.rst +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_compile_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_core.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_orm.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_reflect_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/__snapshots__/test_unit_structured_types.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_iceberg_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_compile_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_dynamic_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_iceberg_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_create_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_generic_options.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_reflect_hybrid_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/__snapshots__/test_reflect_snowflake_table.ambr +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_compile_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_iceberg_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_create_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_generic_options.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_dynamic_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_hybrid_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/custom_tables/test_reflect_snowflake_table.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/data/users.txt +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/README.md +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/__init__.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/conftest.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/test_suite.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/sqlalchemy_test_suite/test_suite_20.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_copy.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_core.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_create.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_custom_functions.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_custom_types.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_geography.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_geometry.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_imports.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_multivalues_insert.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_orm.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_pandas.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_qmark.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_semi_structured_datatypes.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_sequence.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_timestamp.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_core.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_cte.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_types.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/test_unit_url.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tests/util.py +0 -0
- {snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/tox.ini +0 -0
|
@@ -9,12 +9,19 @@ Source code is also available at:
|
|
|
9
9
|
|
|
10
10
|
# Release Notes
|
|
11
11
|
|
|
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
|
+
|
|
12
19
|
- v1.7.1(December 02, 2024)
|
|
13
20
|
- Add support for partition by to copy into <location>
|
|
14
21
|
- Fix BOOLEAN type not found in snowdialect
|
|
22
|
+
- Add support for autocommit Isolation Level
|
|
15
23
|
|
|
16
24
|
- v1.7.0(November 21, 2024)
|
|
17
|
-
|
|
18
25
|
- Add support for dynamic tables and required options
|
|
19
26
|
- Add support for hybrid tables
|
|
20
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'
|
|
@@ -419,6 +421,79 @@ data_object = json.loads(row[1])
|
|
|
419
421
|
data_array = json.loads(row[2])
|
|
420
422
|
```
|
|
421
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
|
+
|
|
422
497
|
### CLUSTER BY Support
|
|
423
498
|
|
|
424
499
|
Snowflake SQLAchemy supports the `CLUSTER BY` parameter for tables. For information about the parameter, see :doc:`/sql-reference/sql/create-table`.
|
|
@@ -367,6 +367,79 @@ data_object = json.loads(row[1])
|
|
|
367
367
|
data_array = json.loads(row[2])
|
|
368
368
|
```
|
|
369
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
|
+
|
|
370
443
|
### CLUSTER BY Support
|
|
371
444
|
|
|
372
445
|
Snowflake SQLAchemy supports the `CLUSTER BY` parameter for tables. For information about the parameter, see :doc:`/sql-reference/sql/create-table`.
|
|
@@ -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"
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import itertools
|
|
6
6
|
import operator
|
|
7
7
|
import re
|
|
8
|
+
import string
|
|
8
9
|
from typing import List
|
|
9
10
|
|
|
10
11
|
from sqlalchemy import exc as sa_exc
|
|
@@ -114,7 +115,8 @@ RESERVED_WORDS = frozenset(
|
|
|
114
115
|
AUTOCOMMIT_REGEXP = re.compile(
|
|
115
116
|
r"\s*(?:UPDATE|INSERT|DELETE|MERGE|COPY)", re.I | re.UNICODE
|
|
116
117
|
)
|
|
117
|
-
|
|
118
|
+
# used for quoting identifiers ie. table names, column names, etc.
|
|
119
|
+
ILLEGAL_INITIAL_CHARACTERS = frozenset({d for d in string.digits}.union({"_", "$"}))
|
|
118
120
|
|
|
119
121
|
"""
|
|
120
122
|
Overwrite methods to handle Snowflake BCR change:
|
|
@@ -439,6 +441,7 @@ class SnowflakeORMSelectCompileState(context.ORMSelectCompileState):
|
|
|
439
441
|
|
|
440
442
|
class SnowflakeIdentifierPreparer(compiler.IdentifierPreparer):
|
|
441
443
|
reserved_words = {x.lower() for x in RESERVED_WORDS}
|
|
444
|
+
illegal_initial_characters = ILLEGAL_INITIAL_CHARACTERS
|
|
442
445
|
|
|
443
446
|
def __init__(self, dialect, **kw):
|
|
444
447
|
quote = '"'
|
|
@@ -1093,10 +1096,24 @@ class SnowflakeTypeCompiler(compiler.GenericTypeCompiler):
|
|
|
1093
1096
|
)
|
|
1094
1097
|
|
|
1095
1098
|
def visit_ARRAY(self, type_, **kw):
|
|
1096
|
-
|
|
1099
|
+
if type_.is_semi_structured:
|
|
1100
|
+
return "ARRAY"
|
|
1101
|
+
not_null = f" {NOT_NULL}" if type_.not_null else ""
|
|
1102
|
+
return f"ARRAY({type_.value_type.compile()}{not_null})"
|
|
1097
1103
|
|
|
1098
1104
|
def visit_OBJECT(self, type_, **kw):
|
|
1099
|
-
|
|
1105
|
+
if type_.is_semi_structured:
|
|
1106
|
+
return "OBJECT"
|
|
1107
|
+
else:
|
|
1108
|
+
contents = []
|
|
1109
|
+
for key in type_.items_types:
|
|
1110
|
+
|
|
1111
|
+
row_text = f"{key} {type_.items_types[key][0].compile()}"
|
|
1112
|
+
# Type and not null is specified
|
|
1113
|
+
if len(type_.items_types[key]) > 1:
|
|
1114
|
+
row_text += f"{' NOT NULL' if type_.items_types[key][1] else ''}"
|
|
1115
|
+
contents.append(row_text)
|
|
1116
|
+
return "OBJECT" if contents == [] else f"OBJECT({', '.join(contents)})"
|
|
1100
1117
|
|
|
1101
1118
|
def visit_BLOB(self, type_, **kw):
|
|
1102
1119
|
return "BINARY"
|
{snowflake_sqlalchemy-1.7.1 → snowflake_sqlalchemy-1.7.2}/src/snowflake/sqlalchemy/custom_types.py
RENAMED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
3
|
#
|
|
4
|
+
from typing import Optional, Tuple, Union
|
|
4
5
|
|
|
5
6
|
import sqlalchemy.types as sqltypes
|
|
6
7
|
import sqlalchemy.util as util
|
|
8
|
+
from sqlalchemy.types import TypeEngine
|
|
7
9
|
|
|
8
10
|
TEXT = sqltypes.VARCHAR
|
|
9
11
|
CHARACTER = sqltypes.CHAR
|
|
@@ -38,7 +40,8 @@ class VARIANT(SnowflakeType):
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
class StructuredType(SnowflakeType):
|
|
41
|
-
def __init__(self):
|
|
43
|
+
def __init__(self, is_semi_structured: bool = False):
|
|
44
|
+
self.is_semi_structured = is_semi_structured
|
|
42
45
|
super().__init__()
|
|
43
46
|
|
|
44
47
|
|
|
@@ -57,13 +60,40 @@ class MAP(StructuredType):
|
|
|
57
60
|
super().__init__()
|
|
58
61
|
|
|
59
62
|
|
|
60
|
-
class OBJECT(
|
|
63
|
+
class OBJECT(StructuredType):
|
|
61
64
|
__visit_name__ = "OBJECT"
|
|
62
65
|
|
|
66
|
+
def __init__(self, **items_types: Union[TypeEngine, Tuple[TypeEngine, bool]]):
|
|
67
|
+
for key, value in items_types.items():
|
|
68
|
+
if not isinstance(value, tuple):
|
|
69
|
+
items_types[key] = (value, False)
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
self.items_types = items_types
|
|
72
|
+
self.is_semi_structured = len(items_types) == 0
|
|
73
|
+
super().__init__()
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
quote_char = "'"
|
|
77
|
+
return "OBJECT(%s)" % ", ".join(
|
|
78
|
+
[
|
|
79
|
+
f"{repr(key).strip(quote_char)}={repr(value)}"
|
|
80
|
+
for key, value in self.items_types.items()
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ARRAY(StructuredType):
|
|
65
86
|
__visit_name__ = "ARRAY"
|
|
66
87
|
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
value_type: Optional[sqltypes.TypeEngine] = None,
|
|
91
|
+
not_null: bool = False,
|
|
92
|
+
):
|
|
93
|
+
self.value_type = value_type
|
|
94
|
+
self.not_null = not_null
|
|
95
|
+
super().__init__(is_semi_structured=value_type is None)
|
|
96
|
+
|
|
67
97
|
|
|
68
98
|
class TIMESTAMP_TZ(SnowflakeType):
|
|
69
99
|
__visit_name__ = "TIMESTAMP_TZ"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
from typing import List
|
|
3
4
|
|
|
4
5
|
import sqlalchemy.types as sqltypes
|
|
5
6
|
from sqlalchemy.sql.type_api import TypeEngine
|
|
@@ -48,11 +49,10 @@ ischema_names = {
|
|
|
48
49
|
"DECIMAL": DECIMAL,
|
|
49
50
|
"DOUBLE": DOUBLE,
|
|
50
51
|
"FIXED": DECIMAL,
|
|
51
|
-
"FLOAT": FLOAT, # Snowflake FLOAT datatype doesn't
|
|
52
|
+
"FLOAT": FLOAT, # Snowflake FLOAT datatype doesn't have parameters
|
|
52
53
|
"INT": INTEGER,
|
|
53
54
|
"INTEGER": INTEGER,
|
|
54
55
|
"NUMBER": _CUSTOM_DECIMAL,
|
|
55
|
-
# 'OBJECT': ?
|
|
56
56
|
"REAL": REAL,
|
|
57
57
|
"BYTEINT": SMALLINT,
|
|
58
58
|
"SMALLINT": SMALLINT,
|
|
@@ -74,19 +74,22 @@ ischema_names = {
|
|
|
74
74
|
"GEOMETRY": GEOMETRY,
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
NOT_NULL_STR = "NOT NULL"
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
|
|
80
|
+
def tokenize_parameters(text: str, character_for_strip=",") -> list:
|
|
79
81
|
"""
|
|
80
82
|
Extracts parameters from a comma-separated string, handling parentheses.
|
|
81
83
|
|
|
82
84
|
:param text: A string with comma-separated parameters, which may include parentheses.
|
|
83
85
|
|
|
86
|
+
:param character_for_strip: A character to strip the text.
|
|
87
|
+
|
|
84
88
|
:return: A list of parameters as strings.
|
|
85
89
|
|
|
86
90
|
:example:
|
|
87
91
|
For input `"a, (b, c), d"`, the output is `['a', '(b, c)', 'd']`.
|
|
88
92
|
"""
|
|
89
|
-
|
|
90
93
|
output_parameters = []
|
|
91
94
|
parameter = ""
|
|
92
95
|
open_parenthesis = 0
|
|
@@ -97,9 +100,9 @@ def extract_parameters(text: str) -> list:
|
|
|
97
100
|
elif c == ")":
|
|
98
101
|
open_parenthesis -= 1
|
|
99
102
|
|
|
100
|
-
if open_parenthesis > 0 or c !=
|
|
103
|
+
if open_parenthesis > 0 or c != character_for_strip:
|
|
101
104
|
parameter += c
|
|
102
|
-
elif c ==
|
|
105
|
+
elif c == character_for_strip:
|
|
103
106
|
output_parameters.append(parameter.strip(" "))
|
|
104
107
|
parameter = ""
|
|
105
108
|
if parameter != "":
|
|
@@ -107,6 +110,21 @@ def extract_parameters(text: str) -> list:
|
|
|
107
110
|
return output_parameters
|
|
108
111
|
|
|
109
112
|
|
|
113
|
+
def parse_index_columns(columns: str) -> List[str]:
|
|
114
|
+
"""
|
|
115
|
+
Parses a string with a list of columns for an index.
|
|
116
|
+
|
|
117
|
+
:param columns: A string with a list of columns for an index, which may include parentheses.
|
|
118
|
+
:param compiler: A SQLAlchemy compiler.
|
|
119
|
+
|
|
120
|
+
:return: A list of columns as strings.
|
|
121
|
+
|
|
122
|
+
:example:
|
|
123
|
+
For input `"[A, B, C]"`, the output is `['A', 'B', 'C']`.
|
|
124
|
+
"""
|
|
125
|
+
return [column.strip() for column in columns.strip("[]").split(",")]
|
|
126
|
+
|
|
127
|
+
|
|
110
128
|
def parse_type(type_text: str) -> TypeEngine:
|
|
111
129
|
"""
|
|
112
130
|
Parses a type definition string and returns the corresponding SQLAlchemy type.
|
|
@@ -122,14 +140,17 @@ def parse_type(type_text: str) -> TypeEngine:
|
|
|
122
140
|
parse_type("VARCHAR(255)")
|
|
123
141
|
String(length=255)
|
|
124
142
|
"""
|
|
143
|
+
|
|
125
144
|
index = type_text.find("(")
|
|
126
145
|
type_name = type_text[:index] if index != -1 else type_text
|
|
146
|
+
|
|
127
147
|
parameters = (
|
|
128
|
-
|
|
148
|
+
tokenize_parameters(type_text[index + 1 : -1]) if type_name != type_text else []
|
|
129
149
|
)
|
|
130
150
|
|
|
131
151
|
col_type_class = ischema_names.get(type_name, None)
|
|
132
152
|
col_type_kw = {}
|
|
153
|
+
|
|
133
154
|
if col_type_class is None:
|
|
134
155
|
col_type_class = NullType
|
|
135
156
|
else:
|
|
@@ -139,6 +160,10 @@ def parse_type(type_text: str) -> TypeEngine:
|
|
|
139
160
|
col_type_kw = __parse_type_with_length_parameters(parameters)
|
|
140
161
|
elif issubclass(col_type_class, MAP):
|
|
141
162
|
col_type_kw = __parse_map_type_parameters(parameters)
|
|
163
|
+
elif issubclass(col_type_class, OBJECT):
|
|
164
|
+
col_type_kw = __parse_object_type_parameters(parameters)
|
|
165
|
+
elif issubclass(col_type_class, ARRAY):
|
|
166
|
+
col_type_kw = __parse_nullable_parameter(parameters)
|
|
142
167
|
if col_type_kw is None:
|
|
143
168
|
col_type_class = NullType
|
|
144
169
|
col_type_kw = {}
|
|
@@ -146,33 +171,63 @@ def parse_type(type_text: str) -> TypeEngine:
|
|
|
146
171
|
return col_type_class(**col_type_kw)
|
|
147
172
|
|
|
148
173
|
|
|
149
|
-
def
|
|
150
|
-
|
|
174
|
+
def __parse_object_type_parameters(parameters):
|
|
175
|
+
object_rows = {}
|
|
176
|
+
not_null_parts = NOT_NULL_STR.split(" ")
|
|
177
|
+
for parameter in parameters:
|
|
178
|
+
parameter_parts = tokenize_parameters(parameter, " ")
|
|
179
|
+
if len(parameter_parts) >= 2:
|
|
180
|
+
key = parameter_parts[0]
|
|
181
|
+
value_type = parse_type(parameter_parts[1])
|
|
182
|
+
if isinstance(value_type, NullType):
|
|
183
|
+
return None
|
|
184
|
+
not_null = (
|
|
185
|
+
len(parameter_parts) == 4
|
|
186
|
+
and parameter_parts[2] == not_null_parts[0]
|
|
187
|
+
and parameter_parts[3] == not_null_parts[1]
|
|
188
|
+
)
|
|
189
|
+
object_rows[key] = (value_type, not_null)
|
|
190
|
+
return object_rows
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def __parse_nullable_parameter(parameters):
|
|
194
|
+
if len(parameters) < 1:
|
|
195
|
+
return {}
|
|
196
|
+
elif len(parameters) > 1:
|
|
151
197
|
return None
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
value_type_str = parameters[1]
|
|
155
|
-
not_null_str = "NOT NULL"
|
|
156
|
-
not_null = False
|
|
198
|
+
parameter_str = parameters[0]
|
|
199
|
+
is_not_null = False
|
|
157
200
|
if (
|
|
158
|
-
len(
|
|
159
|
-
and
|
|
201
|
+
len(parameter_str) >= len(NOT_NULL_STR)
|
|
202
|
+
and parameter_str[-len(NOT_NULL_STR) :] == NOT_NULL_STR
|
|
160
203
|
):
|
|
161
|
-
|
|
162
|
-
|
|
204
|
+
is_not_null = True
|
|
205
|
+
parameter_str = parameter_str[: -len(NOT_NULL_STR) - 1]
|
|
163
206
|
|
|
164
|
-
|
|
165
|
-
value_type
|
|
166
|
-
if isinstance(key_type, NullType) or isinstance(value_type, NullType):
|
|
207
|
+
value_type: TypeEngine = parse_type(parameter_str)
|
|
208
|
+
if isinstance(value_type, NullType):
|
|
167
209
|
return None
|
|
168
210
|
|
|
169
211
|
return {
|
|
170
|
-
"key_type": key_type,
|
|
171
212
|
"value_type": value_type,
|
|
172
|
-
"not_null":
|
|
213
|
+
"not_null": is_not_null,
|
|
173
214
|
}
|
|
174
215
|
|
|
175
216
|
|
|
217
|
+
def __parse_map_type_parameters(parameters):
|
|
218
|
+
if len(parameters) != 2:
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
key_type_str = parameters[0]
|
|
222
|
+
value_type_str = parameters[1]
|
|
223
|
+
key_type: TypeEngine = parse_type(key_type_str)
|
|
224
|
+
value_type = __parse_nullable_parameter([value_type_str])
|
|
225
|
+
if isinstance(value_type, NullType) or isinstance(key_type, NullType):
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
return {"key_type": key_type, **value_type}
|
|
229
|
+
|
|
230
|
+
|
|
176
231
|
def __parse_type_with_length_parameters(parameters):
|
|
177
232
|
return (
|
|
178
233
|
{"length": int(parameters[0])}
|