Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog

## Unreleased
- BREAKING: Types: The generic `Float` without a precision now compiles to
`DOUBLE` instead of `FLOAT`, CrateDB's 4-byte `REAL`, which rounded Python
floats to 24 bits. This matches SQLAlchemy's documented behaviour on
backends that distinguish the two. `Float(precision<=24)`, `REAL` and the
uppercase `FLOAT` type still render 4-byte columns
- Types: Fixed `CLOB`, `NCHAR`, `NVARCHAR`, `DATETIME`, and `DATE` compiling to
type names CrateDB cannot parse. They now map to `STRING`, `CHAR`, `VARCHAR`,
and `TIMESTAMP` respectively, matching their generic lower-case counterparts
Expand Down
14 changes: 14 additions & 0 deletions src/sqlalchemy_cratedb/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,20 @@ def visit_FLOAT(self, type_, **kw):
return "DOUBLE"
return "FLOAT"

def visit_float(self, type_, **kw):
"""
The generic `Float` without a precision is an 8-byte `DOUBLE`.

CrateDB's `FLOAT` is the 4-byte `REAL`, which rounds Python floats to
24 bits of precision. SQLAlchemy documents a precision-less `Float` as
8-byte on backends that distinguish the two, such as PostgreSQL and
MSSQL. A 4-byte column is still available through `Float(precision=24)`
or lower, `REAL`, or the uppercase `FLOAT` type.
"""
if type_.precision is None:
return "DOUBLE"
return self.visit_FLOAT(type_, **kw)

def visit_JSON(self, type_, **kw):
return "OBJECT"

Expand Down
25 changes: 25 additions & 0 deletions tests/compiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
from unittest import TestCase, mock, skipIf
from unittest.mock import MagicMock, patch

import pytest
import sqlalchemy as sa
from crate.client.cursor import Cursor
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.sql import Update, text

from sqlalchemy_cratedb.compiler import crate_before_execute
from sqlalchemy_cratedb.dialect import CrateDialect
from tests.settings import crate_host
from tests.util import ExtraAssertions

Expand Down Expand Up @@ -605,3 +607,26 @@ def test_ddl_with_jsonb_columns(self):

"""),
)


FLOAT_DDL_CASES = [
(sa.Float(), "DOUBLE"),
(sa.Float(asdecimal=True), "DOUBLE"),
(sa.Float(precision=53), "DOUBLE"),
(sa.Float(precision=25), "DOUBLE"),
(sa.Float(precision=24), "FLOAT"),
(sa.Float(precision=10), "FLOAT"),
(sa.FLOAT(), "FLOAT"),
(sa.REAL(), "REAL"),
]
# `sa.Double` is only available on SQLAlchemy>=2.0.
if hasattr(sa, "Double"):
FLOAT_DDL_CASES.append((sa.Double(), "DOUBLE"))


@pytest.mark.parametrize("type_,expected", FLOAT_DDL_CASES)
def test_float_ddl(type_, expected):
"""
A precision-less generic `Float` is 8-byte; 4-byte types stay available.
"""
assert type_.compile(dialect=CrateDialect()) == expected
2 changes: 1 addition & 1 deletion tests/create_table_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class User(self.Base):
"\n\tshort_col SHORT, "
"\n\tdatetime_col TIMESTAMP WITHOUT TIME ZONE, "
"\n\tdate_col TIMESTAMP, "
"\n\tfloat_col FLOAT, "
"\n\tfloat_col DOUBLE, "
"\n\tdecimal_col NUMERIC(10, 2), "
"\n\tPRIMARY KEY (string_col)\n)\n\n"
),
Expand Down