Store NUMERIC columns as NUMERIC - #300
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
2bc3cec to
6206a9e
Compare
|
Hi @florinutz, Could you fix conflicts? |
6206a9e to
2571ad4
Compare
bgunebakan
left a comment
There was a problem hiding this comment.
Thanks, Added some comments.
| @reflection.cache | ||
| def get_columns(self, connection, table_name, schema=None, **kw): | ||
| query = ( | ||
| "SELECT column_name, data_type " |
There was a problem hiding this comment.
get_columns selects only column_name, data_type, so a reflected column arrives with no precision. That gap was exist before this PR, but it was harmless while NUMERIC mapped to LONG, now that a precision-less NUMERIC raise a CompileError.
The declared precision and scale carry into DDL and casts, where the types previously compiled to `LONG` and `DOUBLE` and lost them. A column declared without a precision raises `CompileError`, since CrateDB only stores the type with one; storing it requires CrateDB 5.9 or later. Bound `Decimal` values reach the driver unconverted, which serializes them as strings, storing every digit. Reflection maps `numeric` and `numeric_array`, and `double` keeps a renderable type before SQLAlchemy 2.0's `DOUBLE`.
779aa6e to
df00229
Compare
bgunebakan
left a comment
There was a problem hiding this comment.
Thanks! I checked my review comments with latest changes, and checked my get_columns comment with real crate container. it raises CompileError: CrateDB stores a NUMERIC column only with a precision now. That was already broken on main, so it didn't introduced by this PR. I think would be nice to have follow up issue for that to fully implement, if you don't prefer to fix here.
indeed. Reported the issue. |
sa.Numeric(10, 2)created aBIGINTcolumn.Decimal("1.25")went in andDecimal("1.00")came back, with no error at any layer — SQLAlchemy re-applies the declared scale on read, so the truncated integer returned looking like a plausible decimal.Closes #292. Also delivers the write half of #163 — see the note on reads below.
Root cause
Two constants in
CrateTypeCompiler, from 2018 when CrateDB had noNUMERICand mapping elsewhere was the only way to make the column creatable:Both discard precision and scale. CrateDB gained
NUMERICas a cast target in 4.4.0 and storage for it in 5.9.0; the mapping was never revisited, anddocs/data-types.rstalready documentsNUMERICas the intended mapping.The dialect also leaves
supports_native_decimalatFalse, so SQLAlchemy binds everyDecimalthroughprocessors.to_floatbefore the driver sees it. On aNUMERIC(38, 24)column:Fix
Render
NUMERIC(precision, scale)in DDL and casts.NUMERICandDECIMALare the same type in SQLAlchemy and in CrateDB, sovisit_DECIMALdefers tovisit_NUMERIC.Bind
Decimalvalues unconverted, via aNumericentry incolspecs; the driver already serializes them as strings, so CrateDB stores every digit. This stops short ofsupports_native_decimal = True: that flag would also tell SQLAlchemy to skip result conversion, and since this driver returns floats,Numeric(asdecimal=True)would hand back floats.Two points worth a maintainer's opinion:
CompileError: CrateDB rejects a bareNUMERICcolumn, so there is nothing correct to emit. Casts may still omit it, since the server acceptsCAST(x AS NUMERIC). Emitting bareNUMERICwould only move the refusal to execution time, and keepingLONGwould leave exactly this bug in place for anyone writingsa.Numeric().Numericcolumns; older servers get a working (if lossy)BIGINTtoday and would start seeing server errors. The repository documents no supported-version floor, so I stated the requirement inCHANGES.mdand the type map instead. Say the word if you'd rather it degraded on older servers.Reads are unchanged: responses are parsed as JSON, whose numbers become floats, so a returned value carries at most a double's digits. The server sends the full value, so this is fixable in
crate-pythonrather than here; crate/crate-python#826 carries the measurement.Tests
tests/numeric_test.py— DDL and cast rendering for all three spellings, precision without scale, a live round-trip through each spelling, and a live write of a value with more digits than a double holds.tests/create_table_test.py— the basic-types table now declares precision on its exact-numeric columns and expectsNUMERIC(10, 2).