diff --git a/src/sqlalchemy_cratedb/compiler.py b/src/sqlalchemy_cratedb/compiler.py index d3a66188..3bffb7d8 100644 --- a/src/sqlalchemy_cratedb/compiler.py +++ b/src/sqlalchemy_cratedb/compiler.py @@ -196,6 +196,9 @@ def visit_unique_constraint(self, constraint, **kw): "they will be omitted when generating DDL statements.") return None + def visit_create_index(self, create, **kw): + return "SELECT 1;" + class CrateTypeCompiler(compiler.GenericTypeCompiler): @@ -244,6 +247,30 @@ def visit_FLOAT_VECTOR(self, type_, **kw): raise ValueError("FloatVector must be initialized with dimension size") return f"FLOAT_VECTOR({dimensions})" + def visit_BLOB(self, type_, **kw): + return "STRING" + + def visit_FLOAT(self, type_, **kw): + """ + From `sqlalchemy.sql.sqltypes.Float`. + + When a :paramref:`.Float.precision` is not provided in a + :class:`_types.Float` type some backend may compile this type as + an 8 bytes / 64 bit float datatype. To use a 4 bytes / 32 bit float + datatype a precision <= 24 can usually be provided or the + :class:`_types.REAL` type can be used. + This is known to be the case in the PostgreSQL and MSSQL dialects + that render the type as ``FLOAT`` that's in both an alias of + ``DOUBLE PRECISION``. Other third party dialects may have similar + behavior. + """ + if not type_.precision: + return "FLOAT" + elif type_.precision <= 24: + return "FLOAT" + else: + return "DOUBLE" + class CrateCompiler(compiler.SQLCompiler): diff --git a/src/sqlalchemy_cratedb/dialect.py b/src/sqlalchemy_cratedb/dialect.py index 425da36f..a157257c 100644 --- a/src/sqlalchemy_cratedb/dialect.py +++ b/src/sqlalchemy_cratedb/dialect.py @@ -34,6 +34,7 @@ from crate.client.exceptions import TimezoneUnawareException from .sa_version import SA_VERSION, SA_1_4, SA_2_0 from .type import FloatVector, ObjectArray, ObjectType +from .type.binary import LargeBinary TYPES_MAP = { "boolean": sqltypes.Boolean, @@ -152,7 +153,8 @@ def process(value): colspecs = { sqltypes.DateTime: DateTime, - sqltypes.Date: Date + sqltypes.Date: Date, + sqltypes.LargeBinary: LargeBinary, } diff --git a/src/sqlalchemy_cratedb/type/__init__.py b/src/sqlalchemy_cratedb/type/__init__.py index 5bd871dc..885597f5 100644 --- a/src/sqlalchemy_cratedb/type/__init__.py +++ b/src/sqlalchemy_cratedb/type/__init__.py @@ -1,4 +1,5 @@ from .array import ObjectArray +from .binary import LargeBinary from .geo import Geopoint, Geoshape from .object import ObjectType from .vector import FloatVector diff --git a/src/sqlalchemy_cratedb/type/binary.py b/src/sqlalchemy_cratedb/type/binary.py new file mode 100644 index 00000000..bbbb29fe --- /dev/null +++ b/src/sqlalchemy_cratedb/type/binary.py @@ -0,0 +1,44 @@ +import base64 +import sqlalchemy as sa + + +class LargeBinary(sa.String): + + """A type for large binary byte data. + + The :class:`.LargeBinary` type corresponds to a large and/or unlengthed + binary type for the target platform, such as BLOB on MySQL and BYTEA for + PostgreSQL. It also handles the necessary conversions for the DBAPI. + + """ + + __visit_name__ = "large_binary" + + def bind_processor(self, dialect): + if dialect.dbapi is None: + return None + + # TODO: DBAPIBinary = dialect.dbapi.Binary + + def process(value): + if value is not None: + # TODO: return DBAPIBinary(value) + return base64.b64encode(value).decode() + else: + return None + + return process + + # Python 3 has native bytes() type + # both sqlite3 and pg8000 seem to return it, + # psycopg2 as of 2.5 returns 'memoryview' + def result_processor(self, dialect, coltype): + if dialect.returns_native_bytes: + return None + + def process(value): + if value is not None: + return base64.b64decode(value) + return value + + return process