Skip to content

Commit

Permalink
Types: Improve type mappings
Browse files Browse the repository at this point in the history
- Consequently use upper-case type definitions from `sqlalchemy.types`
- Add `timestamp without time zone` types (scalar and array)
- On SQLAlchemy 2, map `real` and `double{_precision}` types to the
  newly introduced `sqltypes.{DOUBLE,DOUBLE_PRECISION}` types

All of this is intended to improve reverse type lookups / reflections.
  • Loading branch information
amotl committed Jun 25, 2024
1 parent 6db4702 commit a63ce6e
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 30 deletions.
4 changes: 2 additions & 2 deletions docs/inspection-reflection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ Create a SQLAlchemy table object:
Reflect column data types from the table metadata:

>>> table.columns.get('name')
Column('name', String(), table=<characters>)
Column('name', VARCHAR(), table=<characters>)

>>> table.primary_key
PrimaryKeyConstraint(Column('id', String(), table=<characters>, primary_key=True...
PrimaryKeyConstraint(Column('id', VARCHAR(), table=<characters>, primary_key=True...


CrateDialect
Expand Down
63 changes: 38 additions & 25 deletions src/sqlalchemy_cratedb/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,54 @@
from .type import FloatVector, ObjectArray, ObjectType

TYPES_MAP = {
"boolean": sqltypes.Boolean,
"short": sqltypes.SmallInteger,
"smallint": sqltypes.SmallInteger,
"timestamp": sqltypes.TIMESTAMP(timezone=False),
"timestamp with time zone": sqltypes.TIMESTAMP(timezone=True),
"boolean": sqltypes.BOOLEAN,
"short": sqltypes.SMALLINT,
"smallint": sqltypes.SMALLINT,
"timestamp": sqltypes.TIMESTAMP,
"timestamp with time zone": sqltypes.TIMESTAMP(timezone=False),
"timestamp without time zone": sqltypes.TIMESTAMP(timezone=True),
"object": ObjectType,
"integer": sqltypes.Integer,
"long": sqltypes.NUMERIC,
"bigint": sqltypes.NUMERIC,
"object_array": ObjectArray, # TODO: Can this also be improved to use `sqltypes.ARRAY`?
"integer": sqltypes.INTEGER,
"long": sqltypes.BIGINT,
"bigint": sqltypes.BIGINT,
"float": sqltypes.FLOAT,
"double": sqltypes.DECIMAL,
"double precision": sqltypes.DECIMAL,
"object_array": ObjectArray,
"float": sqltypes.Float,
"real": sqltypes.Float,
"string": sqltypes.String,
"text": sqltypes.String,
"real": sqltypes.REAL,
"string": sqltypes.VARCHAR,
"text": sqltypes.VARCHAR,
"float_vector": FloatVector,
}

try:
# SQLAlchemy >= 1.1
from sqlalchemy.types import ARRAY
TYPES_MAP["integer_array"] = ARRAY(sqltypes.Integer)
TYPES_MAP["boolean_array"] = ARRAY(sqltypes.Boolean)
TYPES_MAP["short_array"] = ARRAY(sqltypes.SmallInteger)
TYPES_MAP["smallint_array"] = ARRAY(sqltypes.SmallInteger)
TYPES_MAP["integer_array"] = ARRAY(sqltypes.INTEGER)
TYPES_MAP["boolean_array"] = ARRAY(sqltypes.BOOLEAN)
TYPES_MAP["short_array"] = ARRAY(sqltypes.SMALLINT)
TYPES_MAP["smallint_array"] = ARRAY(sqltypes.SMALLINT)
TYPES_MAP["timestamp_array"] = ARRAY(sqltypes.TIMESTAMP)
TYPES_MAP["timestamp_array"] = ARRAY(sqltypes.TIMESTAMP(timezone=False))
TYPES_MAP["timestamp with time zone_array"] = ARRAY(sqltypes.TIMESTAMP(timezone=True))
TYPES_MAP["long_array"] = ARRAY(sqltypes.NUMERIC)
TYPES_MAP["bigint_array"] = ARRAY(sqltypes.NUMERIC)
TYPES_MAP["double_array"] = ARRAY(sqltypes.DECIMAL)
TYPES_MAP["double precision_array"] = ARRAY(sqltypes.DECIMAL)
TYPES_MAP["float_array"] = ARRAY(sqltypes.Float)
TYPES_MAP["real_array"] = ARRAY(sqltypes.Float)
TYPES_MAP["string_array"] = ARRAY(sqltypes.String)
TYPES_MAP["text_array"] = ARRAY(sqltypes.String)
TYPES_MAP["long_array"] = ARRAY(sqltypes.BIGINT)
TYPES_MAP["bigint_array"] = ARRAY(sqltypes.BIGINT)
TYPES_MAP["float_array"] = ARRAY(sqltypes.FLOAT)
TYPES_MAP["real_array"] = ARRAY(sqltypes.REAL)
TYPES_MAP["string_array"] = ARRAY(sqltypes.VARCHAR)
TYPES_MAP["text_array"] = ARRAY(sqltypes.VARCHAR)
except Exception:
pass

Check warning on line 76 in src/sqlalchemy_cratedb/dialect.py

View check run for this annotation

Codecov / codecov/patch

src/sqlalchemy_cratedb/dialect.py#L75-L76

Added lines #L75 - L76 were not covered by tests

try:
# SQLAlchemy >= 2.0
from sqlalchemy.types import DOUBLE, DOUBLE_PRECISION
TYPES_MAP["real"] = DOUBLE
TYPES_MAP["real_array"] = ARRAY(DOUBLE)
TYPES_MAP["double"] = DOUBLE
TYPES_MAP["double_array"] = ARRAY(DOUBLE)
TYPES_MAP["double precision"] = DOUBLE_PRECISION
TYPES_MAP["double precision_array"] = ARRAY(DOUBLE_PRECISION)
except Exception:
pass

Expand Down
12 changes: 9 additions & 3 deletions tests/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import logging

from crate.client import connect
from sqlalchemy_cratedb import SA_VERSION, SA_2_0
from sqlalchemy_cratedb import SA_VERSION, SA_2_0, SA_1_4
from tests.settings import crate_host

log = logging.getLogger()
Expand Down Expand Up @@ -184,16 +184,22 @@ def create_test_suite():
'docs/crud.rst',
'docs/working-with-types.rst',
'docs/advanced-querying.rst',
'docs/inspection-reflection.rst',
]

# Don't run DataFrame integration tests on SQLAlchemy 1.3 and Python 3.7.
# Don't run DataFrame integration tests on SQLAlchemy 1.4 and earlier, or Python 3.7.
skip_dataframe = SA_VERSION < SA_2_0 or sys.version_info < (3, 8)
if not skip_dataframe:
sqlalchemy_integration_tests += [
'docs/dataframe.rst',
]

# Don't run reflection integration tests on SQLAlchemy 1.3 and earlier and Python 3.10 and 3.11.
skip_reflection = SA_VERSION < SA_1_4 and (3, 10) <= sys.version_info < (3, 12)
if not skip_reflection:
sqlalchemy_integration_tests += [
'docs/inspection-reflection.rst',
]

s = doctest.DocFileSuite(
*sqlalchemy_integration_tests,
module_relative=False,
Expand Down

0 comments on commit a63ce6e

Please sign in to comment.