Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Changelog
- Types: Added support for BLOB type, per base64 encoding

## 2026/05/28 0.42.0
- Added support for SQL Alchemy 2.1
Expand Down
3 changes: 3 additions & 0 deletions src/sqlalchemy_cratedb/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ def visit_TIMESTAMP(self, type_, **kw):
"""
return "TIMESTAMP %s" % ((type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE",)

def visit_BLOB(self, type_, **kw):
return "STRING"


class CrateCompiler(compiler.SQLCompiler):
def visit_getitem_binary(self, binary, operator, **kw):
Expand Down
2 changes: 2 additions & 0 deletions src/sqlalchemy_cratedb/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
)
from .sa_version import SA_1_4, SA_2_0, SA_VERSION
from .type import FloatVector, ObjectArray, ObjectType
from .type.binary import LargeBinary
from .util import SSLMode

TYPES_MAP = {
Expand Down Expand Up @@ -161,6 +162,7 @@ def process(value):
sqltypes.Date: Date,
sqltypes.DateTime: DateTime,
sqltypes.TIMESTAMP: DateTime,
sqltypes.LargeBinary: LargeBinary,
}

if SA_VERSION >= SA_2_0:
Expand Down
2 changes: 2 additions & 0 deletions src/sqlalchemy_cratedb/type/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from .array import ObjectArray
from .binary import LargeBinary
from .geo import Geopoint, Geoshape
from .object import ObjectType
from .vector import FloatVector, knn_match

__all__ = [
Geopoint,
Geoshape,
LargeBinary,
ObjectArray,
ObjectType,
FloatVector,
Expand Down
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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we also mention CrateDB' BLOB type here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CrateDB does not provide a BLOB type, does it?

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or activated, to be more standards-compliant. Let's investigate within another iteration before getting things wrong. Thanks!


def process(value):
if value is not None:
# TODO: return DBAPIBinary(value)
return base64.b64encode(value).decode()
else:
return None

return process
Comment on lines +17 to +30
Copy link
Copy Markdown
Contributor Author

@amotl amotl Dec 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self, or others who want to pick this up:

Review that detail about returning a DBAPIBinary, or not.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amotl Did you review it?

Copy link
Copy Markdown
Contributor Author

@amotl amotl Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I just staged the patch directly from its origin, where the code is in use and works well. However, it can still be wrong from a generic perspective.

Let's toggle this patch back into draft mode: Better safe than sorry, thanks! The other commit 465b55b has been removed here and diverged to a separate patch.


# 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
Comment on lines +32 to +44
Copy link
Copy Markdown
Contributor Author

@amotl amotl Dec 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self, or others who want to pick this up:

Review that detail about dialect.returns_native_bytes: Should it be handled differently, because, well, base64.b64decode actually returns native bytes already?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amotl Did you review it?