Skip to content

[Bug] format_string prints the unscaled value for %s of a DECIMAL and strips trailing zeros for %g #39

Open

Description

@comphead

Describe the bug

Spark's format_string hands its arguments to Java's java.util.Formatter (stringExpressions.scala#L2045). Two behaviors of datafusion-spark differ from that:

  1. %s with a DECIMAL argument prints the unscaled integer. Spark prints the decimal with its scale.
  2. %g and %G drop trailing zeros like C does. Java keeps the full precision, which is six significant digits by default.

To Reproduce

Spark 4.0.0:

SELECT format_string('String: %s', CAST(123.456 AS DECIMAL(10,3)))
-- String: 123.456
SELECT format_string('%s', CAST(0.5 AS DECIMAL(10,2)))
-- 0.50
SELECT format_string('Compact: %g', 1234.5D)
-- Compact: 1234.50
SELECT format_string('%g', 0.0001D)
-- 0.000100000

A literal such as 1234.5 is a DECIMAL in Spark and makes %g throw (see below), so the examples use 1234.5D.

DataFusion, existing tests:

The 0.0001 example was not run on DataFusion.

Expected behavior

Print a DECIMAL argument of %s with its scale, keep trailing zeros for %g and %G like Formatter, and update the expectations above.

Related

Additional context

Found by running the queries in datafusion/sqllogictest/test_files/spark on a local Spark and comparing the results with the expected values in the files, which is the kind of check proposed in #7. Spark results come from Apple's build of Spark 4.0.0 (4.0.0.65-apple) with a UTC session and spark.sql.ansi.enabled=false unless stated. The Spark source links point at the v4.0.0 tag. DataFusion results are the sqllogictest expectations at 9dec77584, which CI enforces. Anything marked as not run was derived from reading the code.

Activity

  1. andygrove commented on Nov 6, 2026

    @andygrove
    Member

    Running format_string.slt on Spark 4.2.0 with the runner in apache/datafusion#27165 found these queries, which raise an error in DataFusion and return a value in Spark:

    SELECT format_string('Bool: %6b', 1)       -- Bool:   true
    SELECT format_string(1)                    -- 1
    SELECT format_string('Hash: %h', 'test')   -- Hash: ec06e15a
    SELECT format_string('Hash: %H', 'test')   -- Hash: EC06E15A
    SELECT format_string('Hash: %10h', 'test') -- Hash:   ec06e15a

    Java's %b prints true for any non-null argument that is not a boolean, and %h prints the argument's hash code, here the hash of Spark's internal UTF8String.

    It also happens the other way round. Spark passes its internal values to java.util.Formatter (stringExpressions.scala#L2086), so %f, %e, %g and %a fail for a DECIMAL argument with f != org.apache.spark.sql.types.Decimal. The tests pass literals such as 1.5, which are DECIMAL in Spark and DOUBLE in DataFusion (#27185), so 61 of them raise this error in Spark.

    Every Spark version from 3.4 to 4.2 passes internal values to java.util.Formatter the same way, so none of this, nor the %t behavior in #14, depends on the Spark version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions