Skip to content

Commit

Permalink
Types: Add support for BINARY columns and improve support for FLOATs
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Jan 15, 2024
1 parent 2f08b2a commit 2a34862
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 1 deletion.
27 changes: 27 additions & 0 deletions src/sqlalchemy_cratedb/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down Expand Up @@ -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):

Expand Down
4 changes: 3 additions & 1 deletion src/sqlalchemy_cratedb/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -152,7 +153,8 @@ def process(value):

colspecs = {
sqltypes.DateTime: DateTime,
sqltypes.Date: Date
sqltypes.Date: Date,
sqltypes.LargeBinary: LargeBinary,
}


Expand Down
1 change: 1 addition & 0 deletions src/sqlalchemy_cratedb/type/__init__.py
Original file line number Diff line number Diff line change
@@ -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
44 changes: 44 additions & 0 deletions src/sqlalchemy_cratedb/type/binary.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 2a34862

Please sign in to comment.