Data type support in SQLAlchemy¶
Snowflake types and their SQLAlchemy equivalents¶
Snowflake-specific types are importable from snowflake.sqlalchemy. When Snowflake SQLAlchemy reflects a column, it maps the Snowflake type name to a SQLAlchemy type as follows:
| Snowflake type names | SQLAlchemy type |
|---|---|
NUMBER, DECIMAL, DEC, FIXED | DECIMAL (NUMBER with precision and scale) |
INT, INTEGER, BIGINT, SMALLINT, TINYINT, BYTEINT | INTEGER, BIGINT, SMALLINT |
FLOAT, DOUBLE, REAL | FLOAT, DOUBLE, REAL |
DECFLOAT | snowflake.sqlalchemy.DECFLOAT |
VARCHAR, STRING, TEXT, CHAR, CHARACTER | VARCHAR, CHAR |
BINARY, VARBINARY | BINARY |
BOOLEAN | BOOLEAN |
DATE, TIME, DATETIME, TIMESTAMP | DATE, TIME, DATETIME, TIMESTAMP |
TIMESTAMP_TZ, TIMESTAMP_LTZ, TIMESTAMP_NTZ | snowflake.sqlalchemy.TIMESTAMP_TZ, TIMESTAMP_LTZ, TIMESTAMP_NTZ |
VARIANT, OBJECT, ARRAY, MAP | snowflake.sqlalchemy.VARIANT, OBJECT, ARRAY, MAP |
GEOGRAPHY, GEOMETRY | snowflake.sqlalchemy.GEOGRAPHY, GEOMETRY |
VECTOR | snowflake.sqlalchemy.VECTOR |
UUID | sqlalchemy.UUID |
Numpy data type support¶
Snowflake SQLAlchemy can bind the following NumPy types as parameter values:
numpy.int64numpy.float64numpy.datetime64
To fetch timestamp values as numpy.datetime64, add numpy=True to the connection parameters:
DECFLOAT data type support¶
Snowflake SQLAlchemy supports the DECFLOAT data type, which provides decimal floating-point values with up to 38 significant digits. Use snowflake.sqlalchemy.DECFLOAT. The SQLAlchemy Numeric type creates a NUMBER column, not a DECFLOAT column.
DECFLOAT has the following restrictions:
- The precision is fixed at 38 digits and can’t be customized.
- It isn’t supported in Iceberg tables or Hybrid Tables.
- It doesn’t support the special values
inf,-inf, andNaN.
To store DECFLOAT values inside semi-structured data, see Decimal and DECFLOAT values in semi-structured data.
DECFLOAT precision¶
The Snowflake Connector for Python converts DECFLOAT values to Python Decimal objects using the current decimal context. The default context precision is 28 digits, so values with more digits are truncated. To preserve all 38 digits, set enable_decfloat=True:
This option isn’t enabled by default because it sets decimal.getcontext().prec = 38, which changes the thread-local decimal context for all Decimal operations in that thread, not just database queries.
Returning float instead of Decimal¶
By default, numeric columns are returned as Python Decimal objects to preserve precision. If you prefer floating-point numbers, you can convert them explicitly:
Alternatively, use the cast() function for type conversion in queries:
VECTOR data type support¶
Snowflake SQLAlchemy supports the VECTOR data type for storing vector embeddings. VECTOR(element_type, dimension) requires both arguments:
element_type:Integer()orFloat(), or the string'INT'or'FLOAT'.dimension: The length of the vector, as a positive integer.
Vector values are returned as Python lists.
UUID data type support¶
By default, SQLAlchemy’s generic Uuid type (and the ORM annotation Mapped[uuid.UUID]) creates a CHAR(32) column, not a native Snowflake UUID column. Use as_uuid to choose whether values are returned as strings or as uuid.UUID objects:
Native UUID columns¶
To create native UUID columns from Uuid and Mapped[uuid.UUID], set enable_native_uuid=True. This option also allows uuid.UUID objects inside semi-structured payloads, where they’re serialized as JSON strings.
This option isn’t enabled by default because it changes the DDL of existing Uuid columns from CHAR(32) to UUID, so tables created earlier no longer match their model definitions. The explicit sqlalchemy.UUID type always creates a native UUID column, regardless of this option.
With enable_native_uuid=True, uuid.UUID values are bound to Uuid columns as strings, and reads honor as_uuid: True returns uuid.UUID objects and False returns hyphenated strings. Reflected UUID columns have as_uuid=False.
Timestamp and timezone support¶
SQLAlchemy’s DateTime type maps to Snowflake timestamp types as follows:
DateTime(timezone=False)(the default) creates aDATETIMEcolumn, which isTIMESTAMP_NTZ.DateTime(timezone=True)creates aTIMESTAMP_TZcolumn.
To choose a Snowflake timestamp type directly, use TIMESTAMP_TZ, TIMESTAMP_LTZ, or TIMESTAMP_NTZ:
Insert timezone-aware datetime values into TIMESTAMP_TZ and TIMESTAMP_LTZ columns, and naive values into TIMESTAMP_NTZ columns:
Snowflake’s TIME data type doesn’t support time zones, so Time(timezone=True) creates a plain TIME column.
VARIANT, ARRAY and OBJECT support¶
Snowflake SQLAlchemy supports the semi-structured data types VARIANT, ARRAY, and OBJECT. When you use ARRAY() or OBJECT() without element or field types, the column is semi-structured. With types, the column is a structured type.
Automatic JSON handling with enable_structured_type_json¶
In Snowflake SQLAlchemy 2.x, enable_structured_type_json defaults to True. For semi-structured VARIANT, OBJECT, and ARRAY columns, and for structured columns, this means:
- Reads return Python objects (
dictorlist) instead of JSON strings. - Writes accept a
dictorlistdirectly. The dialect serializes the value and wraps it inPARSE_JSON(). Because Snowflake doesn’t allow functions in aVALUESclause, inserts are rendered asINSERT ... SELECT, and multi-row inserts asSELECT ... UNION ALL SELECT ....
The following limitations apply while this option is on:
executemanyisn’t supported for semi-structured columns. Passing a list of two or more parameter sets, such asconnection.execute(my_table.insert(), [row1, row2]), or an ORM flush that batches several new objects, raises252001: Failed to rewrite multi-row insert. Use single-row inserts or the multi-rowinsert().values([...])form shown earlier.RETURNINGisn’t supported for inserts into semi-structured columns.literal_bindsisn’t supported for semi-structured writes. Execute statements with bound parameters instead.- Statements are limited by Snowflake’s statement size limit. For large data volumes, stage the data and use COPY INTO instead.
To restore the previous behavior, where reads return JSON strings and you write pre-serialized values, set enable_structured_type_json=False. This option is deprecated and emits a DeprecationWarning:
Subscript access¶
Python subscript syntax works on VARIANT, OBJECT, ARRAY, and MAP columns, in the SELECT list and in clauses such as WHERE:
Negative array indexes aren’t supported.
Custom JSON serializer¶
To customize JSON serialization and deserialization, pass the standard SQLAlchemy json_serializer and json_deserializer arguments to create_engine():
Decimal and DECFLOAT values in semi-structured data¶
When you write a decimal.Decimal value into a semi-structured column, it’s serialized as a JSON string so that all 38 DECFLOAT digits are preserved. To read it back as DECFLOAT, cast it to VARCHAR first, because Snowflake doesn’t allow a direct cast from the JSON text to DECFLOAT:
Also set enable_decfloat=True to avoid truncation to 28 digits. See DECFLOAT precision.
GEOGRAPHY and GEOMETRY support¶
Snowflake SQLAlchemy supports the geospatial data types GEOGRAPHY and GEOMETRY:
By default, geospatial values are returned as GeoJSON strings.
Structured data types support¶
Snowflake SQLAlchemy supports the structured data types MAP, OBJECT, and ARRAY with element, key, and field types.
With enable_structured_type_json (the default), structured columns accept a Python dict or list on insert and update, and return a dict or list on read. The dialect writes the value as CAST(PARSE_JSON(...) AS <declared type>):
The same limitations as for semi-structured columns apply: no RETURNING, no executemany, and no literal_binds. Read values are decoded from JSON and aren’t converted to the declared element types. For example, MAP(NUMBER, ...) keys are returned as strings, and DATE elements as ISO strings.
MAP¶
MAP(key_type, value_type, not_null=False) represents key-value pairs. Both key_type and value_type are required:
OBJECT¶
OBJECT(**items) represents an object with named fields. Pass each field as a keyword argument whose value is a type, or a (type, not_null) tuple:
ARRAY¶
ARRAY(value_type, not_null=False) represents a list of values of the same type:
In a standard table, Snowflake rejects ARRAY(..., not_null=True) because a non-nullable field can’t be contained within an array or map.