Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPARK-47781][SQL] Handle negative scale decimals for JDBC data sources #45956

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class MySQLIntegrationSuite extends DockerJDBCIntegrationSuite {
assert(row.getInt(3) == 77777)
assert(row.getInt(4) == 123456789)
assert(row.getLong(5) == 123456789012345L)
val bd = new BigDecimal("123456789012345.12345678901234500000")
val bd = new BigDecimal("123456789012345.123456789012345000")
assert(row.getAs[BigDecimal](6).equals(bd))
assert(row.getFloat(7) == 42.75)
assert(row.getDouble(8) == 1.0000000000000002)
Expand Down Expand Up @@ -173,7 +173,7 @@ class MySQLIntegrationSuite extends DockerJDBCIntegrationSuite {
assert(rows.getInt(2) === 16777215)
assert(rows.getLong(3) === 4294967295L)
assert(rows.getAs[BigDecimal](4).equals(new BigDecimal("9223372036854775808")))
assert(rows.getAs[BigDecimal](5).equals(new BigDecimal("123456789012345.12345678901234500000")))
assert(rows.getAs[BigDecimal](5).equals(new BigDecimal("123456789012345.123456789012345000")))
assert(rows.getDouble(6) === 1.0000000000000002)
if (isMaria) {
assert(rows.getBoolean(7) === false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ class OracleIntegrationSuite extends DockerJDBCIntegrationSuite with SharedSpark
""".stripMargin.replaceAll("\n", " "))


conn.prepareStatement("CREATE TABLE numerics (b DECIMAL(1), f DECIMAL(3, 2), i DECIMAL(10))")
conn.prepareStatement("CREATE TABLE numerics (b DECIMAL(1), f DECIMAL(3, 2), i DECIMAL(10)," +
"n NUMBER(7,-2))")
.executeUpdate()
conn.prepareStatement(
"INSERT INTO numerics VALUES (4, 1.23, 9999999999)").executeUpdate()
"INSERT INTO numerics VALUES (4, 1.23, 9999999999, 7456123.89)").executeUpdate()
conn.commit()

conn.prepareStatement("CREATE TABLE oracle_types (d BINARY_DOUBLE, f BINARY_FLOAT)")
Expand Down Expand Up @@ -159,18 +160,13 @@ class OracleIntegrationSuite extends DockerJDBCIntegrationSuite with SharedSpark
}

test("SPARK-16625 : Importing Oracle numeric types") {
val df = sqlContext.read.jdbc(jdbcUrl, "numerics", new Properties)
val rows = df.collect()
assert(rows.length == 1)
val row = rows(0)
// The main point of the below assertions is not to make sure that these Oracle types are
// mapped to decimal types, but to make sure that the returned values are correct.
// A value > 1 from DECIMAL(1) is correct:
assert(row.getDecimal(0).compareTo(BigDecimal.valueOf(4)) == 0)
// A value with fractions from DECIMAL(3, 2) is correct:
assert(row.getDecimal(1).compareTo(BigDecimal.valueOf(1.23)) == 0)
// A value > Int.MaxValue from DECIMAL(10) is correct:
assert(row.getDecimal(2).compareTo(BigDecimal.valueOf(9999999999L)) == 0)
Seq("true", "false").foreach { flag =>
withSQLConf((SQLConf.LEGACY_ALLOW_NEGATIVE_SCALE_OF_DECIMAL_ENABLED.key, flag)) {
val df = sqlContext.read.jdbc(jdbcUrl, "numerics", new Properties)
checkAnswer(df, Seq(Row(BigDecimal.valueOf(4), BigDecimal.valueOf(1.23),
BigDecimal.valueOf(9999999999L), BigDecimal.valueOf(7456100))))
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ object DecimalPrecision extends TypeCoercionRule {
def widerDecimalType(p1: Int, s1: Int, p2: Int, s2: Int): DecimalType = {
val scale = max(s1, s2)
val range = max(p1 - s1, p2 - s2)
bounded(range + scale, scale)
}

def bounded(precision: Int, scale: Int): DecimalType = {
if (conf.getConf(SQLConf.LEGACY_RETAIN_FRACTION_DIGITS_FIRST)) {
DecimalType.bounded(range + scale, scale)
DecimalType.bounded(precision, scale)
} else {
DecimalType.boundedPreferIntegralDigits(range + scale, scale)
DecimalType.boundedPreferIntegralDigits(precision, scale)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import org.apache.spark.executor.InputMetrics
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, Row}
import org.apache.spark.sql.catalyst.{InternalRow, SQLConfHelper}
import org.apache.spark.sql.catalyst.analysis.Resolver
import org.apache.spark.sql.catalyst.analysis.{DecimalPrecision, Resolver}
import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
import org.apache.spark.sql.catalyst.expressions.SpecificInternalRow
import org.apache.spark.sql.catalyst.parser.CatalystSqlParser
Expand Down Expand Up @@ -196,9 +196,13 @@ object JdbcUtils extends Logging with SQLConfHelper {
case java.sql.Types.CHAR => CharType(precision)
case java.sql.Types.CLOB => StringType
case java.sql.Types.DATE => DateType
case java.sql.Types.DECIMAL if precision != 0 || scale != 0 =>
DecimalType.bounded(precision, scale)
case java.sql.Types.DECIMAL => DecimalType.SYSTEM_DEFAULT
case java.sql.Types.DECIMAL | java.sql.Types.NUMERIC if precision != 0 || scale != 0 =>
if (scale < 0) {
DecimalPrecision.bounded(precision - scale, 0)
} else {
DecimalPrecision.bounded(precision, scale)
}
case java.sql.Types.DECIMAL | java.sql.Types.NUMERIC => DecimalType.SYSTEM_DEFAULT
case java.sql.Types.DOUBLE => DoubleType
case java.sql.Types.FLOAT => FloatType
case java.sql.Types.INTEGER => if (signed) IntegerType else LongType
Expand All @@ -207,9 +211,6 @@ object JdbcUtils extends Logging with SQLConfHelper {
case java.sql.Types.LONGVARCHAR => StringType
case java.sql.Types.NCHAR => StringType
case java.sql.Types.NCLOB => StringType
case java.sql.Types.NUMERIC if precision != 0 || scale != 0 =>
DecimalType.bounded(precision, scale)
case java.sql.Types.NUMERIC => DecimalType.SYSTEM_DEFAULT
case java.sql.Types.NVARCHAR => StringType
case java.sql.Types.REAL => DoubleType
case java.sql.Types.REF => StringType
Expand Down