I'm working with DuckDB and have several client-provided SQL expressions that use DECIMAL(38,10) columns (fixed precision with 10 digits after the decimal point).
For example:
SELECT S1__AMOUNT * S1__PRICE * S1__UNITS * 1000
All columns like S1__AMOUNT, S1__PRICE, etc. are DECIMAL(38,10).
When multiplying several of these columns (especially three or more) and then multiplying by a constant (e.g. * 1000), I get:
duckdb.duckdb.OutOfRangeException: Out of Range Error:
Overflow in multiplication of DECIMAL(38) (194586756000000000000000000000000000 * 1000).
You might want to add an explicit cast to a decimal with a smaller scale.
Limitations:
I can not cast to DOUBLE, but after each operation it is allowed to cast its result to decimal(38,10)
I know that I can manually rewrite SQL clauses to cast after each step like:
CAST(
CAST(
CAST(S1__AMOUNT * S1__PRICE AS DECIMAL(38,10))
* S1__UNITS AS DECIMAL(38,10)
)
* 1000 AS DECIMAL(38,10)
)
But since SQL clauses are written by client and can be pretty random I do not want to use this clumsy and bug prone way.
The question:
Is there any way to configure DuckDB so that it:
- Automatically reduces intermediate decimal scale/precision if needed to fit inside
DECIMAL(38,10), without throwing an OutOfRangeException, or - Automatically casts intermediate arithmetic results back to a safe
DECIMAL(38,10), or - Provides an expression/function to safely multiply with overflow-safe decimal promotion?
If not, is the only reliable approach to rewrite all expressions and insert explicit casts after every multiplication/division?
Exact code to reproduce the problem:
import duckdb
import polars as pl
df = pl.DataFrame({"AMOUNT": 8760, "PRICE": 22.2131, "RATE": 1})
df = df.cast(pl.Decimal(scale=10))
result = duckdb.sql("""
FROM df
SELECT AMOUNT * PRICE * RATE * 1000
""").pl()
print(result)

doubleis smaller thandecimal. It only seems to work because it starts discarding the least significant digits. The value you're using,194586756000000000000000000000000000, has 36 digits while the type you specified accepts 28. Had you used egdecimal(38)you'd have no problem with that, but*1000would still exceed this..pl()- I just inlined an input Polars dataframe instead of a parquet file. It produces the same exception for me, but you may want to double check and amend if necessary.