From 7a383990b049877be4cdab39a51b19c9b7e2a3bd Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:15:23 +0000 Subject: [PATCH 01/95] Translation obligations: batch 1, arithmetic operators and numeric functions D151: per-form translation obligations for the 45 translatable numeric forms (7 arithmetic operators, 38 numeric functions), 291 obligations, each met by one case with inline data under cases/translation: - type_combination 148: the argument types each page documents (integer NUMBER, NUMBER with a scale, FLOAT, DECFLOAT, text where the page converts it), never what a conversion rule claims; - null_argument 65: NULL in each argument; - boundary 57: magnitude, negative, rounding_scale and empty_string; - error_parity 21: documented errors, such as LOG with base 1, a rounding_mode on FLOAT and division by zero. Each obligation records the wrong behaviour its case catches (D153) and the page text it reads. Inputs are asymmetric so a wrong translation differs: truncating vs floor modulo, half-away vs half-even rounding, swapped arguments, integer vs decimal division. No case has a Snowflake result yet. --- .../expressions/operators-arithmetic.add.toml | 132 + .../operators-arithmetic.divide.toml | 133 + .../operators-arithmetic.modulo.toml | 109 + .../operators-arithmetic.multiply.toml | 132 + .../operators-arithmetic.subtract.toml | 132 + .../operators-arithmetic.unary-minus.toml | 74 + .../operators-arithmetic.unary-plus.toml | 73 + cases/translation/functions/abs.toml | 65 + cases/translation/functions/acos.toml | 52 + cases/translation/functions/acosh.toml | 51 + cases/translation/functions/asin.toml | 51 + cases/translation/functions/asinh.toml | 51 + cases/translation/functions/atan.toml | 51 + cases/translation/functions/atan2.toml | 98 + cases/translation/functions/atanh.toml | 51 + cases/translation/functions/cbrt.toml | 63 + cases/translation/functions/ceil.toml | 102 + cases/translation/functions/cos.toml | 52 + cases/translation/functions/cosh.toml | 51 + cases/translation/functions/cot.toml | 51 + cases/translation/functions/degrees.toml | 51 + cases/translation/functions/div0.toml | 75 + cases/translation/functions/div0null.toml | 75 + cases/translation/functions/exp.toml | 63 + cases/translation/functions/factorial.toml | 75 + cases/translation/functions/floor.toml | 102 + cases/translation/functions/ln.toml | 74 + cases/translation/functions/log.toml | 119 + cases/translation/functions/mod.toml | 98 + cases/translation/functions/pi.toml | 16 + cases/translation/functions/pow.pow.toml | 102 + cases/translation/functions/pow.power.toml | 102 + cases/translation/functions/radians.toml | 51 + .../functions/round.round-expr-scale.toml | 161 ++ cases/translation/functions/round.round.toml | 150 + cases/translation/functions/sign.toml | 65 + cases/translation/functions/sin.toml | 52 + cases/translation/functions/sinh.toml | 51 + cases/translation/functions/sqrt.toml | 63 + cases/translation/functions/square.toml | 74 + cases/translation/functions/tan.toml | 51 + cases/translation/functions/tanh.toml | 51 + cases/translation/functions/trunc.trunc.toml | 102 + .../translation/functions/trunc.truncate.toml | 102 + cases/translation/functions/width-bucket.toml | 139 + .../translation/obligations/expressions.toml | 729 +++++ .../translation/obligations/functions.toml | 2535 +++++++++++++++++ 47 files changed, 6902 insertions(+) create mode 100644 cases/translation/expressions/operators-arithmetic.add.toml create mode 100644 cases/translation/expressions/operators-arithmetic.divide.toml create mode 100644 cases/translation/expressions/operators-arithmetic.modulo.toml create mode 100644 cases/translation/expressions/operators-arithmetic.multiply.toml create mode 100644 cases/translation/expressions/operators-arithmetic.subtract.toml create mode 100644 cases/translation/expressions/operators-arithmetic.unary-minus.toml create mode 100644 cases/translation/expressions/operators-arithmetic.unary-plus.toml create mode 100644 cases/translation/functions/abs.toml create mode 100644 cases/translation/functions/acos.toml create mode 100644 cases/translation/functions/acosh.toml create mode 100644 cases/translation/functions/asin.toml create mode 100644 cases/translation/functions/asinh.toml create mode 100644 cases/translation/functions/atan.toml create mode 100644 cases/translation/functions/atan2.toml create mode 100644 cases/translation/functions/atanh.toml create mode 100644 cases/translation/functions/cbrt.toml create mode 100644 cases/translation/functions/ceil.toml create mode 100644 cases/translation/functions/cos.toml create mode 100644 cases/translation/functions/cosh.toml create mode 100644 cases/translation/functions/cot.toml create mode 100644 cases/translation/functions/degrees.toml create mode 100644 cases/translation/functions/div0.toml create mode 100644 cases/translation/functions/div0null.toml create mode 100644 cases/translation/functions/exp.toml create mode 100644 cases/translation/functions/factorial.toml create mode 100644 cases/translation/functions/floor.toml create mode 100644 cases/translation/functions/ln.toml create mode 100644 cases/translation/functions/log.toml create mode 100644 cases/translation/functions/mod.toml create mode 100644 cases/translation/functions/pi.toml create mode 100644 cases/translation/functions/pow.pow.toml create mode 100644 cases/translation/functions/pow.power.toml create mode 100644 cases/translation/functions/radians.toml create mode 100644 cases/translation/functions/round.round-expr-scale.toml create mode 100644 cases/translation/functions/round.round.toml create mode 100644 cases/translation/functions/sign.toml create mode 100644 cases/translation/functions/sin.toml create mode 100644 cases/translation/functions/sinh.toml create mode 100644 cases/translation/functions/sqrt.toml create mode 100644 cases/translation/functions/square.toml create mode 100644 cases/translation/functions/tan.toml create mode 100644 cases/translation/functions/tanh.toml create mode 100644 cases/translation/functions/trunc.trunc.toml create mode 100644 cases/translation/functions/trunc.truncate.toml create mode 100644 cases/translation/functions/width-bucket.toml create mode 100644 coverage/translation/obligations/expressions.toml create mode 100644 coverage/translation/obligations/functions.toml diff --git a/cases/translation/expressions/operators-arithmetic.add.toml b/cases/translation/expressions/operators-arithmetic.add.toml new file mode 100644 index 000000000..d55d07235 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.add.toml @@ -0,0 +1,132 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.add" +session_profile = "utc-v1" + +[[case]] +id = "t001-num-num-380380" +description = "a NUMBER(38,0) + b NUMBER(38,0). Catches: operands swapped for subtraction or truncated to 32 bits" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["7", "-12"], + ["40000000000", "3"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-num-num-102380" +description = "a NUMBER(10,2) + b NUMBER(38,0). Catches: the sum losing the NUMBER(10,2) scale (4 instead of 4.25)" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["1.25", "3"], + ["-0.75", "2"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-num-num-102124" +description = "a NUMBER(10,2) + b NUMBER(12,4). Catches: the result scale taken from the left operand only (1.31 instead of 1.3125)" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] +rows = [ + ["1.25", "0.0625"], + ["-3.10", "1.0001"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-num-float-380" +description = "a NUMBER(38,0) + b FLOAT. Catches: a FLOAT operand rounded to the integer side" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["3", "0.5"], + ["-2", "1e-3"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-num-float-102" +description = "a NUMBER(10,2) + b FLOAT. Catches: fixed-point turned into FLOAT before adding or the reverse" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["0.10", "0.2"], + ["2.50", "-0.25"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-float-float-" +description = "a FLOAT + b FLOAT. Catches: float addition replaced by decimal addition (0.30000000000000004 vs 0.3)" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["0.1", "0.2"], + ["1e16", "1"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-null-a" +description = "NULL as a, the other operand set. Catches: NULL treated as 0 instead of propagating" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["NULL", "2.50"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-b" +description = "NULL as b, the other operand set. Catches: NULL treated as 0 instead of propagating" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["2.50", "NULL"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-max-38" +description = "38-digit integers that still fit. Catches: arithmetic done in 64 bits" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["9999999999999999999999999999999999999", "9"], + ["-9999999999999999999999999999999999999", "-9"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-negatives" +description = "Negative fixed-point sums crossing zero. Catches: sign lost or magnitude added" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["-5.25", "2.00"], + ["-0.01", "0.02"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-overflow" +description = "The sum of two 38-digit integers overflows NUMBER(38,0). Catches: wrapping or a silent switch to FLOAT instead of the out-of-range error" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999999", "99999999999999999999999999999999999999"], +] +query = "SELECT a + b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.divide.toml b/cases/translation/expressions/operators-arithmetic.divide.toml new file mode 100644 index 000000000..236974031 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.divide.toml @@ -0,0 +1,133 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.divide" +session_profile = "utc-v1" + +[[case]] +id = "t001-num-num-380380" +description = "a NUMBER(38,0) / b NUMBER(38,0). Catches: integer division (3 instead of 3.5) or a truncated rather than rounded scale-6 quotient (0.666666 vs 0.666667)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["7", "2"], + ["-7", "2"], + ["2", "3"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-num-num-102380" +description = "a NUMBER(10,2) / b NUMBER(38,0). Catches: the quotient scale (s+6) handled wrongly" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["1.25", "4"], + ["-10.00", "3"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-num-num-102124" +description = "a NUMBER(10,2) / b NUMBER(12,4). Catches: quotient scale taken from the divisor" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] +rows = [ + ["1.00", "0.0300"], + ["5.50", "2.2000"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-num-float-380" +description = "a NUMBER(38,0) / b FLOAT. Catches: FLOAT divisor rounded" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1", "3.0"], + ["-5", "0.25"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-num-float-102" +description = "a NUMBER(10,2) / b FLOAT. Catches: fixed-point / FLOAT result type swapped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1.00", "8.0"], + ["3.00", "-0.5"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-float-float-" +description = "a FLOAT / b FLOAT. Catches: float division done in decimal" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1", "3"], + ["-0.5", "4"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-null-a" +description = "NULL as a, the other operand set. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["NULL", "2.00"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-b" +description = "NULL as b, the other operand set. Catches: NULL divisor turned into the division-by-zero error or 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["2.00", "NULL"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-scale-12" +description = "Scales near the 12-digit quotient cap. Catches: the quotient scale not capped at 12" +columns = [{ name = "a", type = "NUMBER(20,8)" }, { name = "b", type = "NUMBER(20,8)" }] +rows = [ + ["1.00000000", "3.00000000"], + ["2.00000000", "7.00000000"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-neg-quotient" +description = "Negative quotients that round at the last digit. Catches: rounding toward zero instead of half away from zero" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["-2", "3"], + ["-1", "6"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-div-zero" +description = "Division by zero is an error. Catches: NULL, 0 or infinity instead of the division-by-zero error" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5", "0"], +] +query = "SELECT a / b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.modulo.toml b/cases/translation/expressions/operators-arithmetic.modulo.toml new file mode 100644 index 000000000..6dd7df4e7 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.modulo.toml @@ -0,0 +1,109 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.modulo" +session_profile = "utc-v1" + +[[case]] +id = "t001-num-num-380380" +description = "a NUMBER(38,0) % b NUMBER(38,0). Catches: floor-based modulo (2 instead of -1): MOD truncates toward zero" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["-7", "3"], + ["7", "-3"], + ["7", "3"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-num-num-102380" +description = "a NUMBER(10,2) % b NUMBER(38,0). Catches: fractional remainder dropped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["7.50", "2"], + ["-7.50", "2"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-num-num-102124" +description = "a NUMBER(10,2) % b NUMBER(12,4). Catches: result scale taken from one operand" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] +rows = [ + ["5.25", "1.1000"], + ["-0.75", "0.5000"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-num-float-380" +description = "a NUMBER(38,0) % b FLOAT. Catches: FLOAT divisor rounded" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["7", "2.5"], + ["-7", "2.5"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-float-float-" +description = "a FLOAT % b FLOAT. Catches: floor-based float modulo" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["5.5", "2"], + ["-5.5", "2"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-null-a" +description = "NULL as a, the other operand set. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["NULL", "3.00"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-null-b" +description = "NULL as b, the other operand set. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["3.00", "NULL"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-both-negative" +description = "Both operands negative. Catches: sign of the remainder taken from the divisor" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["-7", "-3"], + ["-1", "-5"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t009-mod-text" +description = "A non-numeric string operand. Catches: the string silently read as 0 instead of the conversion error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "3"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.multiply.toml b/cases/translation/expressions/operators-arithmetic.multiply.toml new file mode 100644 index 000000000..17473caa9 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.multiply.toml @@ -0,0 +1,132 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.multiply" +session_profile = "utc-v1" + +[[case]] +id = "t001-num-num-380380" +description = "a NUMBER(38,0) * b NUMBER(38,0). Catches: 32-bit multiplication overflowing" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["6", "-7"], + ["3000000000", "4"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-num-num-102380" +description = "a NUMBER(10,2) * b NUMBER(38,0). Catches: the NUMBER(10,2) scale lost" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["1.25", "3"], + ["-0.05", "7"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-num-num-102124" +description = "a NUMBER(10,2) * b NUMBER(12,4). Catches: the product scale capped or taken from one side (1.5625 vs 1.56)" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] +rows = [ + ["1.25", "1.2500"], + ["0.01", "0.0001"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-num-float-380" +description = "a NUMBER(38,0) * b FLOAT. Catches: FLOAT operand truncated" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["3", "0.1"], + ["-4", "2.5"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-num-float-102" +description = "a NUMBER(10,2) * b FLOAT. Catches: fixed-point / FLOAT result type swapped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1.10", "1.1"], + ["2.00", "-0.5"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-float-float-" +description = "a FLOAT * b FLOAT. Catches: underflow to zero handled differently" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["0.1", "3"], + ["1e-200", "1e-200"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-null-a" +description = "NULL as a, the other operand set. Catches: NULL treated as 1 or 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["NULL", "2.00"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-b" +description = "NULL as b, the other operand set. Catches: NULL treated as 1 or 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["2.00", "NULL"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-float-big" +description = "FLOAT products near the double range. Catches: overflow to infinity treated differently" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1e200", "1e100"], + ["-1e300", "1e10"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-scale-sum" +description = "Scales adding past 12 digits. Catches: product scale above Snowflake's cap kept or rounded wrongly" +columns = [{ name = "a", type = "NUMBER(20,10)" }, { name = "b", type = "NUMBER(20,10)" }] +rows = [ + ["1.0000000001", "1.0000000001"], + ["0.3333333333", "3.0000000000"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-overflow" +description = "A product beyond 38 digits. Catches: wrapping or silent FLOAT instead of the out-of-range error" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999999", "10"], +] +query = "SELECT a * b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.subtract.toml b/cases/translation/expressions/operators-arithmetic.subtract.toml new file mode 100644 index 000000000..7c971102a --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.subtract.toml @@ -0,0 +1,132 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.subtract" +session_profile = "utc-v1" + +[[case]] +id = "t001-num-num-380380" +description = "a NUMBER(38,0) - b NUMBER(38,0). Catches: operands in the wrong order (7 instead of -7)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["3", "10"], + ["-4", "7"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-num-num-102380" +description = "a NUMBER(10,2) - b NUMBER(38,0). Catches: scale of the NUMBER(10,2) side dropped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5.75", "8"], + ["-1.25", "-3"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-num-num-102124" +description = "a NUMBER(10,2) - b NUMBER(12,4). Catches: the result scale taken from one operand only" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] +rows = [ + ["1.25", "0.0625"], + ["0.10", "3.3333"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-num-float-380" +description = "a NUMBER(38,0) - b FLOAT. Catches: FLOAT rounded to integer" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1", "0.75"], + ["-3", "0.5"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-num-float-102" +description = "a NUMBER(10,2) - b FLOAT. Catches: fixed-point / FLOAT result type swapped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] +rows = [ + ["0.30", "0.1"], + ["10.00", "0.001"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-float-float-" +description = "a FLOAT - b FLOAT. Catches: float subtraction done in decimal" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["0.3", "0.1"], + ["1e-5", "2e-5"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-date-date" +description = "DATE minus DATE: the page says the result is an integer number of days. Catches: an INTERVAL instead of an integer count of days, or days counted across the leap day wrongly (29, -730)" +columns = [{ name = "a", type = "DATE" }, { name = "b", type = "DATE" }] +rows = [ + ["'2024-03-01'", "'2024-02-01'"], + ["'2023-01-01'", "'2024-12-31'"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008-null-a" +description = "NULL as a, the other operand set. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["NULL", "1.00"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-null-b" +description = "NULL as b, the other operand set. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["1.00", "NULL"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t010-to-negative" +description = "A smaller minus a larger value. Catches: absolute value taken" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["1.50", "4.25"], + ["-2.00", "-0.50"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-overflow" +description = "The difference of the most negative and the largest 38-digit integers overflows. Catches: wrapping instead of the out-of-range error" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["-99999999999999999999999999999999999999", "99999999999999999999999999999999999999"], +] +query = "SELECT a - b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.unary-minus.toml b/cases/translation/expressions/operators-arithmetic.unary-minus.toml new file mode 100644 index 000000000..a32ff8bc6 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.unary-minus.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.unary-minus" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "Negating integers. Catches: the sign dropped or the value doubled" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["5"], + ["-12"], + ["0"], +] +query = "SELECT -a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "Negating NUMBER(10,2). Catches: the scale lost (-1 instead of -1.25)" +columns = [{ name = "a", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["-0.05"], +] +query = "SELECT -a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "Negating FLOAT. Catches: negative zero or exponent handling" +columns = [{ name = "a", type = "FLOAT" }] +rows = [ + ["1.5e-10"], + ["-2.25"], +] +query = "SELECT -a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "Negating NULL. Catches: NULL turned into 0" +columns = [{ name = "a", type = "NUMBER(10,2)" }] +rows = [ + ["NULL"], +] +query = "SELECT -a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-max" +description = "Negating the largest 38-digit integer. Catches: a 64-bit negation that overflows" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999999"], + ["-99999999999999999999999999999999999999"], +] +query = "SELECT -a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t006-double-neg" +description = "Negation of a negation and of a subtraction. Catches: precedence: - a - b read as -(a - b)" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["1.50", "2.00"], +] +query = "SELECT - a - b AS r, - (- a) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/expressions/operators-arithmetic.unary-plus.toml b/cases/translation/expressions/operators-arithmetic.unary-plus.toml new file mode 100644 index 000000000..7821cb9c6 --- /dev/null +++ b/cases/translation/expressions/operators-arithmetic.unary-plus.toml @@ -0,0 +1,73 @@ +category = "translation" +construct = "sf.expressions.operators-arithmetic.unary-plus" +session_profile = "utc-v1" + +[[case]] +id = "t001-text" +description = "Unary plus on numeric strings converts them. Catches: the string returned unchanged, so ordering and arithmetic stay textual" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'12.50'"], + ["'-3'"], + ["'9'"], +] +query = "SELECT +a + 1 AS r FROM input ORDER BY +a" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "Unary plus keeps a NUMBER(10,2) as it is. Catches: a cast that changes the scale" +columns = [{ name = "a", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["-0.50"], +] +query = "SELECT +a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "Unary plus on FLOAT. Catches: conversion to fixed-point" +columns = [{ name = "a", type = "FLOAT" }] +rows = [ + ["0.1"], + ["-1e300"], +] +query = "SELECT +a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "Unary plus on NULL text. Catches: NULL replaced or converted to 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], +] +query = "SELECT +a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-empty" +description = "Unary plus on an empty string. Catches: the empty string read as 0 when Snowflake errors, or the reverse" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT +a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t006-bad-text" +description = "Unary plus on a non-numeric string is an error. Catches: the string passed through unconverted" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'1a'"], +] +query = "SELECT +a AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/abs.toml b/cases/translation/functions/abs.toml new file mode 100644 index 000000000..3aa1c2a5d --- /dev/null +++ b/cases/translation/functions/abs.toml @@ -0,0 +1,65 @@ +category = "translation" +construct = "sf.functions.abs" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "ABS of integers. Catches: the sign kept (ABS as identity) or SIGN computed" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["-7"], + ["0"], + ["12"], +] +query = "SELECT ABS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "ABS of NUMBER(10,2), keeping its type. Catches: the scale lost or FLOAT returned" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.50"], + ["-0.01"], + ["3.25"], +] +query = "SELECT ABS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "ABS of FLOAT. Catches: negative zero and tiny negatives" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["-0.0"], + ["-1e-300"], + ["2.5"], +] +query = "SELECT ABS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "ABS of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["NULL"], +] +query = "SELECT ABS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-extremes" +description = "The 38-digit and double extremes. Catches: 64-bit arithmetic overflowing" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-99999999999999999999999999999999999999", "-1e308"], + ["99999999999999999999999999999999999999", "1e-320"], +] +query = "SELECT ABS(x) AS r, ABS(y) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/acos.toml b/cases/translation/functions/acos.toml new file mode 100644 index 000000000..91955ea5d --- /dev/null +++ b/cases/translation/functions/acos.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.acos" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ACOS of FLOAT values. Catches: ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.25"], + ["-0.6"], + ["1.0"], + ["-1.0"], +] +query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ACOS of NUMBER(12,4), converted to FLOAT. Catches: ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-0.1250"], +] +query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ACOS of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-edges" +description = "The domain edges, 1 and -1. Catches: pi returned for 1 or 0 for -1 (sign of the edge)" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["-1"], +] +query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/acosh.toml b/cases/translation/functions/acosh.toml new file mode 100644 index 000000000..42a474d33 --- /dev/null +++ b/cases/translation/functions/acosh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.acosh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ACOSH of FLOAT values. Catches: ASINH computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["2.5"], + ["10"], +] +query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ACOSH of NUMBER(12,4), converted to FLOAT. Catches: ASINH computed; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["1.5000"], + ["3.0000"], +] +query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ACOSH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-edge" +description = "At 1, the domain edge, and large values. Catches: edge returned as NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["1e300"], +] +query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/asin.toml b/cases/translation/functions/asin.toml new file mode 100644 index 000000000..1cc22329d --- /dev/null +++ b/cases/translation/functions/asin.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.asin" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ASIN of FLOAT values. Catches: ACOS computed instead, or degrees returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.25"], + ["-0.6"], + ["1.0"], +] +query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ASIN of NUMBER(12,4), converted to FLOAT. Catches: ACOS computed instead, or degrees returned; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-0.1250"], +] +query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ASIN of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-odd" +description = "Odd symmetry at negatives. Catches: absolute value taken" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["-0.5"], + ["-1"], +] +query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/asinh.toml b/cases/translation/functions/asinh.toml new file mode 100644 index 000000000..46f98bd63 --- /dev/null +++ b/cases/translation/functions/asinh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.asinh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ASINH of FLOAT values. Catches: ACOSH computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["-2.5"], + ["0"], +] +query = "SELECT ASINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ASINH of NUMBER(12,4), converted to FLOAT. Catches: ACOSH computed; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-3.0000"], +] +query = "SELECT ASINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ASINH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ASINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large arguments. Catches: precision loss in the log formula" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e300"], + ["-1e300"], +] +query = "SELECT ASINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/atan.toml b/cases/translation/functions/atan.toml new file mode 100644 index 000000000..857d55610 --- /dev/null +++ b/cases/translation/functions/atan.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.atan" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ATAN of FLOAT values. Catches: ATAN2 with a swapped argument, or degrees returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["-0.5"], + ["1e10"], +] +query = "SELECT ATAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ATAN of NUMBER(12,4), converted to FLOAT. Catches: ATAN2 with a swapped argument, or degrees returned; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.0000"], + ["-0.2500"], +] +query = "SELECT ATAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ATAN of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ATAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-huge" +description = "Very large arguments approach pi/2. Catches: overflow handling" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e300"], + ["-1e300"], +] +query = "SELECT ATAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/atan2.toml b/cases/translation/functions/atan2.toml new file mode 100644 index 000000000..1292bf403 --- /dev/null +++ b/cases/translation/functions/atan2.toml @@ -0,0 +1,98 @@ +category = "translation" +construct = "sf.functions.atan2" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ATAN2 in all four quadrants. Catches: arguments swapped (ATAN2(y, x) vs ATAN2(x, y)) or ATAN(y/x) losing the quadrant" +columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["1", "2"], + ["-1", "-1"], + ["1", "-1"], + ["0", "-1"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ATAN2 of NUMBER(12,4), converted to DOUBLE. Catches: the result left fixed-point" +columns = [{ name = "y", type = "NUMBER(12,4)" }, { name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["1.0000", "3.0000"], + ["-2.5000", "0.5000"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-text" +description = "ATAN2 of numeric strings, converted to DOUBLE. Catches: the strings rejected" +columns = [{ name = "y", type = "VARCHAR" }, { name = "x", type = "VARCHAR" }] +rows = [ + ["'1.5'", "'-2'"], + ["'-0.5'", "'3'"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null-y" +description = "NULL y. Catches: NULL turned into 0 (giving 0 or pi)" +columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["NULL", "1"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-null-x" +description = "NULL x. Catches: NULL turned into 0 (giving pi/2)" +columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["1", "NULL"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-zeros" +description = "Signed zeros on the negative x axis. Catches: the sign of zero lost (pi vs -pi)" +columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["0", "0"], + ["-0.0", "-1"], + ["0.0", "-1"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t007-bad-text" +description = "A string that is not a number is an error. Catches: the string read as 0" +columns = [{ name = "y", type = "VARCHAR" }, { name = "x", type = "FLOAT" }] +rows = [ + ["'abc'", "1"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t008-boolean" +description = "A BOOLEAN argument is an error (\"any other data type\"). Catches: TRUE read as 1" +columns = [{ name = "y", type = "BOOLEAN" }, { name = "x", type = "FLOAT" }] +rows = [ + ["TRUE", "1"], +] +query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/atanh.toml b/cases/translation/functions/atanh.toml new file mode 100644 index 000000000..85dc3ac62 --- /dev/null +++ b/cases/translation/functions/atanh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.atanh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "ATANH of FLOAT values. Catches: TANH computed, or the odd symmetry lost" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.5"], + ["-0.25"], + ["0"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "ATANH of NUMBER(12,4), converted to FLOAT. Catches: TANH computed, or the odd symmetry lost; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-0.9000"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "ATANH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-near-one" +description = "Close to the domain edges. Catches: precision near the asymptote" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.999999"], + ["-0.999999"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/cbrt.toml b/cases/translation/functions/cbrt.toml new file mode 100644 index 000000000..8207113ce --- /dev/null +++ b/cases/translation/functions/cbrt.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.cbrt" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "CBRT of FLOAT values. Catches: SQRT computed, or POW(x, 1/3) giving NaN for negatives" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["27"], + ["2"], + ["0.125"], +] +query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "CBRT of NUMBER(12,4), converted to FLOAT. Catches: SQRT computed, or POW(x, 1/3) giving NaN for negatives; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["8.0000"], + ["-27.0000"], +] +query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-decfloat" +description = "CBRT of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["27"], + ["-8"], +] +query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "CBRT of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-neg" +description = "Cube roots of negatives are negative. Catches: NaN from a fractional power of a negative" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["-8"], + ["-0.001"], +] +query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/ceil.toml b/cases/translation/functions/ceil.toml new file mode 100644 index 000000000..994cf8b7c --- /dev/null +++ b/cases/translation/functions/ceil.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.ceil" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "CEIL of integers with a negative scale. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a negative scale ignored" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1567", "-2"], + ["-1567", "-2"], + ["5", "0"], +] +query = "SELECT CEIL(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "CEIL of NUMBER(12,4) with and without a scale. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.1000"], + ["-2.1000"], + ["-2.5000"], + ["2.7000"], +] +query = "SELECT CEIL(x) AS r, CEIL(x, 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "CEIL of FLOAT returns FLOAT. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["3.14159"], + ["-3.14159"], + ["-0.4"], +] +query = "SELECT CEIL(x) AS r, CEIL(x, 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["2.25"], + ["-2.25"], +] +query = "SELECT CEIL(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "CEIL of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], +] +query = "SELECT CEIL(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-scale" +description = "CEIL with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT CEIL(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-scale-past" +description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["12.3456", "6"], + ["12.3456", "-5"], + ["-987.6543", "3"], +] +query = "SELECT CEIL(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-negatives" +description = "Negative values at the rounding step. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.15"], + ["-0.01"], +] +query = "SELECT CEIL(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/cos.toml b/cases/translation/functions/cos.toml new file mode 100644 index 000000000..1f9370c56 --- /dev/null +++ b/cases/translation/functions/cos.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.cos" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "COS of FLOAT values. Catches: SIN computed, or the argument read in degrees" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], + ["1"], + ["-2.5"], + ["3.141592653589793"], +] +query = "SELECT COS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "COS of NUMBER(12,4), converted to FLOAT. Catches: SIN computed, or the argument read in degrees; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["1.5000"], + ["-0.7500"], +] +query = "SELECT COS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "COS of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT COS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large arguments. Catches: argument reduction differing" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e6"], + ["-12345.678"], +] +query = "SELECT COS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/cosh.toml b/cases/translation/functions/cosh.toml new file mode 100644 index 000000000..d0d42cdb5 --- /dev/null +++ b/cases/translation/functions/cosh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.cosh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "COSH of FLOAT values. Catches: SINH computed (cosh is even, sinh odd)" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], + ["1"], + ["-2"], +] +query = "SELECT COSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "COSH of NUMBER(12,4), converted to FLOAT. Catches: SINH computed (cosh is even, sinh odd); or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-1.2500"], +] +query = "SELECT COSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "COSH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT COSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-overflow-inf" +description = "Arguments past the double range. Catches: infinity versus error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["710"], + ["-710"], +] +query = "SELECT COSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/cot.toml b/cases/translation/functions/cot.toml new file mode 100644 index 000000000..f83a23ace --- /dev/null +++ b/cases/translation/functions/cot.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.cot" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "COT of FLOAT values. Catches: TAN computed instead of its reciprocal" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.5"], + ["-1"], + ["2"], +] +query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "COT of NUMBER(12,4), converted to FLOAT. Catches: TAN computed instead of its reciprocal; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.7854"], + ["-0.2500"], +] +query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "COT of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-small" +description = "Very small arguments give large cotangents. Catches: reciprocal computed in fixed-point" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e-8"], + ["-1e-8"], +] +query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/degrees.toml b/cases/translation/functions/degrees.toml new file mode 100644 index 000000000..a6526bd05 --- /dev/null +++ b/cases/translation/functions/degrees.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.degrees" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "DEGREES of FLOAT values. Catches: RADIANS computed (the reverse conversion)" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["3.141592653589793"], + ["-1"], + ["0.5"], +] +query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "DEGREES of NUMBER(12,4), converted to FLOAT. Catches: RADIANS computed (the reverse conversion); or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["1.0000"], + ["-0.5000"], +] +query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "DEGREES of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large radian values. Catches: overflow handling" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e300"], + ["-1e305"], +] +query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/div0.toml b/cases/translation/functions/div0.toml new file mode 100644 index 000000000..ecfd9f5f4 --- /dev/null +++ b/cases/translation/functions/div0.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.div0" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "DIV0 of integers, as / divides. Catches: integer division (3 instead of 3.500000)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["7", "2"], + ["-7", "2"], + ["5", "0"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "DIV0 of NUMBER(10,2) values. Catches: quotient scale differing from /" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["1.00", "3.00"], + ["-2.50", "0.00"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "DIV0 of FLOAT values. Catches: infinity or NaN for a zero divisor instead of 0" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1", "3"], + ["-1", "0"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null-a" +description = "NULL dividend. Catches: NULL turned into 0" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "2"], + ["NULL", "0"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-null-b" +description = "NULL divisor. Catches: a NULL divisor returning 0 (that is DIV0NULL)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5", "NULL"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-neg-zero" +description = "Negative dividends over zero. Catches: -0 or a negative zero result" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["-5.00", "0.00"], + ["-0.01", "3.00"], +] +query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/div0null.toml b/cases/translation/functions/div0null.toml new file mode 100644 index 000000000..5ec13cf71 --- /dev/null +++ b/cases/translation/functions/div0null.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.div0null" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "DIV0NULL of integers, as / divides. Catches: integer division (3 instead of 3.500000)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["7", "2"], + ["-7", "2"], + ["5", "0"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "DIV0NULL of NUMBER(10,2) values. Catches: quotient scale differing from /" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["1.00", "3.00"], + ["-2.50", "0.00"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "DIV0NULL of FLOAT values. Catches: infinity or NaN for a zero divisor instead of 0" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["1", "3"], + ["-1", "0"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null-a" +description = "NULL dividend. Catches: NULL turned into 0" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "2"], + ["NULL", "0"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-null-b" +description = "NULL divisor. Catches: a NULL divisor returning NULL (that is DIV0)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5", "NULL"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-neg-zero" +description = "Negative dividends over zero. Catches: -0 or a negative zero result" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["-5.00", "0.00"], + ["-0.01", "3.00"], +] +query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/exp.toml b/cases/translation/functions/exp.toml new file mode 100644 index 000000000..f7797f53c --- /dev/null +++ b/cases/translation/functions/exp.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.exp" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "EXP of FLOAT values. Catches: LN computed, or e^x read as 10^x" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["-2"], + ["0.5"], +] +query = "SELECT EXP(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "EXP of NUMBER(12,4), converted to FLOAT. Catches: LN computed, or e^x read as 10^x; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.0000"], + ["-0.5000"], +] +query = "SELECT EXP(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-decfloat" +description = "EXP of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["1"], + ["-2.5"], +] +query = "SELECT EXP(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "EXP of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT EXP(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-overflow-inf" +description = "Past the double range. Catches: infinity versus error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["710"], + ["-746"], +] +query = "SELECT EXP(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/factorial.toml b/cases/translation/functions/factorial.toml new file mode 100644 index 000000000..9498d2c82 --- /dev/null +++ b/cases/translation/functions/factorial.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.factorial" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "FACTORIAL of integers. Catches: gamma(x) computed (fractional), or x! off by one (n-1)!" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["0"], + ["1"], + ["5"], + ["10"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec-int" +description = "FACTORIAL of an integral NUMBER(10,2). Catches: scale carried into the result" +columns = [{ name = "n", type = "NUMBER(10,2)" }] +rows = [ + ["6.00"], + ["3.00"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "FACTORIAL of NULL. Catches: NULL turned into 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large factorials that still fit 38 digits. Catches: 64-bit overflow (21! does not fit a BIGINT)" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["20"], + ["21"], + ["33"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t005-negative" +description = "A negative argument. Catches: a gamma value or 1 instead of the error" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["-1"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t006-too-big" +description = "A result past 38 digits. Catches: FLOAT or wrapping instead of the error" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["34"], +] +query = "SELECT FACTORIAL(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/floor.toml b/cases/translation/functions/floor.toml new file mode 100644 index 000000000..8ce566704 --- /dev/null +++ b/cases/translation/functions/floor.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.floor" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "FLOOR of integers with a negative scale. Catches: CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1567", "-2"], + ["-1567", "-2"], + ["5", "0"], +] +query = "SELECT FLOOR(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "FLOOR of NUMBER(12,4) with and without a scale. Catches: CEIL or truncation used (FLOOR(-2.1) is -3)" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.1000"], + ["-2.1000"], + ["-2.5000"], + ["2.7000"], +] +query = "SELECT FLOOR(x) AS r, FLOOR(x, 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "FLOOR of FLOAT returns FLOAT. Catches: CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["3.14159"], + ["-3.14159"], + ["-0.4"], +] +query = "SELECT FLOOR(x) AS r, FLOOR(x, 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "FLOOR of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["2.25"], + ["-2.25"], +] +query = "SELECT FLOOR(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "FLOOR of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], +] +query = "SELECT FLOOR(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-scale" +description = "FLOOR with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT FLOOR(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-scale-past" +description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["12.3456", "6"], + ["12.3456", "-5"], + ["-987.6543", "3"], +] +query = "SELECT FLOOR(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-negatives" +description = "Negative values at the rounding step. Catches: CEIL or truncation used (FLOOR(-2.1) is -3)" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.15"], + ["-0.01"], +] +query = "SELECT FLOOR(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/ln.toml b/cases/translation/functions/ln.toml new file mode 100644 index 000000000..ec00d03c3 --- /dev/null +++ b/cases/translation/functions/ln.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.functions.ln" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "LN of FLOAT values. Catches: LOG10 or LOG2 computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2.718281828459045"], + ["10"], + ["0.5"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "LN of NUMBER(12,4), converted to FLOAT. Catches: LOG10 or LOG2 computed; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.0000"], + ["0.2500"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-decfloat" +description = "LN of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["10"], + ["0.5"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "LN of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-tiny" +description = "Very small positive values. Catches: underflow to -infinity" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e-300"], + ["5e-324"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t006-zero" +description = "LN of 0 or a negative value is an error. Catches: NULL, NaN or -infinity instead of the error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], +] +query = "SELECT LN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/log.toml b/cases/translation/functions/log.toml new file mode 100644 index 000000000..9ccfe5a4e --- /dev/null +++ b/cases/translation/functions/log.toml @@ -0,0 +1,119 @@ +category = "translation" +construct = "sf.functions.log" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "LOG of integers. Catches: arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" +columns = [{ name = "b", type = "NUMBER(38,0)" }, { name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["2", "8"], + ["10", "1000"], + ["3", "2"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "LOG of fixed-point values. Catches: base rounded to an integer" +columns = [{ name = "b", type = "NUMBER(10,2)" }, { name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["2.50", "6.25"], + ["0.50", "8.00"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "LOG of FLOAT values. Catches: computed in decimal" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["2.718281828459045", "10"], + ["10", "0.001"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-mixed" +description = "LOG with an integer base and a FLOAT value. Catches: type promotion differing" +columns = [{ name = "b", type = "NUMBER(38,0)" }, { name = "x", type = "FLOAT" }] +rows = [ + ["2", "0.25"], + ["10", "12345.678"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-base" +description = "NULL base. Catches: NULL turned into e or 10" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["NULL", "8"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-expr" +description = "NULL value. Catches: NULL turned into 1" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["2", "NULL"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-base-below-one" +description = "A base between 0 and 1 gives negative logs of values above 1. Catches: the sign lost" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["0.5", "8"], + ["0.1", "0.001"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-base-one" +description = "A base of 1 is an error. Catches: infinity or NULL instead of the error" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["1", "8"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t009-base-neg" +description = "A negative base is an error. Catches: NaN instead of the error" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["-2", "8"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t010-expr-zero" +description = "A value of 0 is an error. Catches: -infinity instead of the error" +columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] +rows = [ + ["2", "0"], +] +query = "SELECT LOG(b, x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/mod.toml b/cases/translation/functions/mod.toml new file mode 100644 index 000000000..c0ffb3fca --- /dev/null +++ b/cases/translation/functions/mod.toml @@ -0,0 +1,98 @@ +category = "translation" +construct = "sf.functions.mod" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "MOD of integers with negatives. Catches: floor-based modulo (MOD(-7, 3) is -1, not 2)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["-7", "3"], + ["7", "-3"], + ["17", "5"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "MOD of NUMBER(10,2) values. Catches: fractional remainder dropped" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +rows = [ + ["7.50", "2.00"], + ["-7.50", "2.25"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-dec-int" +description = "MOD of a scaled and an integer operand. Catches: result scale from one side" +columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["10.25", "3"], + ["-0.75", "2"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-float" +description = "MOD of FLOAT values. Catches: floor-based float modulo" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] +rows = [ + ["5.5", "2"], + ["-5.5", "2"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-a" +description = "NULL dividend. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "3"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-b" +description = "NULL divisor. Catches: NULL treated as 0" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["3", "NULL"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-order" +description = "Argument order with negatives. Catches: arguments swapped" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["3", "-7"], + ["-3", "-7"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-max" +description = "Remainders of 38-digit integers. Catches: 64-bit modulo" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999999", "7"], + ["-99999999999999999999999999999999999999", "1000000007"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/pi.toml b/cases/translation/functions/pi.toml new file mode 100644 index 000000000..6f7489558 --- /dev/null +++ b/cases/translation/functions/pi.toml @@ -0,0 +1,16 @@ +category = "translation" +construct = "sf.functions.pi" +session_profile = "utc-v1" + +[[case]] +id = "t001-value" +description = "PI() in arithmetic, at full double precision. Catches: a rounded constant (3.14159) or a NUMBER" +columns = [{ name = "k", type = "NUMBER(38,0)" }] +rows = [ + ["1"], + ["-2"], + ["1000000"], +] +query = "SELECT PI() * k AS r, PI() / 4 AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/pow.pow.toml b/cases/translation/functions/pow.pow.toml new file mode 100644 index 000000000..389c7ba6d --- /dev/null +++ b/cases/translation/functions/pow.pow.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.pow.pow" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "POW of integers returns FLOAT. Catches: arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "NUMBER(38,0)" }] +rows = [ + ["2", "3"], + ["-2", "3"], + ["10", "-2"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "POW with fractional exponents. Catches: the exponent rounded" +columns = [{ name = "x", type = "NUMBER(10,2)" }, { name = "y", type = "NUMBER(10,2)" }] +rows = [ + ["4.00", "0.50"], + ["2.25", "1.50"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "POW of FLOAT values. Catches: computed in decimal" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["1.5", "2.5"], + ["0.5", "-3"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "POW of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }, { name = "y", type = "DECFLOAT" }] +rows = [ + ["1.1", "3"], + ["2", "-1"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "NULL base. Catches: NULL^0 returning 1" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["NULL", "0"], + ["NULL", "2"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-y" +description = "NULL exponent. Catches: 1^NULL returning 1" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["1", "NULL"], + ["2", "NULL"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-overflow-inf" +description = "Powers past the double range, and 0^0. Catches: infinity versus error; 0^0 as 0" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["10", "400"], + ["0", "0"], + ["10", "-400"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-neg-base" +description = "Negative bases with integer exponents. Catches: the sign lost for odd exponents" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-3", "3"], + ["-3", "2"], + ["-0.5", "-1"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/pow.power.toml b/cases/translation/functions/pow.power.toml new file mode 100644 index 000000000..670c8a002 --- /dev/null +++ b/cases/translation/functions/pow.power.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.pow.power" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "POWER of integers returns FLOAT. Catches: arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "NUMBER(38,0)" }] +rows = [ + ["2", "3"], + ["-2", "3"], + ["10", "-2"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "POWER with fractional exponents. Catches: the exponent rounded" +columns = [{ name = "x", type = "NUMBER(10,2)" }, { name = "y", type = "NUMBER(10,2)" }] +rows = [ + ["4.00", "0.50"], + ["2.25", "1.50"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "POWER of FLOAT values. Catches: computed in decimal" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["1.5", "2.5"], + ["0.5", "-3"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "POWER of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }, { name = "y", type = "DECFLOAT" }] +rows = [ + ["1.1", "3"], + ["2", "-1"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "NULL base. Catches: NULL^0 returning 1" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["NULL", "0"], + ["NULL", "2"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-y" +description = "NULL exponent. Catches: 1^NULL returning 1" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["1", "NULL"], + ["2", "NULL"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-overflow-inf" +description = "Powers past the double range, and 0^0. Catches: infinity versus error; 0^0 as 0" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["10", "400"], + ["0", "0"], + ["10", "-400"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-neg-base" +description = "Negative bases with integer exponents. Catches: the sign lost for odd exponents" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-3", "3"], + ["-3", "2"], + ["-0.5", "-1"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/radians.toml b/cases/translation/functions/radians.toml new file mode 100644 index 000000000..40540cd6e --- /dev/null +++ b/cases/translation/functions/radians.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.radians" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "RADIANS of FLOAT values. Catches: DEGREES computed (the reverse conversion)" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["180"], + ["-90"], + ["45.5"], +] +query = "SELECT RADIANS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "RADIANS of NUMBER(12,4), converted to FLOAT. Catches: DEGREES computed (the reverse conversion); or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["360.0000"], + ["-1.0000"], +] +query = "SELECT RADIANS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "RADIANS of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT RADIANS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large degree values. Catches: overflow handling" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e300"], + ["-1e305"], +] +query = "SELECT RADIANS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/round.round-expr-scale.toml b/cases/translation/functions/round.round-expr-scale.toml new file mode 100644 index 000000000..eac20bb95 --- /dev/null +++ b/cases/translation/functions/round.round-expr-scale.toml @@ -0,0 +1,161 @@ +category = "translation" +construct = "sf.functions.round.round-expr-scale" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "ROUND of integers with a negative scale. Catches: a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1550", "-2"], + ["-1550", "-2"], + ["1549", "-2"], +] +query = "SELECT ROUND(EXPR => x, SCALE => s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "ROUND of NUMBER(12,4) halves. Catches: banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["-2.5000"], + ["3.5000"], + ["1.4500"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 0) AS r, ROUND(EXPR => x, SCALE => 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "ROUND of FLOAT halves exact in binary. Catches: half-to-even for FLOAT, or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2.5"], + ["-2.5"], + ["0.125"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 0) AS r, ROUND(EXPR => x, SCALE => 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "ROUND of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["1.005"], + ["-2.5"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 2) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-mode-even" +description = "rounding_mode 'HALF_TO_EVEN' on fixed-point. Catches: the mode ignored (2.5 to 3 instead of 2)" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["3.5000"], + ["-2.5000"], + ["0.0250"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 0, ROUNDING_MODE => 'HALF_TO_EVEN') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-mode-away" +description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["-0.0250"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 1, ROUNDING_MODE => 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-null-x" +description = "ROUND of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["NULL"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-scale" +description = "ROUND with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT ROUND(EXPR => x, SCALE => s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-scale" +description = "Scales past the input scale and a carry across digits. Catches: carry lost (9.995 to 9.99) or scale capped" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["9.9950", "2"], + ["12.3456", "8"], + ["-0.0005", "3"], +] +query = "SELECT ROUND(EXPR => x, SCALE => s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-max" +description = "Rounding the largest 38-digit integers up to a power of ten. Catches: overflow to FLOAT" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999949"], + ["-1234567890123456789012345678901234567"], +] +query = "SELECT ROUND(EXPR => x, SCALE => -1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-mode-float" +description = "A rounding_mode with a FLOAT input is an error. Catches: the mode applied to FLOAT instead of the documented error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2.5"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 0, ROUNDING_MODE => 'HALF_TO_EVEN') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t012-mode-bad" +description = "An unknown rounding_mode is an error. Catches: the mode ignored" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], +] +query = "SELECT ROUND(EXPR => x, SCALE => 0, ROUNDING_MODE => 'HALF_UP') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t013-expr-no-scale" +description = "EXPR => without SCALE => is an error, as the page says. Catches: SCALE defaulted to 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], +] +query = "SELECT ROUND(EXPR => x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/round.round.toml b/cases/translation/functions/round.round.toml new file mode 100644 index 000000000..0a6c64355 --- /dev/null +++ b/cases/translation/functions/round.round.toml @@ -0,0 +1,150 @@ +category = "translation" +construct = "sf.functions.round.round" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "ROUND of integers with a negative scale. Catches: a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1550", "-2"], + ["-1550", "-2"], + ["1549", "-2"], +] +query = "SELECT ROUND(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "ROUND of NUMBER(12,4) halves. Catches: banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["-2.5000"], + ["3.5000"], + ["1.4500"], +] +query = "SELECT ROUND(x, 0) AS r, ROUND(x, 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "ROUND of FLOAT halves exact in binary. Catches: half-to-even for FLOAT, or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2.5"], + ["-2.5"], + ["0.125"], +] +query = "SELECT ROUND(x, 0) AS r, ROUND(x, 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "ROUND of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["1.005"], + ["-2.5"], +] +query = "SELECT ROUND(x, 2) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-mode-even" +description = "rounding_mode 'HALF_TO_EVEN' on fixed-point. Catches: the mode ignored (2.5 to 3 instead of 2)" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["3.5000"], + ["-2.5000"], + ["0.0250"], +] +query = "SELECT ROUND(x, 0, 'HALF_TO_EVEN') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006-mode-away" +description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], + ["-0.0250"], +] +query = "SELECT ROUND(x, 1, 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007-null-x" +description = "ROUND of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["NULL"], +] +query = "SELECT ROUND(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-scale" +description = "ROUND with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT ROUND(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-scale" +description = "Scales past the input scale and a carry across digits. Catches: carry lost (9.995 to 9.99) or scale capped" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["9.9950", "2"], + ["12.3456", "8"], + ["-0.0005", "3"], +] +query = "SELECT ROUND(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-max" +description = "Rounding the largest 38-digit integers up to a power of ten. Catches: overflow to FLOAT" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["99999999999999999999999999999999999949"], + ["-1234567890123456789012345678901234567"], +] +query = "SELECT ROUND(x, -1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t011-mode-float" +description = "A rounding_mode with a FLOAT input is an error. Catches: the mode applied to FLOAT instead of the documented error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2.5"], +] +query = "SELECT ROUND(x, 0, 'HALF_TO_EVEN') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t012-mode-bad" +description = "An unknown rounding_mode is an error. Catches: the mode ignored" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.5000"], +] +query = "SELECT ROUND(x, 0, 'HALF_UP') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/sign.toml b/cases/translation/functions/sign.toml new file mode 100644 index 000000000..5a93ed105 --- /dev/null +++ b/cases/translation/functions/sign.toml @@ -0,0 +1,65 @@ +category = "translation" +construct = "sf.functions.sign" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "SIGN of integers. Catches: ABS computed, or -0.001 rounded to 0 before taking the sign" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["-7"], + ["0"], + ["12"], +] +query = "SELECT SIGN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "SIGN of NUMBER(10,2), keeping its type. Catches: the scale lost or FLOAT returned" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.50"], + ["-0.01"], + ["3.25"], +] +query = "SELECT SIGN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "SIGN of FLOAT. Catches: negative zero and tiny negatives" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["-0.0"], + ["-1e-300"], + ["2.5"], +] +query = "SELECT SIGN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "SIGN of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["NULL"], +] +query = "SELECT SIGN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-extremes" +description = "The 38-digit and double extremes. Catches: 64-bit arithmetic overflowing" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-99999999999999999999999999999999999999", "-1e308"], + ["99999999999999999999999999999999999999", "1e-320"], +] +query = "SELECT SIGN(x) AS r, SIGN(y) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/sin.toml b/cases/translation/functions/sin.toml new file mode 100644 index 000000000..f3233815e --- /dev/null +++ b/cases/translation/functions/sin.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.sin" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "SIN of FLOAT values. Catches: COS computed, or the argument read in degrees" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], + ["1"], + ["-2.5"], + ["3.141592653589793"], +] +query = "SELECT SIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "SIN of NUMBER(12,4), converted to FLOAT. Catches: COS computed, or the argument read in degrees; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["1.5000"], + ["-0.7500"], +] +query = "SELECT SIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "SIN of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT SIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-large" +description = "Large arguments. Catches: argument reduction differing" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e6"], + ["-12345.678"], +] +query = "SELECT SIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/sinh.toml b/cases/translation/functions/sinh.toml new file mode 100644 index 000000000..2f340bc59 --- /dev/null +++ b/cases/translation/functions/sinh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.sinh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "SINH of FLOAT values. Catches: COSH computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], + ["1"], + ["-2"], +] +query = "SELECT SINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "SINH of NUMBER(12,4), converted to FLOAT. Catches: COSH computed; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-1.2500"], +] +query = "SELECT SINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "SINH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT SINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-overflow-inf" +description = "Arguments past the double range. Catches: infinity versus error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["710"], + ["-710"], +] +query = "SELECT SINH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/sqrt.toml b/cases/translation/functions/sqrt.toml new file mode 100644 index 000000000..701721f5b --- /dev/null +++ b/cases/translation/functions/sqrt.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.sqrt" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "SQRT of FLOAT values. Catches: CBRT or squaring computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2"], + ["0.25"], + ["1e10"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "SQRT of NUMBER(12,4), converted to FLOAT. Catches: CBRT or squaring computed; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.0000"], + ["0.0625"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-decfloat" +description = "SQRT of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["2"], + ["0.25"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-null" +description = "SQRT of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005-tiny-huge" +description = "Very small and very large values. Catches: precision at the extremes" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e-300"], + ["1e300"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/square.toml b/cases/translation/functions/square.toml new file mode 100644 index 000000000..ff918ffe7 --- /dev/null +++ b/cases/translation/functions/square.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.functions.square" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "SQUARE of integers returns FLOAT. Catches: x*x kept as NUMBER (the type differs), or SQRT computed" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["-3"], + ["12"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "SQUARE of NUMBER(10,2). Catches: the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.50"], + ["-0.25"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "SQUARE of FLOAT. Catches: SQRT computed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.1"], + ["-2.5"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "SQUARE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["1.1"], + ["-3"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null" +description = "SQUARE of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-overflow-inf" +description = "Squares past the double range. Catches: infinity versus error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e200"], + ["-1e155"], +] +query = "SELECT SQUARE(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/tan.toml b/cases/translation/functions/tan.toml new file mode 100644 index 000000000..461b1e067 --- /dev/null +++ b/cases/translation/functions/tan.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.tan" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "TAN of FLOAT values. Catches: SIN or COT computed, or degrees" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.5"], + ["-1"], + ["1.5"], +] +query = "SELECT TAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "TAN of NUMBER(12,4), converted to FLOAT. Catches: SIN or COT computed, or degrees; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.7854"], + ["-0.2500"], +] +query = "SELECT TAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "TAN of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT TAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-near-pole" +description = "Near pi/2. Catches: pole handling" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1.5707963267948966"], + ["-1.5707"], +] +query = "SELECT TAN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/tanh.toml b/cases/translation/functions/tanh.toml new file mode 100644 index 000000000..1e587bd40 --- /dev/null +++ b/cases/translation/functions/tanh.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.tanh" +session_profile = "utc-v1" + +[[case]] +id = "t001-float" +description = "TANH of FLOAT values. Catches: SINH or a clamped value" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.5"], + ["-1"], + ["20"], +] +query = "SELECT TANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-fixed" +description = "TANH of NUMBER(12,4), converted to FLOAT. Catches: SINH or a clamped value; or the result left fixed-point" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["0.5000"], + ["-2.0000"], +] +query = "SELECT TANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-null" +description = "TANH of NULL. Catches: NULL turned into 0 or NaN" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["NULL"], +] +query = "SELECT TANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004-saturate" +description = "Large arguments saturate to 1 and -1. Catches: NaN from exp overflow" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1000"], + ["-1000"], +] +query = "SELECT TANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/trunc.trunc.toml b/cases/translation/functions/trunc.trunc.toml new file mode 100644 index 000000000..5881d2ca2 --- /dev/null +++ b/cases/translation/functions/trunc.trunc.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.trunc.trunc" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "TRUNC of integers with a negative scale. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a negative scale ignored" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1567", "-2"], + ["-1567", "-2"], + ["5", "0"], +] +query = "SELECT TRUNC(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "TRUNC of NUMBER(12,4) with and without a scale. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.1000"], + ["-2.1000"], + ["-2.5000"], + ["2.7000"], +] +query = "SELECT TRUNC(x) AS r, TRUNC(x, 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "TRUNC of FLOAT returns FLOAT. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["3.14159"], + ["-3.14159"], + ["-0.4"], +] +query = "SELECT TRUNC(x) AS r, TRUNC(x, 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "TRUNC of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["2.25"], + ["-2.25"], +] +query = "SELECT TRUNC(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "TRUNC of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], +] +query = "SELECT TRUNC(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-scale" +description = "TRUNC with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT TRUNC(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-scale-past" +description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["12.3456", "6"], + ["12.3456", "-5"], + ["-987.6543", "3"], +] +query = "SELECT TRUNC(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-negatives" +description = "Negative values at the rounding step. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.19"], + ["-0.09"], +] +query = "SELECT TRUNC(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/trunc.truncate.toml b/cases/translation/functions/trunc.truncate.toml new file mode 100644 index 000000000..744cb3ec3 --- /dev/null +++ b/cases/translation/functions/trunc.truncate.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.trunc.truncate" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "TRUNCATE of integers with a negative scale. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a negative scale ignored" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["1567", "-2"], + ["-1567", "-2"], + ["5", "0"], +] +query = "SELECT TRUNCATE(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "TRUNCATE of NUMBER(12,4) with and without a scale. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2)" +columns = [{ name = "x", type = "NUMBER(12,4)" }] +rows = [ + ["2.1000"], + ["-2.1000"], + ["-2.5000"], + ["2.7000"], +] +query = "SELECT TRUNCATE(x) AS r, TRUNCATE(x, 2) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "TRUNCATE of FLOAT returns FLOAT. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a NUMBER returned" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["3.14159"], + ["-3.14159"], + ["-0.4"], +] +query = "SELECT TRUNCATE(x) AS r, TRUNCATE(x, 1) AS s FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-decfloat" +description = "TRUNCATE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +columns = [{ name = "x", type = "DECFLOAT" }] +rows = [ + ["2.25"], + ["-2.25"], +] +query = "SELECT TRUNCATE(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-x" +description = "TRUNCATE of NULL. Catches: NULL turned into 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], +] +query = "SELECT TRUNCATE(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-scale" +description = "TRUNCATE with a NULL scale. Catches: the NULL scale read as 0" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["2.5555", "NULL"], +] +query = "SELECT TRUNCATE(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-scale-past" +description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" +columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] +rows = [ + ["12.3456", "6"], + ["12.3456", "-5"], + ["-987.6543", "3"], +] +query = "SELECT TRUNCATE(x, s) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t008-negatives" +description = "Negative values at the rounding step. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2)" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["-2.19"], + ["-0.09"], +] +query = "SELECT TRUNCATE(x, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/width-bucket.toml b/cases/translation/functions/width-bucket.toml new file mode 100644 index 000000000..d62fb8931 --- /dev/null +++ b/cases/translation/functions/width-bucket.toml @@ -0,0 +1,139 @@ +category = "translation" +construct = "sf.functions.width-bucket" +session_profile = "utc-v1" + +[[case]] +id = "t001-int" +description = "WIDTH_BUCKET of integers, including below and above the range. Catches: buckets numbered from 0, or the overflow buckets 0 and n+1 missing" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["-5"], + ["0"], + ["24"], + ["25"], + ["99"], + ["100"], + ["250"], +] +query = "SELECT WIDTH_BUCKET(x, 0, 100, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002-dec" +description = "WIDTH_BUCKET of fixed-point values on bucket edges. Catches: edges assigned to the lower bucket" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["12.50"], + ["12.49"], + ["37.50"], + ["0.00"], +] +query = "SELECT WIDTH_BUCKET(x, 0, 50, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003-float" +description = "WIDTH_BUCKET of FLOAT values with FLOAT end points. Catches: integer end points assumed" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.3"], + ["0.75"], + ["-0.1"], +] +query = "SELECT WIDTH_BUCKET(x, 0.25, 1.25, 5) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004-reversed" +description = "End points given high to low. Catches: the end points swapped back, numbering buckets upwards" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["90"], + ["10"], + ["101"], + ["-1"], +] +query = "SELECT WIDTH_BUCKET(x, 100, 0, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005-null-expr" +description = "NULL value. Catches: NULL put in bucket 0" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], +] +query = "SELECT WIDTH_BUCKET(x, 0, 100, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006-null-min" +description = "NULL low end point. Catches: NULL read as 0" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "m", type = "NUMBER(38,0)" }] +rows = [ + ["50", "NULL"], +] +query = "SELECT WIDTH_BUCKET(x, m, 100, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007-null-max" +description = "NULL high end point. Catches: NULL read as 0" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "m", type = "NUMBER(38,0)" }] +rows = [ + ["50", "NULL"], +] +query = "SELECT WIDTH_BUCKET(x, 0, m, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008-null-n" +description = "NULL bucket count. Catches: NULL read as 1" +columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["50", "NULL"], +] +query = "SELECT WIDTH_BUCKET(x, 0, 100, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009-range-edge" +description = "Values at the documented 2^53 - 1 limit. Catches: precision loss rounding the limit into another bucket" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["9007199254740991"], + ["-9007199254740991"], +] +query = "SELECT WIDTH_BUCKET(x, -9007199254740991, 9007199254740991, 3) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t010-zero-buckets" +description = "num_buckets 0 is an error. Catches: bucket 0 returned" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["5"], +] +query = "SELECT WIDTH_BUCKET(x, 0, 10, 0) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t011-equal-ends" +description = "Equal end points are an error. Catches: division by zero returning NULL or infinity" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["5"], +] +query = "SELECT WIDTH_BUCKET(x, 10, 10, 4) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/coverage/translation/obligations/expressions.toml b/coverage/translation/obligations/expressions.toml new file mode 100644 index 000000000..d970ae031 --- /dev/null +++ b/coverage/translation/obligations/expressions.toml @@ -0,0 +1,729 @@ +# Translation obligations of family expressions (D151): per form, the documented type +# combinations, NULL in each argument, boundaries and error parity, each met by its +# cases under cases/translation. Hand-written; see coverage/translation/README.md. + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t001" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "NUMBER(38,0) + NUMBER(38,0)" +note = "a NUMBER(38,0) + b NUMBER(38,0)." +catches = "operands swapped for subtraction or truncated to 32 bits" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t001-num-num-380380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t002" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "NUMBER(10,2) + NUMBER(38,0)" +note = "a NUMBER(10,2) + b NUMBER(38,0)." +catches = "the sum losing the NUMBER(10,2) scale (4 instead of 4.25)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t002-num-num-102380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t003" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "NUMBER(10,2) + NUMBER(12,4)" +note = "a NUMBER(10,2) + b NUMBER(12,4)." +catches = "the result scale taken from the left operand only (1.31 instead of 1.3125)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t003-num-num-102124"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t004" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "NUMBER(38,0) + FLOAT" +note = "a NUMBER(38,0) + b FLOAT." +catches = "a FLOAT operand rounded to the integer side" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t004-num-float-380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t005" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "NUMBER(10,2) + FLOAT" +note = "a NUMBER(10,2) + b FLOAT." +catches = "fixed-point turned into FLOAT before adding or the reverse" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t005-num-float-102"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t006" +form = "sf.expressions.operators-arithmetic.add" +kind = "type_combination" +target = "FLOAT + FLOAT" +note = "a FLOAT + b FLOAT." +catches = "float addition replaced by decimal addition (0.30000000000000004 vs 0.3)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t006-float-float-"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t007" +form = "sf.expressions.operators-arithmetic.add" +kind = "null_argument" +target = "a" +note = "NULL as a, the other operand set." +catches = "NULL treated as 0 instead of propagating" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t007-null-a"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t008" +form = "sf.expressions.operators-arithmetic.add" +kind = "null_argument" +target = "b" +note = "NULL as b, the other operand set." +catches = "NULL treated as 0 instead of propagating" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t008-null-b"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t009" +form = "sf.expressions.operators-arithmetic.add" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "38-digit integers that still fit." +catches = "arithmetic done in 64 bits" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t009-max-38"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t010" +form = "sf.expressions.operators-arithmetic.add" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative fixed-point sums crossing zero." +catches = "sign lost or magnitude added" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t010-negatives"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.add.t011" +form = "sf.expressions.operators-arithmetic.add" +kind = "error_parity" +target = "error" +note = "The sum of two 38-digit integers overflows NUMBER(38,0)." +catches = "wrapping or a silent switch to FLOAT instead of the out-of-range error" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t011-overflow"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t001" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "NUMBER(38,0) / NUMBER(38,0)" +note = "a NUMBER(38,0) / b NUMBER(38,0)." +catches = "integer division (3 instead of 3.5) or a truncated rather than rounded scale-6 quotient (0.666666 vs 0.666667)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t001-num-num-380380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t002" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "NUMBER(10,2) / NUMBER(38,0)" +note = "a NUMBER(10,2) / b NUMBER(38,0)." +catches = "the quotient scale (s+6) handled wrongly" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t002-num-num-102380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t003" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "NUMBER(10,2) / NUMBER(12,4)" +note = "a NUMBER(10,2) / b NUMBER(12,4)." +catches = "quotient scale taken from the divisor" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t003-num-num-102124"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t004" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "NUMBER(38,0) / FLOAT" +note = "a NUMBER(38,0) / b FLOAT." +catches = "FLOAT divisor rounded" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t004-num-float-380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t005" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "NUMBER(10,2) / FLOAT" +note = "a NUMBER(10,2) / b FLOAT." +catches = "fixed-point / FLOAT result type swapped" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t005-num-float-102"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t006" +form = "sf.expressions.operators-arithmetic.divide" +kind = "type_combination" +target = "FLOAT / FLOAT" +note = "a FLOAT / b FLOAT." +catches = "float division done in decimal" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t006-float-float-"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t007" +form = "sf.expressions.operators-arithmetic.divide" +kind = "null_argument" +target = "a" +note = "NULL as a, the other operand set." +catches = "NULL treated as 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t007-null-a"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t008" +form = "sf.expressions.operators-arithmetic.divide" +kind = "null_argument" +target = "b" +note = "NULL as b, the other operand set." +catches = "NULL divisor turned into the division-by-zero error or 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t008-null-b"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t009" +form = "sf.expressions.operators-arithmetic.divide" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales near the 12-digit quotient cap." +catches = "the quotient scale not capped at 12" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t009-scale-12"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t010" +form = "sf.expressions.operators-arithmetic.divide" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative quotients that round at the last digit." +catches = "rounding toward zero instead of half away from zero" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t010-neg-quotient"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.divide.t011" +form = "sf.expressions.operators-arithmetic.divide" +kind = "error_parity" +target = "error" +note = "Division by zero is an error." +catches = "NULL, 0 or infinity instead of the division-by-zero error" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t011-div-zero"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t001" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "type_combination" +target = "NUMBER(38,0) % NUMBER(38,0)" +note = "a NUMBER(38,0) % b NUMBER(38,0)." +catches = "floor-based modulo (2 instead of -1): MOD truncates toward zero" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t001-num-num-380380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t002" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "type_combination" +target = "NUMBER(10,2) % NUMBER(38,0)" +note = "a NUMBER(10,2) % b NUMBER(38,0)." +catches = "fractional remainder dropped" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t002-num-num-102380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t003" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "type_combination" +target = "NUMBER(10,2) % NUMBER(12,4)" +note = "a NUMBER(10,2) % b NUMBER(12,4)." +catches = "result scale taken from one operand" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t003-num-num-102124"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t004" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "type_combination" +target = "NUMBER(38,0) % FLOAT" +note = "a NUMBER(38,0) % b FLOAT." +catches = "FLOAT divisor rounded" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t004-num-float-380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t005" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "type_combination" +target = "FLOAT % FLOAT" +note = "a FLOAT % b FLOAT." +catches = "floor-based float modulo" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t005-float-float-"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t006" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "null_argument" +target = "a" +note = "NULL as a, the other operand set." +catches = "NULL treated as 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t006-null-a"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t007" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "null_argument" +target = "b" +note = "NULL as b, the other operand set." +catches = "NULL treated as 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t007-null-b"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t008" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Both operands negative." +catches = "sign of the remainder taken from the divisor" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t008-both-negative"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t009" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "error_parity" +target = "error" +note = "A non-numeric string operand." +catches = "the string silently read as 0 instead of the conversion error" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t009-mod-text"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t001" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "NUMBER(38,0) * NUMBER(38,0)" +note = "a NUMBER(38,0) * b NUMBER(38,0)." +catches = "32-bit multiplication overflowing" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t001-num-num-380380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t002" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "NUMBER(10,2) * NUMBER(38,0)" +note = "a NUMBER(10,2) * b NUMBER(38,0)." +catches = "the NUMBER(10,2) scale lost" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t002-num-num-102380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t003" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "NUMBER(10,2) * NUMBER(12,4)" +note = "a NUMBER(10,2) * b NUMBER(12,4)." +catches = "the product scale capped or taken from one side (1.5625 vs 1.56)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t003-num-num-102124"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t004" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "NUMBER(38,0) * FLOAT" +note = "a NUMBER(38,0) * b FLOAT." +catches = "FLOAT operand truncated" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t004-num-float-380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t005" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "NUMBER(10,2) * FLOAT" +note = "a NUMBER(10,2) * b FLOAT." +catches = "fixed-point / FLOAT result type swapped" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t005-num-float-102"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t006" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "type_combination" +target = "FLOAT * FLOAT" +note = "a FLOAT * b FLOAT." +catches = "underflow to zero handled differently" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t006-float-float-"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t007" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "null_argument" +target = "a" +note = "NULL as a, the other operand set." +catches = "NULL treated as 1 or 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t007-null-a"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t008" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "null_argument" +target = "b" +note = "NULL as b, the other operand set." +catches = "NULL treated as 1 or 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t008-null-b"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t009" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "FLOAT products near the double range." +catches = "overflow to infinity treated differently" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t009-float-big"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t010" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales adding past 12 digits." +catches = "product scale above Snowflake's cap kept or rounded wrongly" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t010-scale-sum"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.multiply.t011" +form = "sf.expressions.operators-arithmetic.multiply" +kind = "error_parity" +target = "error" +note = "A product beyond 38 digits." +catches = "wrapping or silent FLOAT instead of the out-of-range error" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t011-overflow"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t001" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "NUMBER(38,0) - NUMBER(38,0)" +note = "a NUMBER(38,0) - b NUMBER(38,0)." +catches = "operands in the wrong order (7 instead of -7)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t001-num-num-380380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t002" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "NUMBER(10,2) - NUMBER(38,0)" +note = "a NUMBER(10,2) - b NUMBER(38,0)." +catches = "scale of the NUMBER(10,2) side dropped" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t002-num-num-102380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t003" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "NUMBER(10,2) - NUMBER(12,4)" +note = "a NUMBER(10,2) - b NUMBER(12,4)." +catches = "the result scale taken from one operand only" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t003-num-num-102124"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t004" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "NUMBER(38,0) - FLOAT" +note = "a NUMBER(38,0) - b FLOAT." +catches = "FLOAT rounded to integer" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t004-num-float-380"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t005" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "NUMBER(10,2) - FLOAT" +note = "a NUMBER(10,2) - b FLOAT." +catches = "fixed-point / FLOAT result type swapped" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t005-num-float-102"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t006" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "FLOAT - FLOAT" +note = "a FLOAT - b FLOAT." +catches = "float subtraction done in decimal" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t006-float-float-"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t007" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "type_combination" +target = "DATE - DATE" +note = "DATE minus DATE: the page says the result is an integer number of days." +catches = "an INTERVAL instead of an integer count of days, or days counted across the leap day wrongly (29, -730)" +reading = "operators-arithmetic: \"a - b Subtracts one date expression ( b ) from another ( a ). The result is an integer number of days.\"" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t007-date-date"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t008" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "null_argument" +target = "a" +note = "NULL as a, the other operand set." +catches = "NULL treated as 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t008-null-a"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t009" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "null_argument" +target = "b" +note = "NULL as b, the other operand set." +catches = "NULL treated as 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t009-null-b"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t010" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A smaller minus a larger value." +catches = "absolute value taken" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t010-to-negative"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.subtract.t011" +form = "sf.expressions.operators-arithmetic.subtract" +kind = "error_parity" +target = "error" +note = "The difference of the most negative and the largest 38-digit integers overflows." +catches = "wrapping instead of the out-of-range error" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t011-overflow"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t001" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "Negating integers." +catches = "the sign dropped or the value doubled" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t002" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "Negating NUMBER(10,2)." +catches = "the scale lost (-1 instead of -1.25)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t003" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "type_combination" +target = "FLOAT" +note = "Negating FLOAT." +catches = "negative zero or exponent handling" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t004" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "null_argument" +target = "a" +note = "Negating NULL." +catches = "NULL turned into 0" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t005" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Negating the largest 38-digit integer." +catches = "a 64-bit negation that overflows" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t005-max"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-minus.t006" +form = "sf.expressions.operators-arithmetic.unary-minus" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negation of a negation and of a subtraction." +catches = "precedence: - a - b read as -(a - b)" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t006-double-neg"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t001" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "type_combination" +target = "VARCHAR" +note = "Unary plus on numeric strings converts them." +catches = "the string returned unchanged, so ordering and arithmetic stay textual" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t001-text"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t002" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "Unary plus keeps a NUMBER(10,2) as it is." +catches = "a cast that changes the scale" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t003" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "type_combination" +target = "FLOAT" +note = "Unary plus on FLOAT." +catches = "conversion to fixed-point" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t004" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "null_argument" +target = "a" +note = "Unary plus on NULL text." +catches = "NULL replaced or converted to 0" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t005" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Unary plus on an empty string." +catches = "the empty string read as 0 when Snowflake errors, or the reverse" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t005-empty"] +required = true + +[[obligation]] +id = "sf.expressions.operators-arithmetic.unary-plus.t006" +form = "sf.expressions.operators-arithmetic.unary-plus" +kind = "error_parity" +target = "error" +note = "Unary plus on a non-numeric string is an error." +catches = "the string passed through unconverted" +reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t006-bad-text"] +required = true diff --git a/coverage/translation/obligations/functions.toml b/coverage/translation/obligations/functions.toml new file mode 100644 index 000000000..4ff026cf0 --- /dev/null +++ b/coverage/translation/obligations/functions.toml @@ -0,0 +1,2535 @@ +# Translation obligations of family functions (D151): per form, the documented type +# combinations, NULL in each argument, boundaries and error parity, each met by its +# cases under cases/translation. Hand-written; see coverage/translation/README.md. + +[[obligation]] +id = "sf.functions.abs.t001" +form = "sf.functions.abs" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "ABS of integers." +catches = "the sign kept (ABS as identity) or SIGN computed" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/abs.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.abs.t002" +form = "sf.functions.abs" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "ABS of NUMBER(10,2), keeping its type." +catches = "the scale lost or FLOAT returned" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/abs.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.abs.t003" +form = "sf.functions.abs" +kind = "type_combination" +target = "FLOAT" +note = "ABS of FLOAT." +catches = "negative zero and tiny negatives" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/abs.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.abs.t004" +form = "sf.functions.abs" +kind = "null_argument" +target = "x" +note = "ABS of NULL." +catches = "NULL turned into 0" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/abs.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.abs.t005" +form = "sf.functions.abs" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "The 38-digit and double extremes." +catches = "64-bit arithmetic overflowing" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/abs.toml#t005-extremes"] +required = true + +[[obligation]] +id = "sf.functions.acos.t001" +form = "sf.functions.acos" +kind = "type_combination" +target = "FLOAT" +note = "ACOS of FLOAT values." +catches = "ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acos.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.acos.t002" +form = "sf.functions.acos" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ACOS of NUMBER(12,4), converted to FLOAT." +catches = "ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acos.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.acos.t003" +form = "sf.functions.acos" +kind = "null_argument" +target = "x" +note = "ACOS of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acos.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.acos.t004" +form = "sf.functions.acos" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "The domain edges, 1 and -1." +catches = "pi returned for 1 or 0 for -1 (sign of the edge)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acos.toml#t004-edges"] +required = true + +[[obligation]] +id = "sf.functions.acosh.t001" +form = "sf.functions.acosh" +kind = "type_combination" +target = "FLOAT" +note = "ACOSH of FLOAT values." +catches = "ASINH computed" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acosh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.acosh.t002" +form = "sf.functions.acosh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ACOSH of NUMBER(12,4), converted to FLOAT." +catches = "ASINH computed; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acosh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.acosh.t003" +form = "sf.functions.acosh" +kind = "null_argument" +target = "x" +note = "ACOSH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acosh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.acosh.t004" +form = "sf.functions.acosh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "At 1, the domain edge, and large values." +catches = "edge returned as NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acosh.toml#t004-edge"] +required = true + +[[obligation]] +id = "sf.functions.asin.t001" +form = "sf.functions.asin" +kind = "type_combination" +target = "FLOAT" +note = "ASIN of FLOAT values." +catches = "ACOS computed instead, or degrees returned" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asin.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.asin.t002" +form = "sf.functions.asin" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ASIN of NUMBER(12,4), converted to FLOAT." +catches = "ACOS computed instead, or degrees returned; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asin.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.asin.t003" +form = "sf.functions.asin" +kind = "null_argument" +target = "x" +note = "ASIN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asin.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.asin.t004" +form = "sf.functions.asin" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Odd symmetry at negatives." +catches = "absolute value taken" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asin.toml#t004-odd"] +required = true + +[[obligation]] +id = "sf.functions.asinh.t001" +form = "sf.functions.asinh" +kind = "type_combination" +target = "FLOAT" +note = "ASINH of FLOAT values." +catches = "ACOSH computed" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asinh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.asinh.t002" +form = "sf.functions.asinh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ASINH of NUMBER(12,4), converted to FLOAT." +catches = "ACOSH computed; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asinh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.asinh.t003" +form = "sf.functions.asinh" +kind = "null_argument" +target = "x" +note = "ASINH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asinh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.asinh.t004" +form = "sf.functions.asinh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large arguments." +catches = "precision loss in the log formula" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asinh.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.atan.t001" +form = "sf.functions.atan" +kind = "type_combination" +target = "FLOAT" +note = "ATAN of FLOAT values." +catches = "ATAN2 with a swapped argument, or degrees returned" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atan.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.atan.t002" +form = "sf.functions.atan" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ATAN of NUMBER(12,4), converted to FLOAT." +catches = "ATAN2 with a swapped argument, or degrees returned; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atan.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.atan.t003" +form = "sf.functions.atan" +kind = "null_argument" +target = "x" +note = "ATAN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atan.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.atan.t004" +form = "sf.functions.atan" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very large arguments approach pi/2." +catches = "overflow handling" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atan.toml#t004-huge"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t001" +form = "sf.functions.atan2" +kind = "type_combination" +target = "FLOAT" +note = "ATAN2 in all four quadrants." +catches = "arguments swapped (ATAN2(y, x) vs ATAN2(x, y)) or ATAN(y/x) losing the quadrant" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t002" +form = "sf.functions.atan2" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ATAN2 of NUMBER(12,4), converted to DOUBLE." +catches = "the result left fixed-point" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t003" +form = "sf.functions.atan2" +kind = "type_combination" +target = "VARCHAR" +note = "ATAN2 of numeric strings, converted to DOUBLE." +catches = "the strings rejected" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t003-text"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t004" +form = "sf.functions.atan2" +kind = "null_argument" +target = "y" +note = "NULL y." +catches = "NULL turned into 0 (giving 0 or pi)" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t004-null-y"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t005" +form = "sf.functions.atan2" +kind = "null_argument" +target = "x" +note = "NULL x." +catches = "NULL turned into 0 (giving pi/2)" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t006" +form = "sf.functions.atan2" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Signed zeros on the negative x axis." +catches = "the sign of zero lost (pi vs -pi)" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t006-zeros"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t007" +form = "sf.functions.atan2" +kind = "error_parity" +target = "error" +note = "A string that is not a number is an error." +catches = "the string read as 0" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t007-bad-text"] +required = true + +[[obligation]] +id = "sf.functions.atan2.t008" +form = "sf.functions.atan2" +kind = "error_parity" +target = "error" +note = "A BOOLEAN argument is an error (\"any other data type\")." +catches = "TRUE read as 1" +reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" +cases = ["cases/translation/functions/atan2.toml#t008-boolean"] +required = true + +[[obligation]] +id = "sf.functions.atanh.t001" +form = "sf.functions.atanh" +kind = "type_combination" +target = "FLOAT" +note = "ATANH of FLOAT values." +catches = "TANH computed, or the odd symmetry lost" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.atanh.t002" +form = "sf.functions.atanh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ATANH of NUMBER(12,4), converted to FLOAT." +catches = "TANH computed, or the odd symmetry lost; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.atanh.t003" +form = "sf.functions.atanh" +kind = "null_argument" +target = "x" +note = "ATANH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.atanh.t004" +form = "sf.functions.atanh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Close to the domain edges." +catches = "precision near the asymptote" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t004-near-one"] +required = true + +[[obligation]] +id = "sf.functions.cbrt.t001" +form = "sf.functions.cbrt" +kind = "type_combination" +target = "FLOAT" +note = "CBRT of FLOAT values." +catches = "SQRT computed, or POW(x, 1/3) giving NaN for negatives" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/cbrt.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.cbrt.t002" +form = "sf.functions.cbrt" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "CBRT of NUMBER(12,4), converted to FLOAT." +catches = "SQRT computed, or POW(x, 1/3) giving NaN for negatives; or the result left fixed-point" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/cbrt.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.cbrt.t003" +form = "sf.functions.cbrt" +kind = "type_combination" +target = "DECFLOAT" +note = "CBRT of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/cbrt.toml#t003-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.cbrt.t004" +form = "sf.functions.cbrt" +kind = "null_argument" +target = "x" +note = "CBRT of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/cbrt.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.cbrt.t005" +form = "sf.functions.cbrt" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Cube roots of negatives are negative." +catches = "NaN from a fractional power of a negative" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/cbrt.toml#t005-neg"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t001" +form = "sf.functions.ceil" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "CEIL of integers with a negative scale." +catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a negative scale ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t002" +form = "sf.functions.ceil" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "CEIL of NUMBER(12,4) with and without a scale." +catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t003" +form = "sf.functions.ceil" +kind = "type_combination" +target = "FLOAT" +note = "CEIL of FLOAT returns FLOAT." +catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t004" +form = "sf.functions.ceil" +kind = "type_combination" +target = "DECFLOAT" +note = "CEIL of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t005" +form = "sf.functions.ceil" +kind = "null_argument" +target = "input_expr" +note = "CEIL of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t006" +form = "sf.functions.ceil" +kind = "null_argument" +target = "scale_expr" +note = "CEIL with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t006-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t007" +form = "sf.functions.ceil" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and far negative." +catches = "scale capped or wrapping" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t007-scale-past"] +required = true + +[[obligation]] +id = "sf.functions.ceil.t008" +form = "sf.functions.ceil" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative values at the rounding step." +catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/ceil.toml#t008-negatives"] +required = true + +[[obligation]] +id = "sf.functions.cos.t001" +form = "sf.functions.cos" +kind = "type_combination" +target = "FLOAT" +note = "COS of FLOAT values." +catches = "SIN computed, or the argument read in degrees" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.cos.t002" +form = "sf.functions.cos" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "COS of NUMBER(12,4), converted to FLOAT." +catches = "SIN computed, or the argument read in degrees; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.cos.t003" +form = "sf.functions.cos" +kind = "null_argument" +target = "x" +note = "COS of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.cos.t004" +form = "sf.functions.cos" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large arguments." +catches = "argument reduction differing" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.cosh.t001" +form = "sf.functions.cosh" +kind = "type_combination" +target = "FLOAT" +note = "COSH of FLOAT values." +catches = "SINH computed (cosh is even, sinh odd)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.cosh.t002" +form = "sf.functions.cosh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "COSH of NUMBER(12,4), converted to FLOAT." +catches = "SINH computed (cosh is even, sinh odd); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.cosh.t003" +form = "sf.functions.cosh" +kind = "null_argument" +target = "x" +note = "COSH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.cosh.t004" +form = "sf.functions.cosh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Arguments past the double range." +catches = "infinity versus error" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t004-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.cot.t001" +form = "sf.functions.cot" +kind = "type_combination" +target = "FLOAT" +note = "COT of FLOAT values." +catches = "TAN computed instead of its reciprocal" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.cot.t002" +form = "sf.functions.cot" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "COT of NUMBER(12,4), converted to FLOAT." +catches = "TAN computed instead of its reciprocal; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.cot.t003" +form = "sf.functions.cot" +kind = "null_argument" +target = "x" +note = "COT of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.cot.t004" +form = "sf.functions.cot" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very small arguments give large cotangents." +catches = "reciprocal computed in fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t004-small"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t001" +form = "sf.functions.degrees" +kind = "type_combination" +target = "FLOAT" +note = "DEGREES of FLOAT values." +catches = "RADIANS computed (the reverse conversion)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t002" +form = "sf.functions.degrees" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "DEGREES of NUMBER(12,4), converted to FLOAT." +catches = "RADIANS computed (the reverse conversion); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t003" +form = "sf.functions.degrees" +kind = "null_argument" +target = "x" +note = "DEGREES of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t004" +form = "sf.functions.degrees" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large radian values." +catches = "overflow handling" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.div0.t001" +form = "sf.functions.div0" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "DIV0 of integers, as / divides." +catches = "integer division (3 instead of 3.500000)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.div0.t002" +form = "sf.functions.div0" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "DIV0 of NUMBER(10,2) values." +catches = "quotient scale differing from /" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.div0.t003" +form = "sf.functions.div0" +kind = "type_combination" +target = "FLOAT" +note = "DIV0 of FLOAT values." +catches = "infinity or NaN for a zero divisor instead of 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.div0.t004" +form = "sf.functions.div0" +kind = "null_argument" +target = "dividend" +note = "NULL dividend." +catches = "NULL turned into 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t004-null-a"] +required = true + +[[obligation]] +id = "sf.functions.div0.t005" +form = "sf.functions.div0" +kind = "null_argument" +target = "divisor" +note = "NULL divisor." +catches = "a NULL divisor returning 0 (that is DIV0NULL)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t005-null-b"] +required = true + +[[obligation]] +id = "sf.functions.div0.t006" +form = "sf.functions.div0" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative dividends over zero." +catches = "-0 or a negative zero result" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t006-neg-zero"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t001" +form = "sf.functions.div0null" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "DIV0NULL of integers, as / divides." +catches = "integer division (3 instead of 3.500000)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t002" +form = "sf.functions.div0null" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "DIV0NULL of NUMBER(10,2) values." +catches = "quotient scale differing from /" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t003" +form = "sf.functions.div0null" +kind = "type_combination" +target = "FLOAT" +note = "DIV0NULL of FLOAT values." +catches = "infinity or NaN for a zero divisor instead of 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t004" +form = "sf.functions.div0null" +kind = "null_argument" +target = "dividend" +note = "NULL dividend." +catches = "NULL turned into 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t004-null-a"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t005" +form = "sf.functions.div0null" +kind = "null_argument" +target = "divisor" +note = "NULL divisor." +catches = "a NULL divisor returning NULL (that is DIV0)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t005-null-b"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t006" +form = "sf.functions.div0null" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative dividends over zero." +catches = "-0 or a negative zero result" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t006-neg-zero"] +required = true + +[[obligation]] +id = "sf.functions.exp.t001" +form = "sf.functions.exp" +kind = "type_combination" +target = "FLOAT" +note = "EXP of FLOAT values." +catches = "LN computed, or e^x read as 10^x" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.exp.t002" +form = "sf.functions.exp" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "EXP of NUMBER(12,4), converted to FLOAT." +catches = "LN computed, or e^x read as 10^x; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.exp.t003" +form = "sf.functions.exp" +kind = "type_combination" +target = "DECFLOAT" +note = "EXP of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t003-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.exp.t004" +form = "sf.functions.exp" +kind = "null_argument" +target = "x" +note = "EXP of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.exp.t005" +form = "sf.functions.exp" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Past the double range." +catches = "infinity versus error" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t005-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t001" +form = "sf.functions.factorial" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "FACTORIAL of integers." +catches = "gamma(x) computed (fractional), or x! off by one (n-1)!" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t002" +form = "sf.functions.factorial" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "FACTORIAL of an integral NUMBER(10,2)." +catches = "scale carried into the result" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t002-dec-int"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t003" +form = "sf.functions.factorial" +kind = "null_argument" +target = "integer_expr" +note = "FACTORIAL of NULL." +catches = "NULL turned into 1" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t004" +form = "sf.functions.factorial" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large factorials that still fit 38 digits." +catches = "64-bit overflow (21! does not fit a BIGINT)" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t005" +form = "sf.functions.factorial" +kind = "error_parity" +target = "error" +note = "A negative argument." +catches = "a gamma value or 1 instead of the error" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t005-negative"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t006" +form = "sf.functions.factorial" +kind = "error_parity" +target = "error" +note = "A result past 38 digits." +catches = "FLOAT or wrapping instead of the error" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t006-too-big"] +required = true + +[[obligation]] +id = "sf.functions.floor.t001" +form = "sf.functions.floor" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "FLOOR of integers with a negative scale." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.floor.t002" +form = "sf.functions.floor" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "FLOOR of NUMBER(12,4) with and without a scale." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.floor.t003" +form = "sf.functions.floor" +kind = "type_combination" +target = "FLOAT" +note = "FLOOR of FLOAT returns FLOAT." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.floor.t004" +form = "sf.functions.floor" +kind = "type_combination" +target = "DECFLOAT" +note = "FLOOR of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.floor.t005" +form = "sf.functions.floor" +kind = "null_argument" +target = "input_expr" +note = "FLOOR of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.floor.t006" +form = "sf.functions.floor" +kind = "null_argument" +target = "scale_expr" +note = "FLOOR with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t006-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.floor.t007" +form = "sf.functions.floor" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and far negative." +catches = "scale capped or wrapping" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t007-scale-past"] +required = true + +[[obligation]] +id = "sf.functions.floor.t008" +form = "sf.functions.floor" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative values at the rounding step." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t008-negatives"] +required = true + +[[obligation]] +id = "sf.functions.ln.t001" +form = "sf.functions.ln" +kind = "type_combination" +target = "FLOAT" +note = "LN of FLOAT values." +catches = "LOG10 or LOG2 computed" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.ln.t002" +form = "sf.functions.ln" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "LN of NUMBER(12,4), converted to FLOAT." +catches = "LOG10 or LOG2 computed; or the result left fixed-point" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.ln.t003" +form = "sf.functions.ln" +kind = "type_combination" +target = "DECFLOAT" +note = "LN of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t003-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.ln.t004" +form = "sf.functions.ln" +kind = "null_argument" +target = "x" +note = "LN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.ln.t005" +form = "sf.functions.ln" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very small positive values." +catches = "underflow to -infinity" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t005-tiny"] +required = true + +[[obligation]] +id = "sf.functions.ln.t006" +form = "sf.functions.ln" +kind = "error_parity" +target = "error" +note = "LN of 0 or a negative value is an error." +catches = "NULL, NaN or -infinity instead of the error" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t006-zero"] +required = true + +[[obligation]] +id = "sf.functions.log.t001" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "LOG of integers." +catches = "arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.log.t002" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "LOG of fixed-point values." +catches = "base rounded to an integer" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.log.t003" +form = "sf.functions.log" +kind = "type_combination" +target = "FLOAT" +note = "LOG of FLOAT values." +catches = "computed in decimal" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.log.t004" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(38,0), FLOAT" +note = "LOG with an integer base and a FLOAT value." +catches = "type promotion differing" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t004-mixed"] +required = true + +[[obligation]] +id = "sf.functions.log.t005" +form = "sf.functions.log" +kind = "null_argument" +target = "base" +note = "NULL base." +catches = "NULL turned into e or 10" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t005-null-base"] +required = true + +[[obligation]] +id = "sf.functions.log.t006" +form = "sf.functions.log" +kind = "null_argument" +target = "expr" +note = "NULL value." +catches = "NULL turned into 1" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t006-null-expr"] +required = true + +[[obligation]] +id = "sf.functions.log.t007" +form = "sf.functions.log" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "A base between 0 and 1 gives negative logs of values above 1." +catches = "the sign lost" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t007-base-below-one"] +required = true + +[[obligation]] +id = "sf.functions.log.t008" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A base of 1 is an error." +catches = "infinity or NULL instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t008-base-one"] +required = true + +[[obligation]] +id = "sf.functions.log.t009" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A negative base is an error." +catches = "NaN instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t009-base-neg"] +required = true + +[[obligation]] +id = "sf.functions.log.t010" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A value of 0 is an error." +catches = "-infinity instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t010-expr-zero"] +required = true + +[[obligation]] +id = "sf.functions.mod.t001" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "MOD of integers with negatives." +catches = "floor-based modulo (MOD(-7, 3) is -1, not 2)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.mod.t002" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "MOD of NUMBER(10,2) values." +catches = "fractional remainder dropped" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.mod.t003" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(10,2), NUMBER(38,0)" +note = "MOD of a scaled and an integer operand." +catches = "result scale from one side" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t003-dec-int"] +required = true + +[[obligation]] +id = "sf.functions.mod.t004" +form = "sf.functions.mod" +kind = "type_combination" +target = "FLOAT" +note = "MOD of FLOAT values." +catches = "floor-based float modulo" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t004-float"] +required = true + +[[obligation]] +id = "sf.functions.mod.t005" +form = "sf.functions.mod" +kind = "null_argument" +target = "expr1" +note = "NULL dividend." +catches = "NULL treated as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t005-null-a"] +required = true + +[[obligation]] +id = "sf.functions.mod.t006" +form = "sf.functions.mod" +kind = "null_argument" +target = "expr2" +note = "NULL divisor." +catches = "NULL treated as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t006-null-b"] +required = true + +[[obligation]] +id = "sf.functions.mod.t007" +form = "sf.functions.mod" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Argument order with negatives." +catches = "arguments swapped" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t007-order"] +required = true + +[[obligation]] +id = "sf.functions.mod.t008" +form = "sf.functions.mod" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Remainders of 38-digit integers." +catches = "64-bit modulo" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t008-max"] +required = true + +[[obligation]] +id = "sf.functions.pi.t001" +form = "sf.functions.pi" +kind = "type_combination" +target = "()" +note = "PI() in arithmetic, at full double precision." +catches = "a rounded constant (3.14159) or a NUMBER" +reading = "the page: PI() takes no argument and returns FLOAT" +cases = ["cases/translation/functions/pi.toml#t001-value"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t001" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "POW of integers returns FLOAT." +catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t002" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "POW with fractional exponents." +catches = "the exponent rounded" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t003" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "FLOAT" +note = "POW of FLOAT values." +catches = "computed in decimal" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t004" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "DECFLOAT" +note = "POW of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t005" +form = "sf.functions.pow.pow" +kind = "null_argument" +target = "x" +note = "NULL base." +catches = "NULL^0 returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t006" +form = "sf.functions.pow.pow" +kind = "null_argument" +target = "y" +note = "NULL exponent." +catches = "1^NULL returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t006-null-y"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t007" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Powers past the double range, and 0^0." +catches = "infinity versus error; 0^0 as 0" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t007-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t008" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative bases with integer exponents." +catches = "the sign lost for odd exponents" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t008-neg-base"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t001" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "POWER of integers returns FLOAT." +catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t002" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "POWER with fractional exponents." +catches = "the exponent rounded" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t003" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "FLOAT" +note = "POWER of FLOAT values." +catches = "computed in decimal" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t004" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "DECFLOAT" +note = "POWER of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t005" +form = "sf.functions.pow.power" +kind = "null_argument" +target = "x" +note = "NULL base." +catches = "NULL^0 returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t006" +form = "sf.functions.pow.power" +kind = "null_argument" +target = "y" +note = "NULL exponent." +catches = "1^NULL returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t006-null-y"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t007" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Powers past the double range, and 0^0." +catches = "infinity versus error; 0^0 as 0" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t007-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t008" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative bases with integer exponents." +catches = "the sign lost for odd exponents" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t008-neg-base"] +required = true + +[[obligation]] +id = "sf.functions.radians.t001" +form = "sf.functions.radians" +kind = "type_combination" +target = "FLOAT" +note = "RADIANS of FLOAT values." +catches = "DEGREES computed (the reverse conversion)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.radians.t002" +form = "sf.functions.radians" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "RADIANS of NUMBER(12,4), converted to FLOAT." +catches = "DEGREES computed (the reverse conversion); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.radians.t003" +form = "sf.functions.radians" +kind = "null_argument" +target = "x" +note = "RADIANS of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.radians.t004" +form = "sf.functions.radians" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large degree values." +catches = "overflow handling" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t001" +form = "sf.functions.round.round" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "ROUND of integers with a negative scale." +catches = "a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t002" +form = "sf.functions.round.round" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ROUND of NUMBER(12,4) halves." +catches = "banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t003" +form = "sf.functions.round.round" +kind = "type_combination" +target = "FLOAT" +note = "ROUND of FLOAT halves exact in binary." +catches = "half-to-even for FLOAT, or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t004" +form = "sf.functions.round.round" +kind = "type_combination" +target = "DECFLOAT" +note = "ROUND of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t005" +form = "sf.functions.round.round" +kind = "type_combination" +target = "'HALF_TO_EVEN'" +note = "rounding_mode 'HALF_TO_EVEN' on fixed-point." +catches = "the mode ignored (2.5 to 3 instead of 2)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t005-mode-even"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t006" +form = "sf.functions.round.round" +kind = "type_combination" +target = "'HALF_AWAY_FROM_ZERO'" +note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." +catches = "the mode read as half-even" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t006-mode-away"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t007" +form = "sf.functions.round.round" +kind = "null_argument" +target = "input_expr" +note = "ROUND of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t007-null-x"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t008" +form = "sf.functions.round.round" +kind = "null_argument" +target = "scale_expr" +note = "ROUND with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t008-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t009" +form = "sf.functions.round.round" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and a carry across digits." +catches = "carry lost (9.995 to 9.99) or scale capped" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t009-scale"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t010" +form = "sf.functions.round.round" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Rounding the largest 38-digit integers up to a power of ten." +catches = "overflow to FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t010-max"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t011" +form = "sf.functions.round.round" +kind = "error_parity" +target = "error" +note = "A rounding_mode with a FLOAT input is an error." +catches = "the mode applied to FLOAT instead of the documented error" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t011-mode-float"] +required = true + +[[obligation]] +id = "sf.functions.round.round.t012" +form = "sf.functions.round.round" +kind = "error_parity" +target = "error" +note = "An unknown rounding_mode is an error." +catches = "the mode ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" +cases = ["cases/translation/functions/round.round.toml#t012-mode-bad"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t001" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "ROUND of integers with a negative scale." +catches = "a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t002" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "ROUND of NUMBER(12,4) halves." +catches = "banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t003" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "FLOAT" +note = "ROUND of FLOAT halves exact in binary." +catches = "half-to-even for FLOAT, or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t004" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "DECFLOAT" +note = "ROUND of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t005" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "'HALF_TO_EVEN'" +note = "rounding_mode 'HALF_TO_EVEN' on fixed-point." +catches = "the mode ignored (2.5 to 3 instead of 2)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t005-mode-even"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t006" +form = "sf.functions.round.round-expr-scale" +kind = "type_combination" +target = "'HALF_AWAY_FROM_ZERO'" +note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." +catches = "the mode read as half-even" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t006-mode-away"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t007" +form = "sf.functions.round.round-expr-scale" +kind = "null_argument" +target = "input_expr" +note = "ROUND of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t007-null-x"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t008" +form = "sf.functions.round.round-expr-scale" +kind = "null_argument" +target = "scale_expr" +note = "ROUND with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t008-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t009" +form = "sf.functions.round.round-expr-scale" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and a carry across digits." +catches = "carry lost (9.995 to 9.99) or scale capped" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t009-scale"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t010" +form = "sf.functions.round.round-expr-scale" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Rounding the largest 38-digit integers up to a power of ten." +catches = "overflow to FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t010-max"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t011" +form = "sf.functions.round.round-expr-scale" +kind = "error_parity" +target = "error" +note = "A rounding_mode with a FLOAT input is an error." +catches = "the mode applied to FLOAT instead of the documented error" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t011-mode-float"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t012" +form = "sf.functions.round.round-expr-scale" +kind = "error_parity" +target = "error" +note = "An unknown rounding_mode is an error." +catches = "the mode ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t012-mode-bad"] +required = true + +[[obligation]] +id = "sf.functions.round.round-expr-scale.t013" +form = "sf.functions.round.round-expr-scale" +kind = "error_parity" +target = "error" +note = "EXPR => without SCALE => is an error, as the page says." +catches = "SCALE defaulted to 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" +cases = ["cases/translation/functions/round.round-expr-scale.toml#t013-expr-no-scale"] +required = true + +[[obligation]] +id = "sf.functions.sign.t001" +form = "sf.functions.sign" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "SIGN of integers." +catches = "ABS computed, or -0.001 rounded to 0 before taking the sign" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/sign.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.sign.t002" +form = "sf.functions.sign" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "SIGN of NUMBER(10,2), keeping its type." +catches = "the scale lost or FLOAT returned" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/sign.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.sign.t003" +form = "sf.functions.sign" +kind = "type_combination" +target = "FLOAT" +note = "SIGN of FLOAT." +catches = "negative zero and tiny negatives" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/sign.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.sign.t004" +form = "sf.functions.sign" +kind = "null_argument" +target = "x" +note = "SIGN of NULL." +catches = "NULL turned into 0" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/sign.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.sign.t005" +form = "sf.functions.sign" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "The 38-digit and double extremes." +catches = "64-bit arithmetic overflowing" +reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/functions/sign.toml#t005-extremes"] +required = true + +[[obligation]] +id = "sf.functions.sin.t001" +form = "sf.functions.sin" +kind = "type_combination" +target = "FLOAT" +note = "SIN of FLOAT values." +catches = "COS computed, or the argument read in degrees" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sin.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.sin.t002" +form = "sf.functions.sin" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "SIN of NUMBER(12,4), converted to FLOAT." +catches = "COS computed, or the argument read in degrees; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sin.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.sin.t003" +form = "sf.functions.sin" +kind = "null_argument" +target = "x" +note = "SIN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sin.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.sin.t004" +form = "sf.functions.sin" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large arguments." +catches = "argument reduction differing" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sin.toml#t004-large"] +required = true + +[[obligation]] +id = "sf.functions.sinh.t001" +form = "sf.functions.sinh" +kind = "type_combination" +target = "FLOAT" +note = "SINH of FLOAT values." +catches = "COSH computed" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sinh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.sinh.t002" +form = "sf.functions.sinh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "SINH of NUMBER(12,4), converted to FLOAT." +catches = "COSH computed; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sinh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.sinh.t003" +form = "sf.functions.sinh" +kind = "null_argument" +target = "x" +note = "SINH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sinh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.sinh.t004" +form = "sf.functions.sinh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Arguments past the double range." +catches = "infinity versus error" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/sinh.toml#t004-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.sqrt.t001" +form = "sf.functions.sqrt" +kind = "type_combination" +target = "FLOAT" +note = "SQRT of FLOAT values." +catches = "CBRT or squaring computed" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.sqrt.t002" +form = "sf.functions.sqrt" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "SQRT of NUMBER(12,4), converted to FLOAT." +catches = "CBRT or squaring computed; or the result left fixed-point" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.sqrt.t003" +form = "sf.functions.sqrt" +kind = "type_combination" +target = "DECFLOAT" +note = "SQRT of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t003-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.sqrt.t004" +form = "sf.functions.sqrt" +kind = "null_argument" +target = "x" +note = "SQRT of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t004-null"] +required = true + +[[obligation]] +id = "sf.functions.sqrt.t005" +form = "sf.functions.sqrt" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very small and very large values." +catches = "precision at the extremes" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t005-tiny-huge"] +required = true + +[[obligation]] +id = "sf.functions.square.t001" +form = "sf.functions.square" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "SQUARE of integers returns FLOAT." +catches = "x*x kept as NUMBER (the type differs), or SQRT computed" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.square.t002" +form = "sf.functions.square" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "SQUARE of NUMBER(10,2)." +catches = "the result left fixed-point" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.square.t003" +form = "sf.functions.square" +kind = "type_combination" +target = "FLOAT" +note = "SQUARE of FLOAT." +catches = "SQRT computed" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.square.t004" +form = "sf.functions.square" +kind = "type_combination" +target = "DECFLOAT" +note = "SQUARE of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.square.t005" +form = "sf.functions.square" +kind = "null_argument" +target = "x" +note = "SQUARE of NULL." +catches = "NULL turned into 0" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t005-null"] +required = true + +[[obligation]] +id = "sf.functions.square.t006" +form = "sf.functions.square" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Squares past the double range." +catches = "infinity versus error" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/square.toml#t006-overflow-inf"] +required = true + +[[obligation]] +id = "sf.functions.tan.t001" +form = "sf.functions.tan" +kind = "type_combination" +target = "FLOAT" +note = "TAN of FLOAT values." +catches = "SIN or COT computed, or degrees" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tan.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.tan.t002" +form = "sf.functions.tan" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "TAN of NUMBER(12,4), converted to FLOAT." +catches = "SIN or COT computed, or degrees; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tan.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.tan.t003" +form = "sf.functions.tan" +kind = "null_argument" +target = "x" +note = "TAN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tan.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.tan.t004" +form = "sf.functions.tan" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Near pi/2." +catches = "pole handling" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tan.toml#t004-near-pole"] +required = true + +[[obligation]] +id = "sf.functions.tanh.t001" +form = "sf.functions.tanh" +kind = "type_combination" +target = "FLOAT" +note = "TANH of FLOAT values." +catches = "SINH or a clamped value" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tanh.toml#t001-float"] +required = true + +[[obligation]] +id = "sf.functions.tanh.t002" +form = "sf.functions.tanh" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "TANH of NUMBER(12,4), converted to FLOAT." +catches = "SINH or a clamped value; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tanh.toml#t002-fixed"] +required = true + +[[obligation]] +id = "sf.functions.tanh.t003" +form = "sf.functions.tanh" +kind = "null_argument" +target = "x" +note = "TANH of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tanh.toml#t003-null"] +required = true + +[[obligation]] +id = "sf.functions.tanh.t004" +form = "sf.functions.tanh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large arguments saturate to 1 and -1." +catches = "NaN from exp overflow" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/tanh.toml#t004-saturate"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t001" +form = "sf.functions.trunc.trunc" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "TRUNC of integers with a negative scale." +catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a negative scale ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t002" +form = "sf.functions.trunc.trunc" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "TRUNC of NUMBER(12,4) with and without a scale." +catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t003" +form = "sf.functions.trunc.trunc" +kind = "type_combination" +target = "FLOAT" +note = "TRUNC of FLOAT returns FLOAT." +catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t004" +form = "sf.functions.trunc.trunc" +kind = "type_combination" +target = "DECFLOAT" +note = "TRUNC of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t005" +form = "sf.functions.trunc.trunc" +kind = "null_argument" +target = "input_expr" +note = "TRUNC of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t006" +form = "sf.functions.trunc.trunc" +kind = "null_argument" +target = "scale_expr" +note = "TRUNC with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t006-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t007" +form = "sf.functions.trunc.trunc" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and far negative." +catches = "scale capped or wrapping" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t007-scale-past"] +required = true + +[[obligation]] +id = "sf.functions.trunc.trunc.t008" +form = "sf.functions.trunc.trunc" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative values at the rounding step." +catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.trunc.toml#t008-negatives"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t001" +form = "sf.functions.trunc.truncate" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "TRUNCATE of integers with a negative scale." +catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a negative scale ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t002" +form = "sf.functions.trunc.truncate" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "TRUNCATE of NUMBER(12,4) with and without a scale." +catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t003" +form = "sf.functions.trunc.truncate" +kind = "type_combination" +target = "FLOAT" +note = "TRUNCATE of FLOAT returns FLOAT." +catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t004" +form = "sf.functions.trunc.truncate" +kind = "type_combination" +target = "DECFLOAT" +note = "TRUNCATE of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t004-decfloat"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t005" +form = "sf.functions.trunc.truncate" +kind = "null_argument" +target = "input_expr" +note = "TRUNCATE of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t005-null-x"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t006" +form = "sf.functions.trunc.truncate" +kind = "null_argument" +target = "scale_expr" +note = "TRUNCATE with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t006-null-scale"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t007" +form = "sf.functions.trunc.truncate" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and far negative." +catches = "scale capped or wrapping" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t007-scale-past"] +required = true + +[[obligation]] +id = "sf.functions.trunc.truncate.t008" +form = "sf.functions.trunc.truncate" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative values at the rounding step." +catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/trunc.truncate.toml#t008-negatives"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t001" +form = "sf.functions.width-bucket" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "WIDTH_BUCKET of integers, including below and above the range." +catches = "buckets numbered from 0, or the overflow buckets 0 and n+1 missing" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t001-int"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t002" +form = "sf.functions.width-bucket" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "WIDTH_BUCKET of fixed-point values on bucket edges." +catches = "edges assigned to the lower bucket" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t002-dec"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t003" +form = "sf.functions.width-bucket" +kind = "type_combination" +target = "FLOAT" +note = "WIDTH_BUCKET of FLOAT values with FLOAT end points." +catches = "integer end points assumed" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t003-float"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t004" +form = "sf.functions.width-bucket" +kind = "type_combination" +target = "min_value > max_value" +note = "End points given high to low." +catches = "the end points swapped back, numbering buckets upwards" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t004-reversed"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t005" +form = "sf.functions.width-bucket" +kind = "null_argument" +target = "expr" +note = "NULL value." +catches = "NULL put in bucket 0" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t005-null-expr"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t006" +form = "sf.functions.width-bucket" +kind = "null_argument" +target = "min_value" +note = "NULL low end point." +catches = "NULL read as 0" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t006-null-min"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t007" +form = "sf.functions.width-bucket" +kind = "null_argument" +target = "max_value" +note = "NULL high end point." +catches = "NULL read as 0" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t007-null-max"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t008" +form = "sf.functions.width-bucket" +kind = "null_argument" +target = "num_buckets" +note = "NULL bucket count." +catches = "NULL read as 1" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t008-null-n"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t009" +form = "sf.functions.width-bucket" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Values at the documented 2^53 - 1 limit." +catches = "precision loss rounding the limit into another bucket" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t009-range-edge"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t010" +form = "sf.functions.width-bucket" +kind = "error_parity" +target = "error" +note = "num_buckets 0 is an error." +catches = "bucket 0 returned" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t010-zero-buckets"] +required = true + +[[obligation]] +id = "sf.functions.width-bucket.t011" +form = "sf.functions.width-bucket" +kind = "error_parity" +target = "error" +note = "Equal end points are an error." +catches = "division by zero returning NULL or infinity" +reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" +cases = ["cases/translation/functions/width-bucket.toml#t011-equal-ends"] +required = true From f607637127768f81247463b7e2e74f83f278a961 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:31:38 +0000 Subject: [PATCH 02/95] Translation obligations: batch 1 audit fixes Answers the auditor's review of a62b1f35 (45 non-pass lines). No obligation id moves; 12 obligations are added at the end of their forms. - Discriminating rows: ROUND ties at 1450 and at scale 2, more than 17 significant digits and 0.29 for CEIL/FLOOR/TRUNC/TRUNCATE on DECFLOAT, a non-perfect cube for CBRT on DECFLOAT, rows where a FLOAT operand or a capped scale changes the value for + and *, and a quadrant IV row for ATAN2. - Negative zero is written as a FLOAT literal (-0e0); DIV0's zero-result case uses FLOAT and DIV0NULL's adds a quotient below the result scale. - Overflow rows really overflow (COSH/SINH at 711), and DEGREES/RADIANS rows overflow only under the wrong evaluation order. - Domain edges, each recording whatever Snowflake returns: ACOS and ASIN outside [-1, 1], ACOSH below 1, ATANH at and past 1, SQRT(-1), COT(0), POW of a negative base with a fractional exponent, and MOD and % by zero. - Notes and catches corrected where they did not match their rows. --- .../expressions/operators-arithmetic.add.toml | 3 +- .../operators-arithmetic.modulo.toml | 15 +- .../operators-arithmetic.multiply.toml | 2 + .../operators-arithmetic.unary-minus.toml | 1 + cases/translation/functions/abs.toml | 2 +- cases/translation/functions/acos.toml | 12 ++ cases/translation/functions/acosh.toml | 11 ++ cases/translation/functions/asin.toml | 12 ++ cases/translation/functions/atan2.toml | 5 +- cases/translation/functions/atanh.toml | 24 +++ cases/translation/functions/cbrt.toml | 2 + cases/translation/functions/ceil.toml | 7 +- cases/translation/functions/cosh.toml | 6 +- cases/translation/functions/cot.toml | 11 ++ cases/translation/functions/degrees.toml | 17 +- cases/translation/functions/div0.toml | 8 +- cases/translation/functions/div0null.toml | 3 +- cases/translation/functions/floor.toml | 6 +- cases/translation/functions/log.toml | 2 +- cases/translation/functions/mod.toml | 11 ++ cases/translation/functions/pow.pow.toml | 13 +- cases/translation/functions/pow.power.toml | 13 +- cases/translation/functions/radians.toml | 6 +- .../functions/round.round-expr-scale.toml | 14 +- cases/translation/functions/round.round.toml | 14 +- cases/translation/functions/sign.toml | 2 +- cases/translation/functions/sinh.toml | 6 +- cases/translation/functions/sqrt.toml | 11 ++ cases/translation/functions/trunc.trunc.toml | 6 +- .../translation/functions/trunc.truncate.toml | 8 +- cases/translation/functions/width-bucket.toml | 4 +- .../translation/obligations/expressions.toml | 15 +- .../translation/obligations/functions.toml | 173 +++++++++++++++--- 33 files changed, 371 insertions(+), 74 deletions(-) diff --git a/cases/translation/expressions/operators-arithmetic.add.toml b/cases/translation/expressions/operators-arithmetic.add.toml index d55d07235..cebe1fa3e 100644 --- a/cases/translation/expressions/operators-arithmetic.add.toml +++ b/cases/translation/expressions/operators-arithmetic.add.toml @@ -52,11 +52,12 @@ tags = ["type-combination"] [[case]] id = "t005-num-float-102" -description = "a NUMBER(10,2) + b FLOAT. Catches: fixed-point turned into FLOAT before adding or the reverse" +description = "a NUMBER(10,2) + b FLOAT. Catches: the FLOAT operand cast to NUMBER(10,2) (1.00 instead of 1.001) or a fixed-point result type" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] rows = [ ["0.10", "0.2"], ["2.50", "-0.25"], + ["1.00", "0.001"], ] query = "SELECT a + b AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/expressions/operators-arithmetic.modulo.toml b/cases/translation/expressions/operators-arithmetic.modulo.toml index 6dd7df4e7..54cd6c631 100644 --- a/cases/translation/expressions/operators-arithmetic.modulo.toml +++ b/cases/translation/expressions/operators-arithmetic.modulo.toml @@ -32,7 +32,7 @@ id = "t003-num-num-102124" description = "a NUMBER(10,2) % b NUMBER(12,4). Catches: result scale taken from one operand" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ - ["5.25", "1.1000"], + ["5.25", "1.0001"], ["-0.75", "0.5000"], ] query = "SELECT a % b AS r FROM input ORDER BY ordinal" @@ -87,7 +87,7 @@ tags = ["null"] [[case]] id = "t008-both-negative" -description = "Both operands negative. Catches: sign of the remainder taken from the divisor" +description = "Both operands negative. Catches: remainder made non-negative (Euclidean or ABS): 2 instead of -1 for -7 % -3, 4 instead of -1 for -1 % -5" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ ["-7", "-3"], @@ -107,3 +107,14 @@ rows = [ query = "SELECT a % b AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["error"] + +[[case]] +id = "t010-zero-divisor" +description = "A zero divisor: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NULL for x % 0 (DuckDB) where Snowflake raises division by zero" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5", "0"], +] +query = "SELECT a % b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/expressions/operators-arithmetic.multiply.toml b/cases/translation/expressions/operators-arithmetic.multiply.toml index 17473caa9..53f1b2b28 100644 --- a/cases/translation/expressions/operators-arithmetic.multiply.toml +++ b/cases/translation/expressions/operators-arithmetic.multiply.toml @@ -57,6 +57,7 @@ columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" } rows = [ ["1.10", "1.1"], ["2.00", "-0.5"], + ["1.10", "0.001"], ] query = "SELECT a * b AS r FROM input ORDER BY ordinal" order = "ordered" @@ -115,6 +116,7 @@ columns = [{ name = "a", type = "NUMBER(20,10)" }, { name = "b", type = "NUMBER( rows = [ ["1.0000000001", "1.0000000001"], ["0.3333333333", "3.0000000000"], + ["1.2345678901", "1.2345678901"], ] query = "SELECT a * b AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/expressions/operators-arithmetic.unary-minus.toml b/cases/translation/expressions/operators-arithmetic.unary-minus.toml index a32ff8bc6..1784007be 100644 --- a/cases/translation/expressions/operators-arithmetic.unary-minus.toml +++ b/cases/translation/expressions/operators-arithmetic.unary-minus.toml @@ -34,6 +34,7 @@ columns = [{ name = "a", type = "FLOAT" }] rows = [ ["1.5e-10"], ["-2.25"], + ["0e0"], ] query = "SELECT -a AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/abs.toml b/cases/translation/functions/abs.toml index 3aa1c2a5d..5a37d1cdc 100644 --- a/cases/translation/functions/abs.toml +++ b/cases/translation/functions/abs.toml @@ -33,7 +33,7 @@ id = "t003-float" description = "ABS of FLOAT. Catches: negative zero and tiny negatives" columns = [{ name = "x", type = "FLOAT" }] rows = [ - ["-0.0"], + ["-0e0"], ["-1e-300"], ["2.5"], ] diff --git a/cases/translation/functions/acos.toml b/cases/translation/functions/acos.toml index 91955ea5d..269a7749b 100644 --- a/cases/translation/functions/acos.toml +++ b/cases/translation/functions/acos.toml @@ -50,3 +50,15 @@ rows = [ query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t005-outside" +description = "ACOS at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1.5"], + ["-1.0001"], +] +query = "SELECT ACOS(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/acosh.toml b/cases/translation/functions/acosh.toml index 42a474d33..e891bec72 100644 --- a/cases/translation/functions/acosh.toml +++ b/cases/translation/functions/acosh.toml @@ -49,3 +49,14 @@ rows = [ query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t005-below-one" +description = "ACOSH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0.5"], +] +query = "SELECT ACOSH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/asin.toml b/cases/translation/functions/asin.toml index 1cc22329d..41d5598c8 100644 --- a/cases/translation/functions/asin.toml +++ b/cases/translation/functions/asin.toml @@ -49,3 +49,15 @@ rows = [ query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t005-outside" +description = "ASIN at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1.5"], + ["-2"], +] +query = "SELECT ASIN(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/atan2.toml b/cases/translation/functions/atan2.toml index 1292bf403..c85e25d88 100644 --- a/cases/translation/functions/atan2.toml +++ b/cases/translation/functions/atan2.toml @@ -11,6 +11,7 @@ rows = [ ["-1", "-1"], ["1", "-1"], ["0", "-1"], + ["-1", "2"], ] query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" order = "ordered" @@ -68,8 +69,8 @@ description = "Signed zeros on the negative x axis. Catches: the sign of zero lo columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ ["0", "0"], - ["-0.0", "-1"], - ["0.0", "-1"], + ["-0e0", "-1"], + ["0e0", "-1"], ] query = "SELECT ATAN2(y, x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/atanh.toml b/cases/translation/functions/atanh.toml index 85dc3ac62..1b2796c90 100644 --- a/cases/translation/functions/atanh.toml +++ b/cases/translation/functions/atanh.toml @@ -45,6 +45,30 @@ columns = [{ name = "x", type = "FLOAT" }] rows = [ ["0.999999"], ["-0.999999"], + ["0.9999999999999999"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t005-edge" +description = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1"], + ["-1"], +] +query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t006-outside" +description = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["2"], ] query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/cbrt.toml b/cases/translation/functions/cbrt.toml index 8207113ce..87891babb 100644 --- a/cases/translation/functions/cbrt.toml +++ b/cases/translation/functions/cbrt.toml @@ -10,6 +10,7 @@ rows = [ ["27"], ["2"], ["0.125"], + ["-27"], ] query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" order = "ordered" @@ -34,6 +35,7 @@ columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["27"], ["-8"], + ["2"], ] query = "SELECT CBRT(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/ceil.toml b/cases/translation/functions/ceil.toml index 994cf8b7c..9fcc80ae0 100644 --- a/cases/translation/functions/ceil.toml +++ b/cases/translation/functions/ceil.toml @@ -44,13 +44,15 @@ tags = ["type-combination"] [[case]] id = "t004-decfloat" -description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["2.25"], ["-2.25"], + ["0.29"], + ["12345678901234567890.25"], ] -query = "SELECT CEIL(x, 1) AS r FROM input ORDER BY ordinal" +query = "SELECT CEIL(x, 1) AS r, CEIL(x, 2) AS s FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] @@ -96,6 +98,7 @@ columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ ["-2.15"], ["-0.01"], + ["2.11"], ] query = "SELECT CEIL(x, 1) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/cosh.toml b/cases/translation/functions/cosh.toml index d0d42cdb5..c2f378661 100644 --- a/cases/translation/functions/cosh.toml +++ b/cases/translation/functions/cosh.toml @@ -40,11 +40,11 @@ tags = ["null"] [[case]] id = "t004-overflow-inf" -description = "Arguments past the double range. Catches: infinity versus error" +description = "Arguments just past the double range (710.48). Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ - ["710"], - ["-710"], + ["711"], + ["-711"], ] query = "SELECT COSH(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/cot.toml b/cases/translation/functions/cot.toml index f83a23ace..d6862a893 100644 --- a/cases/translation/functions/cot.toml +++ b/cases/translation/functions/cot.toml @@ -49,3 +49,14 @@ rows = [ query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t005-pole" +description = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["0"], +] +query = "SELECT COT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/degrees.toml b/cases/translation/functions/degrees.toml index a6526bd05..57fd49e3e 100644 --- a/cases/translation/functions/degrees.toml +++ b/cases/translation/functions/degrees.toml @@ -40,11 +40,22 @@ tags = ["null"] [[case]] id = "t004-large" -description = "Large radian values. Catches: overflow handling" +description = "Large radian values. Catches: x*180 computed before dividing by pi, overflowing where the result is finite" columns = [{ name = "x", type = "FLOAT" }] rows = [ - ["1e300"], - ["-1e305"], + ["1e306"], + ["-1e306"], +] +query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t005-overflow" +description = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: Infinity versus an error" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1e307"], ] query = "SELECT DEGREES(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/div0.toml b/cases/translation/functions/div0.toml index ecfd9f5f4..e1ffdd915 100644 --- a/cases/translation/functions/div0.toml +++ b/cases/translation/functions/div0.toml @@ -64,11 +64,11 @@ tags = ["null"] [[case]] id = "t006-neg-zero" -description = "Negative dividends over zero. Catches: -0 or a negative zero result" -columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] +description = "Negative dividends over zero. Catches: the sign of the dividend kept on the zero result (-0 instead of 0)" +columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ - ["-5.00", "0.00"], - ["-0.01", "3.00"], + ["-5", "0"], + ["-0.01", "3"], ] query = "SELECT DIV0(a, b) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/div0null.toml b/cases/translation/functions/div0null.toml index 5ec13cf71..d7564c0f3 100644 --- a/cases/translation/functions/div0null.toml +++ b/cases/translation/functions/div0null.toml @@ -64,11 +64,12 @@ tags = ["null"] [[case]] id = "t006-neg-zero" -description = "Negative dividends over zero. Catches: -0 or a negative zero result" +description = "Negative dividends over zero. Catches: a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ ["-5.00", "0.00"], ["-0.01", "3.00"], + ["-0.01", "99999999.00"], ] query = "SELECT DIV0NULL(a, b) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/floor.toml b/cases/translation/functions/floor.toml index 8ce566704..9f7b811fd 100644 --- a/cases/translation/functions/floor.toml +++ b/cases/translation/functions/floor.toml @@ -44,13 +44,15 @@ tags = ["type-combination"] [[case]] id = "t004-decfloat" -description = "FLOOR of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +description = "FLOOR of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["2.25"], ["-2.25"], + ["0.29"], + ["12345678901234567890.25"], ] -query = "SELECT FLOOR(x, 1) AS r FROM input ORDER BY ordinal" +query = "SELECT FLOOR(x, 1) AS r, FLOOR(x, 2) AS s FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] diff --git a/cases/translation/functions/log.toml b/cases/translation/functions/log.toml index 9ccfe5a4e..5e739858c 100644 --- a/cases/translation/functions/log.toml +++ b/cases/translation/functions/log.toml @@ -29,7 +29,7 @@ tags = ["type-combination"] [[case]] id = "t003-float" -description = "LOG of FLOAT values. Catches: computed in decimal" +description = "LOG of FLOAT values. Catches: arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ ["2.718281828459045", "10"], diff --git a/cases/translation/functions/mod.toml b/cases/translation/functions/mod.toml index c0ffb3fca..afa4f6fb0 100644 --- a/cases/translation/functions/mod.toml +++ b/cases/translation/functions/mod.toml @@ -96,3 +96,14 @@ rows = [ query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t009-zero-divisor" +description = "MOD by zero: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["5", "0"], +] +query = "SELECT MOD(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/pow.pow.toml b/cases/translation/functions/pow.pow.toml index 389c7ba6d..9f46114a3 100644 --- a/cases/translation/functions/pow.pow.toml +++ b/cases/translation/functions/pow.pow.toml @@ -29,7 +29,7 @@ tags = ["type-combination"] [[case]] id = "t003-float" -description = "POW of FLOAT values. Catches: computed in decimal" +description = "POW of FLOAT values. Catches: arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ ["1.5", "2.5"], @@ -100,3 +100,14 @@ rows = [ query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t009-neg-frac" +description = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-8", "0.5"], +] +query = "SELECT POW(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/pow.power.toml b/cases/translation/functions/pow.power.toml index 670c8a002..6f58d1127 100644 --- a/cases/translation/functions/pow.power.toml +++ b/cases/translation/functions/pow.power.toml @@ -29,7 +29,7 @@ tags = ["type-combination"] [[case]] id = "t003-float" -description = "POWER of FLOAT values. Catches: computed in decimal" +description = "POWER of FLOAT values. Catches: arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ ["1.5", "2.5"], @@ -100,3 +100,14 @@ rows = [ query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t009-neg-frac" +description = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] +rows = [ + ["-8", "0.5"], +] +query = "SELECT POWER(x, y) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/radians.toml b/cases/translation/functions/radians.toml index 40540cd6e..ff8ba8780 100644 --- a/cases/translation/functions/radians.toml +++ b/cases/translation/functions/radians.toml @@ -40,11 +40,11 @@ tags = ["null"] [[case]] id = "t004-large" -description = "Large degree values. Catches: overflow handling" +description = "Large degree values. Catches: x*pi computed before dividing by 180, overflowing where the result is finite" columns = [{ name = "x", type = "FLOAT" }] rows = [ - ["1e300"], - ["-1e305"], + ["1e308"], + ["-1.7e308"], ] query = "SELECT RADIANS(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/round.round-expr-scale.toml b/cases/translation/functions/round.round-expr-scale.toml index eac20bb95..39e1f7db2 100644 --- a/cases/translation/functions/round.round-expr-scale.toml +++ b/cases/translation/functions/round.round-expr-scale.toml @@ -4,11 +4,11 @@ session_profile = "utc-v1" [[case]] id = "t001-int" -description = "ROUND of integers with a negative scale. Catches: a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +description = "ROUND of integers with a negative scale. Catches: half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ - ["1550", "-2"], - ["-1550", "-2"], + ["1450", "-2"], + ["-1450", "-2"], ["1549", "-2"], ] query = "SELECT ROUND(EXPR => x, SCALE => s) AS r FROM input ORDER BY ordinal" @@ -70,13 +70,13 @@ tags = ["type-combination"] [[case]] id = "t006-mode-away" -description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even" +description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ - ["2.5000"], + ["0.2500"], ["-0.0250"], ] -query = "SELECT ROUND(EXPR => x, SCALE => 1, ROUNDING_MODE => 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" +query = "SELECT ROUND(EXPR => x, SCALE => 2, ROUNDING_MODE => 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] @@ -117,7 +117,7 @@ tags = ["boundary"] [[case]] id = "t010-max" -description = "Rounding the largest 38-digit integers up to a power of ten. Catches: overflow to FLOAT" +description = "Rounding 38-digit integers at scale -1, near the 38-digit limit. Catches: overflow to FLOAT" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ ["99999999999999999999999999999999999949"], diff --git a/cases/translation/functions/round.round.toml b/cases/translation/functions/round.round.toml index 0a6c64355..e1e518c0f 100644 --- a/cases/translation/functions/round.round.toml +++ b/cases/translation/functions/round.round.toml @@ -4,11 +4,11 @@ session_profile = "utc-v1" [[case]] id = "t001-int" -description = "ROUND of integers with a negative scale. Catches: a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +description = "ROUND of integers with a negative scale. Catches: half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ - ["1550", "-2"], - ["-1550", "-2"], + ["1450", "-2"], + ["-1450", "-2"], ["1549", "-2"], ] query = "SELECT ROUND(x, s) AS r FROM input ORDER BY ordinal" @@ -70,13 +70,13 @@ tags = ["type-combination"] [[case]] id = "t006-mode-away" -description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even" +description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ - ["2.5000"], + ["0.2500"], ["-0.0250"], ] -query = "SELECT ROUND(x, 1, 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" +query = "SELECT ROUND(x, 2, 'HALF_AWAY_FROM_ZERO') AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] @@ -117,7 +117,7 @@ tags = ["boundary"] [[case]] id = "t010-max" -description = "Rounding the largest 38-digit integers up to a power of ten. Catches: overflow to FLOAT" +description = "Rounding 38-digit integers at scale -1, near the 38-digit limit. Catches: overflow to FLOAT" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ ["99999999999999999999999999999999999949"], diff --git a/cases/translation/functions/sign.toml b/cases/translation/functions/sign.toml index 5a93ed105..dc3aff84c 100644 --- a/cases/translation/functions/sign.toml +++ b/cases/translation/functions/sign.toml @@ -4,7 +4,7 @@ session_profile = "utc-v1" [[case]] id = "t001-int" -description = "SIGN of integers. Catches: ABS computed, or -0.001 rounded to 0 before taking the sign" +description = "SIGN of integers. Catches: ABS computed (-7 gives 7 instead of -1)" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ ["-7"], diff --git a/cases/translation/functions/sinh.toml b/cases/translation/functions/sinh.toml index 2f340bc59..dde4176a1 100644 --- a/cases/translation/functions/sinh.toml +++ b/cases/translation/functions/sinh.toml @@ -40,11 +40,11 @@ tags = ["null"] [[case]] id = "t004-overflow-inf" -description = "Arguments past the double range. Catches: infinity versus error" +description = "Arguments just past the double range (710.48). Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ - ["710"], - ["-710"], + ["711"], + ["-711"], ] query = "SELECT SINH(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/sqrt.toml b/cases/translation/functions/sqrt.toml index 701721f5b..8a4505485 100644 --- a/cases/translation/functions/sqrt.toml +++ b/cases/translation/functions/sqrt.toml @@ -61,3 +61,14 @@ rows = [ query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t006-negative" +description = "SQRT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["-1"], +] +query = "SELECT SQRT(x) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/trunc.trunc.toml b/cases/translation/functions/trunc.trunc.toml index 5881d2ca2..98658af56 100644 --- a/cases/translation/functions/trunc.trunc.toml +++ b/cases/translation/functions/trunc.trunc.toml @@ -44,13 +44,15 @@ tags = ["type-combination"] [[case]] id = "t004-decfloat" -description = "TRUNC of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +description = "TRUNC of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["2.25"], ["-2.25"], + ["0.29"], + ["12345678901234567890.25"], ] -query = "SELECT TRUNC(x, 1) AS r FROM input ORDER BY ordinal" +query = "SELECT TRUNC(x, 1) AS r, TRUNC(x, 2) AS s FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] diff --git a/cases/translation/functions/trunc.truncate.toml b/cases/translation/functions/trunc.truncate.toml index 744cb3ec3..5a4fe0ab3 100644 --- a/cases/translation/functions/trunc.truncate.toml +++ b/cases/translation/functions/trunc.truncate.toml @@ -44,13 +44,15 @@ tags = ["type-combination"] [[case]] id = "t004-decfloat" -description = "TRUNCATE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" +description = "TRUNCATE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["2.25"], ["-2.25"], + ["0.29"], + ["12345678901234567890.25"], ] -query = "SELECT TRUNCATE(x, 1) AS r FROM input ORDER BY ordinal" +query = "SELECT TRUNCATE(x, 1) AS r, TRUNCATE(x, 2) AS s FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] @@ -78,7 +80,7 @@ tags = ["null"] [[case]] id = "t007-scale-past" -description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" +description = "Scales past the input scale and far negative. Catches: a negative scale beyond the integer digits not giving 0, or a scale above the input scale changing the value" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ ["12.3456", "6"], diff --git a/cases/translation/functions/width-bucket.toml b/cases/translation/functions/width-bucket.toml index d62fb8931..205a8d8d2 100644 --- a/cases/translation/functions/width-bucket.toml +++ b/cases/translation/functions/width-bucket.toml @@ -42,7 +42,7 @@ rows = [ ["0.75"], ["-0.1"], ] -query = "SELECT WIDTH_BUCKET(x, 0.25, 1.25, 5) AS r FROM input ORDER BY ordinal" +query = "SELECT WIDTH_BUCKET(x, 0.25::FLOAT, 1.25::FLOAT, 5) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["type-combination"] @@ -111,6 +111,8 @@ columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ ["9007199254740991"], ["-9007199254740991"], + ["-3002399751580331"], + ["-3002399751580330"], ] query = "SELECT WIDTH_BUCKET(x, -9007199254740991, 9007199254740991, 3) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/coverage/translation/obligations/expressions.toml b/coverage/translation/obligations/expressions.toml index d970ae031..30f194e4b 100644 --- a/coverage/translation/obligations/expressions.toml +++ b/coverage/translation/obligations/expressions.toml @@ -52,7 +52,7 @@ form = "sf.expressions.operators-arithmetic.add" kind = "type_combination" target = "NUMBER(10,2) + FLOAT" note = "a NUMBER(10,2) + b FLOAT." -catches = "fixed-point turned into FLOAT before adding or the reverse" +catches = "the FLOAT operand cast to NUMBER(10,2) (1.00 instead of 1.001) or a fixed-point result type" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t005-num-float-102"] required = true @@ -332,7 +332,7 @@ kind = "boundary" subkind = "negative" target = "negative" note = "Both operands negative." -catches = "sign of the remainder taken from the divisor" +catches = "remainder made non-negative (Euclidean or ABS): 2 instead of -1 for -7 % -3, 4 instead of -1 for -1 % -5" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t008-both-negative"] required = true @@ -348,6 +348,17 @@ reading = "operators-arithmetic: a and b are numeric expressions; the page's sca cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t009-mod-text"] required = true +[[obligation]] +id = "sf.expressions.operators-arithmetic.modulo.t010" +form = "sf.expressions.operators-arithmetic.modulo" +kind = "error_parity" +target = "error" +note = "A zero divisor: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NULL for x % 0 (DuckDB) where Snowflake raises division by zero" +reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t010-zero-divisor"] +required = true + [[obligation]] id = "sf.expressions.operators-arithmetic.multiply.t001" form = "sf.expressions.operators-arithmetic.multiply" diff --git a/coverage/translation/obligations/functions.toml b/coverage/translation/obligations/functions.toml index 4ff026cf0..0fc3142e7 100644 --- a/coverage/translation/obligations/functions.toml +++ b/coverage/translation/obligations/functions.toml @@ -103,6 +103,18 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/acos.toml#t004-edges"] required = true +[[obligation]] +id = "sf.functions.acos.t005" +form = "sf.functions.acos" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "ACOS at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acos.toml#t005-outside"] +required = true + [[obligation]] id = "sf.functions.acosh.t001" form = "sf.functions.acosh" @@ -148,6 +160,18 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/acosh.toml#t004-edge"] required = true +[[obligation]] +id = "sf.functions.acosh.t005" +form = "sf.functions.acosh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "ACOSH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/acosh.toml#t005-below-one"] +required = true + [[obligation]] id = "sf.functions.asin.t001" form = "sf.functions.asin" @@ -193,6 +217,18 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/asin.toml#t004-odd"] required = true +[[obligation]] +id = "sf.functions.asin.t005" +form = "sf.functions.asin" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "ASIN at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/asin.toml#t005-outside"] +required = true + [[obligation]] id = "sf.functions.asinh.t001" form = "sf.functions.asinh" @@ -417,6 +453,30 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/atanh.toml#t004-near-one"] required = true +[[obligation]] +id = "sf.functions.atanh.t005" +form = "sf.functions.atanh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t005-edge"] +required = true + +[[obligation]] +id = "sf.functions.atanh.t006" +form = "sf.functions.atanh" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/atanh.toml#t006-outside"] +required = true + [[obligation]] id = "sf.functions.cbrt.t001" form = "sf.functions.cbrt" @@ -512,7 +572,7 @@ form = "sf.functions.ceil" kind = "type_combination" target = "DECFLOAT" note = "CEIL of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT" +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/ceil.toml#t004-decfloat"] required = true @@ -647,7 +707,7 @@ form = "sf.functions.cosh" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Arguments past the double range." +note = "Arguments just past the double range (710.48)." catches = "infinity versus error" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" cases = ["cases/translation/functions/cosh.toml#t004-overflow-inf"] @@ -698,6 +758,18 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/cot.toml#t004-small"] required = true +[[obligation]] +id = "sf.functions.cot.t005" +form = "sf.functions.cot" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t005-pole"] +required = true + [[obligation]] id = "sf.functions.degrees.t001" form = "sf.functions.degrees" @@ -738,11 +810,23 @@ kind = "boundary" subkind = "magnitude" target = "magnitude" note = "Large radian values." -catches = "overflow handling" +catches = "x*180 computed before dividing by pi, overflowing where the result is finite" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" cases = ["cases/translation/functions/degrees.toml#t004-large"] required = true +[[obligation]] +id = "sf.functions.degrees.t005" +form = "sf.functions.degrees" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "Infinity versus an error" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t005-overflow"] +required = true + [[obligation]] id = "sf.functions.div0.t001" form = "sf.functions.div0" @@ -805,7 +889,7 @@ kind = "boundary" subkind = "negative" target = "negative" note = "Negative dividends over zero." -catches = "-0 or a negative zero result" +catches = "the sign of the dividend kept on the zero result (-0 instead of 0)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" cases = ["cases/translation/functions/div0.toml#t006-neg-zero"] required = true @@ -872,7 +956,7 @@ kind = "boundary" subkind = "negative" target = "negative" note = "Negative dividends over zero." -catches = "-0 or a negative zero result" +catches = "a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" cases = ["cases/translation/functions/div0null.toml#t006-neg-zero"] required = true @@ -1039,7 +1123,7 @@ form = "sf.functions.floor" kind = "type_combination" target = "DECFLOAT" note = "FLOOR of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT" +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/floor.toml#t004-decfloat"] required = true @@ -1185,7 +1269,7 @@ form = "sf.functions.log" kind = "type_combination" target = "FLOAT" note = "LOG of FLOAT values." -catches = "computed in decimal" +catches = "arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" cases = ["cases/translation/functions/log.toml#t003-float"] required = true @@ -1358,6 +1442,17 @@ reading = "the page: both arguments numeric expressions; the numeric category gi cases = ["cases/translation/functions/mod.toml#t008-max"] required = true +[[obligation]] +id = "sf.functions.mod.t009" +form = "sf.functions.mod" +kind = "error_parity" +target = "error" +note = "MOD by zero: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t009-zero-divisor"] +required = true + [[obligation]] id = "sf.functions.pi.t001" form = "sf.functions.pi" @@ -1397,7 +1492,7 @@ form = "sf.functions.pow.pow" kind = "type_combination" target = "FLOAT" note = "POW of FLOAT values." -catches = "computed in decimal" +catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" cases = ["cases/translation/functions/pow.pow.toml#t003-float"] required = true @@ -1459,6 +1554,18 @@ reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric cases = ["cases/translation/functions/pow.pow.toml#t008-neg-base"] required = true +[[obligation]] +id = "sf.functions.pow.pow.t009" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, NULL or an error differing from Snowflake" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t009-neg-frac"] +required = true + [[obligation]] id = "sf.functions.pow.power.t001" form = "sf.functions.pow.power" @@ -1487,7 +1594,7 @@ form = "sf.functions.pow.power" kind = "type_combination" target = "FLOAT" note = "POWER of FLOAT values." -catches = "computed in decimal" +catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" cases = ["cases/translation/functions/pow.power.toml#t003-float"] required = true @@ -1549,6 +1656,18 @@ reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric cases = ["cases/translation/functions/pow.power.toml#t008-neg-base"] required = true +[[obligation]] +id = "sf.functions.pow.power.t009" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, NULL or an error differing from Snowflake" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t009-neg-frac"] +required = true + [[obligation]] id = "sf.functions.radians.t001" form = "sf.functions.radians" @@ -1589,7 +1708,7 @@ kind = "boundary" subkind = "magnitude" target = "magnitude" note = "Large degree values." -catches = "overflow handling" +catches = "x*pi computed before dividing by 180, overflowing where the result is finite" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" cases = ["cases/translation/functions/radians.toml#t004-large"] required = true @@ -1600,7 +1719,7 @@ form = "sf.functions.round.round" kind = "type_combination" target = "NUMBER(38,0)" note = "ROUND of integers with a negative scale." -catches = "a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +catches = "half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" cases = ["cases/translation/functions/round.round.toml#t001-int"] required = true @@ -1655,7 +1774,7 @@ form = "sf.functions.round.round" kind = "type_combination" target = "'HALF_AWAY_FROM_ZERO'" note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." -catches = "the mode read as half-even" +catches = "the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" cases = ["cases/translation/functions/round.round.toml#t006-mode-away"] required = true @@ -1700,7 +1819,7 @@ form = "sf.functions.round.round" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Rounding the largest 38-digit integers up to a power of ten." +note = "Rounding 38-digit integers at scale -1, near the 38-digit limit." catches = "overflow to FLOAT" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" cases = ["cases/translation/functions/round.round.toml#t010-max"] @@ -1734,7 +1853,7 @@ form = "sf.functions.round.round-expr-scale" kind = "type_combination" target = "NUMBER(38,0)" note = "ROUND of integers with a negative scale." -catches = "a negative scale ignored, or 1550 rounded to even (1600 expected half away)" +catches = "half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" cases = ["cases/translation/functions/round.round-expr-scale.toml#t001-int"] required = true @@ -1789,7 +1908,7 @@ form = "sf.functions.round.round-expr-scale" kind = "type_combination" target = "'HALF_AWAY_FROM_ZERO'" note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." -catches = "the mode read as half-even" +catches = "the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" cases = ["cases/translation/functions/round.round-expr-scale.toml#t006-mode-away"] required = true @@ -1834,7 +1953,7 @@ form = "sf.functions.round.round-expr-scale" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Rounding the largest 38-digit integers up to a power of ten." +note = "Rounding 38-digit integers at scale -1, near the 38-digit limit." catches = "overflow to FLOAT" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" cases = ["cases/translation/functions/round.round-expr-scale.toml#t010-max"] @@ -1879,7 +1998,7 @@ form = "sf.functions.sign" kind = "type_combination" target = "NUMBER(38,0)" note = "SIGN of integers." -catches = "ABS computed, or -0.001 rounded to 0 before taking the sign" +catches = "ABS computed (-7 gives 7 instead of -1)" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" cases = ["cases/translation/functions/sign.toml#t001-int"] required = true @@ -2013,7 +2132,7 @@ form = "sf.functions.sinh" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Arguments past the double range." +note = "Arguments just past the double range (710.48)." catches = "infinity versus error" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" cases = ["cases/translation/functions/sinh.toml#t004-overflow-inf"] @@ -2075,6 +2194,18 @@ reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric cat cases = ["cases/translation/functions/sqrt.toml#t005-tiny-huge"] required = true +[[obligation]] +id = "sf.functions.sqrt.t006" +form = "sf.functions.sqrt" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "SQRT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/sqrt.toml#t006-negative"] +required = true + [[obligation]] id = "sf.functions.square.t001" form = "sf.functions.square" @@ -2271,7 +2402,7 @@ form = "sf.functions.trunc.trunc" kind = "type_combination" target = "DECFLOAT" note = "TRUNC of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT" +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/trunc.trunc.toml#t004-decfloat"] required = true @@ -2361,7 +2492,7 @@ form = "sf.functions.trunc.truncate" kind = "type_combination" target = "DECFLOAT" note = "TRUNCATE of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT" +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/trunc.truncate.toml#t004-decfloat"] required = true @@ -2395,7 +2526,7 @@ kind = "boundary" subkind = "rounding_scale" target = "rounding_scale" note = "Scales past the input scale and far negative." -catches = "scale capped or wrapping" +catches = "a negative scale beyond the integer digits not giving 0, or a scale above the input scale changing the value" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/trunc.truncate.toml#t007-scale-past"] required = true From 2d5e2b03eca7230f9a71f9582a3c9299941005e7 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:35:19 +0000 Subject: [PATCH 03/95] Translation obligations: batch 1 audit notes ATANH t004 adds 1e-17, where a naive log formula gives 0; CEIL t004 adds 0.28, which FLOAT rounds up at scale 2; the zero-divisor notes on MOD and % say they move to boundary if Snowflake returns a value. --- .../expressions/operators-arithmetic.modulo.toml | 2 +- cases/translation/functions/atanh.toml | 3 ++- cases/translation/functions/ceil.toml | 3 ++- cases/translation/functions/mod.toml | 2 +- coverage/translation/obligations/expressions.toml | 2 +- coverage/translation/obligations/functions.toml | 6 +++--- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cases/translation/expressions/operators-arithmetic.modulo.toml b/cases/translation/expressions/operators-arithmetic.modulo.toml index 54cd6c631..8816e9be9 100644 --- a/cases/translation/expressions/operators-arithmetic.modulo.toml +++ b/cases/translation/expressions/operators-arithmetic.modulo.toml @@ -110,7 +110,7 @@ tags = ["error"] [[case]] id = "t010-zero-divisor" -description = "A zero divisor: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NULL for x % 0 (DuckDB) where Snowflake raises division by zero" +description = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error. Catches: NULL for x % 0 (DuckDB) where Snowflake raises division by zero" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ ["5", "0"], diff --git a/cases/translation/functions/atanh.toml b/cases/translation/functions/atanh.toml index 1b2796c90..ce1b08186 100644 --- a/cases/translation/functions/atanh.toml +++ b/cases/translation/functions/atanh.toml @@ -40,12 +40,13 @@ tags = ["null"] [[case]] id = "t004-near-one" -description = "Close to the domain edges. Catches: precision near the asymptote" +description = "Close to the domain edges. Catches: a naive 0.5*LN((1+x)/(1-x)): 0 instead of 1e-17 near zero, or precision lost near the asymptote" columns = [{ name = "x", type = "FLOAT" }] rows = [ ["0.999999"], ["-0.999999"], ["0.9999999999999999"], + ["1e-17"], ] query = "SELECT ATANH(x) AS r FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/ceil.toml b/cases/translation/functions/ceil.toml index 9fcc80ae0..7d824510d 100644 --- a/cases/translation/functions/ceil.toml +++ b/cases/translation/functions/ceil.toml @@ -44,13 +44,14 @@ tags = ["type-combination"] [[case]] id = "t004-decfloat" -description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" +description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.28 at scale 2 rounded up to 0.29" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ ["2.25"], ["-2.25"], ["0.29"], ["12345678901234567890.25"], + ["0.28"], ] query = "SELECT CEIL(x, 1) AS r, CEIL(x, 2) AS s FROM input ORDER BY ordinal" order = "ordered" diff --git a/cases/translation/functions/mod.toml b/cases/translation/functions/mod.toml index afa4f6fb0..50ee8fac8 100644 --- a/cases/translation/functions/mod.toml +++ b/cases/translation/functions/mod.toml @@ -99,7 +99,7 @@ tags = ["boundary"] [[case]] id = "t009-zero-divisor" -description = "MOD by zero: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" +description = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error. Catches: a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ ["5", "0"], diff --git a/coverage/translation/obligations/expressions.toml b/coverage/translation/obligations/expressions.toml index 30f194e4b..b77bf88b1 100644 --- a/coverage/translation/obligations/expressions.toml +++ b/coverage/translation/obligations/expressions.toml @@ -353,7 +353,7 @@ id = "sf.expressions.operators-arithmetic.modulo.t010" form = "sf.expressions.operators-arithmetic.modulo" kind = "error_parity" target = "error" -note = "A zero divisor: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +note = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error." catches = "NULL for x % 0 (DuckDB) where Snowflake raises division by zero" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t010-zero-divisor"] diff --git a/coverage/translation/obligations/functions.toml b/coverage/translation/obligations/functions.toml index 0fc3142e7..f8ab49764 100644 --- a/coverage/translation/obligations/functions.toml +++ b/coverage/translation/obligations/functions.toml @@ -448,7 +448,7 @@ kind = "boundary" subkind = "magnitude" target = "magnitude" note = "Close to the domain edges." -catches = "precision near the asymptote" +catches = "a naive 0.5*LN((1+x)/(1-x)): 0 instead of 1e-17 near zero, or precision lost near the asymptote" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" cases = ["cases/translation/functions/atanh.toml#t004-near-one"] required = true @@ -572,7 +572,7 @@ form = "sf.functions.ceil" kind = "type_combination" target = "DECFLOAT" note = "CEIL of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.28 at scale 2 rounded up to 0.29" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" cases = ["cases/translation/functions/ceil.toml#t004-decfloat"] required = true @@ -1447,7 +1447,7 @@ id = "sf.functions.mod.t009" form = "sf.functions.mod" kind = "error_parity" target = "error" -note = "MOD by zero: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +note = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error." catches = "a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" cases = ["cases/translation/functions/mod.toml#t009-zero-divisor"] From e8691b63c390e3c15e2a8234361d424fdac95f2f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:39:31 +0000 Subject: [PATCH 04/95] Use underscore case ids in the D151 batch 1 cases The case loader accepts only lower-case identifiers, so the hyphenated ids were rejected. Obligation ids are unchanged; only case ids and the obligation case refs move from t001-slug to t001_slug. --- .../expressions/operators-arithmetic.add.toml | 22 +- .../operators-arithmetic.divide.toml | 22 +- .../operators-arithmetic.modulo.toml | 20 +- .../operators-arithmetic.multiply.toml | 22 +- .../operators-arithmetic.subtract.toml | 22 +- .../operators-arithmetic.unary-minus.toml | 12 +- .../operators-arithmetic.unary-plus.toml | 12 +- cases/translation/functions/abs.toml | 10 +- cases/translation/functions/acos.toml | 10 +- cases/translation/functions/acosh.toml | 10 +- cases/translation/functions/asin.toml | 10 +- cases/translation/functions/asinh.toml | 8 +- cases/translation/functions/atan.toml | 8 +- cases/translation/functions/atan2.toml | 16 +- cases/translation/functions/atanh.toml | 12 +- cases/translation/functions/cbrt.toml | 10 +- cases/translation/functions/ceil.toml | 16 +- cases/translation/functions/cos.toml | 8 +- cases/translation/functions/cosh.toml | 8 +- cases/translation/functions/cot.toml | 10 +- cases/translation/functions/degrees.toml | 10 +- cases/translation/functions/div0.toml | 12 +- cases/translation/functions/div0null.toml | 12 +- cases/translation/functions/exp.toml | 10 +- cases/translation/functions/factorial.toml | 12 +- cases/translation/functions/floor.toml | 16 +- cases/translation/functions/ln.toml | 12 +- cases/translation/functions/log.toml | 20 +- cases/translation/functions/mod.toml | 18 +- cases/translation/functions/pi.toml | 2 +- cases/translation/functions/pow.pow.toml | 18 +- cases/translation/functions/pow.power.toml | 18 +- cases/translation/functions/radians.toml | 8 +- .../functions/round.round-expr-scale.toml | 26 +- cases/translation/functions/round.round.toml | 24 +- cases/translation/functions/sign.toml | 10 +- cases/translation/functions/sin.toml | 8 +- cases/translation/functions/sinh.toml | 8 +- cases/translation/functions/sqrt.toml | 12 +- cases/translation/functions/square.toml | 12 +- cases/translation/functions/tan.toml | 8 +- cases/translation/functions/tanh.toml | 8 +- cases/translation/functions/trunc.trunc.toml | 16 +- .../translation/functions/trunc.truncate.toml | 16 +- cases/translation/functions/width-bucket.toml | 22 +- .../translation/obligations/expressions.toml | 132 ++--- .../translation/obligations/functions.toml | 474 +++++++++--------- 47 files changed, 606 insertions(+), 606 deletions(-) diff --git a/cases/translation/expressions/operators-arithmetic.add.toml b/cases/translation/expressions/operators-arithmetic.add.toml index cebe1fa3e..e6601b746 100644 --- a/cases/translation/expressions/operators-arithmetic.add.toml +++ b/cases/translation/expressions/operators-arithmetic.add.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.add" session_profile = "utc-v1" [[case]] -id = "t001-num-num-380380" +id = "t001_num_num_380380" description = "a NUMBER(38,0) + b NUMBER(38,0). Catches: operands swapped for subtraction or truncated to 32 bits" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -15,7 +15,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-num-num-102380" +id = "t002_num_num_102380" description = "a NUMBER(10,2) + b NUMBER(38,0). Catches: the sum losing the NUMBER(10,2) scale (4 instead of 4.25)" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -27,7 +27,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-num-num-102124" +id = "t003_num_num_102124" description = "a NUMBER(10,2) + b NUMBER(12,4). Catches: the result scale taken from the left operand only (1.31 instead of 1.3125)" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-num-float-380" +id = "t004_num_float_380" description = "a NUMBER(38,0) + b FLOAT. Catches: a FLOAT operand rounded to the integer side" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-num-float-102" +id = "t005_num_float_102" description = "a NUMBER(10,2) + b FLOAT. Catches: the FLOAT operand cast to NUMBER(10,2) (1.00 instead of 1.001) or a fixed-point result type" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-float-float-" +id = "t006_float_float" description = "a FLOAT + b FLOAT. Catches: float addition replaced by decimal addition (0.30000000000000004 vs 0.3)" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -76,7 +76,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-null-a" +id = "t007_null_a" description = "NULL as a, the other operand set. Catches: NULL treated as 0 instead of propagating" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -87,7 +87,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-b" +id = "t008_null_b" description = "NULL as b, the other operand set. Catches: NULL treated as 0 instead of propagating" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-max-38" +id = "t009_max_38" description = "38-digit integers that still fit. Catches: arithmetic done in 64 bits" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -110,7 +110,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-negatives" +id = "t010_negatives" description = "Negative fixed-point sums crossing zero. Catches: sign lost or magnitude added" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -122,7 +122,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-overflow" +id = "t011_overflow" description = "The sum of two 38-digit integers overflows NUMBER(38,0). Catches: wrapping or a silent switch to FLOAT instead of the out-of-range error" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.divide.toml b/cases/translation/expressions/operators-arithmetic.divide.toml index 236974031..10c173738 100644 --- a/cases/translation/expressions/operators-arithmetic.divide.toml +++ b/cases/translation/expressions/operators-arithmetic.divide.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.divide" session_profile = "utc-v1" [[case]] -id = "t001-num-num-380380" +id = "t001_num_num_380380" description = "a NUMBER(38,0) / b NUMBER(38,0). Catches: integer division (3 instead of 3.5) or a truncated rather than rounded scale-6 quotient (0.666666 vs 0.666667)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-num-num-102380" +id = "t002_num_num_102380" description = "a NUMBER(10,2) / b NUMBER(38,0). Catches: the quotient scale (s+6) handled wrongly" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-num-num-102124" +id = "t003_num_num_102124" description = "a NUMBER(10,2) / b NUMBER(12,4). Catches: quotient scale taken from the divisor" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-num-float-380" +id = "t004_num_float_380" description = "a NUMBER(38,0) / b FLOAT. Catches: FLOAT divisor rounded" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-num-float-102" +id = "t005_num_float_102" description = "a NUMBER(10,2) / b FLOAT. Catches: fixed-point / FLOAT result type swapped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-float-float-" +id = "t006_float_float" description = "a FLOAT / b FLOAT. Catches: float division done in decimal" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -76,7 +76,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-null-a" +id = "t007_null_a" description = "NULL as a, the other operand set. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -87,7 +87,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-b" +id = "t008_null_b" description = "NULL as b, the other operand set. Catches: NULL divisor turned into the division-by-zero error or 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-scale-12" +id = "t009_scale_12" description = "Scales near the 12-digit quotient cap. Catches: the quotient scale not capped at 12" columns = [{ name = "a", type = "NUMBER(20,8)" }, { name = "b", type = "NUMBER(20,8)" }] rows = [ @@ -110,7 +110,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-neg-quotient" +id = "t010_neg_quotient" description = "Negative quotients that round at the last digit. Catches: rounding toward zero instead of half away from zero" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -122,7 +122,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-div-zero" +id = "t011_div_zero" description = "Division by zero is an error. Catches: NULL, 0 or infinity instead of the division-by-zero error" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.modulo.toml b/cases/translation/expressions/operators-arithmetic.modulo.toml index 8816e9be9..0000f5fbf 100644 --- a/cases/translation/expressions/operators-arithmetic.modulo.toml +++ b/cases/translation/expressions/operators-arithmetic.modulo.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.modulo" session_profile = "utc-v1" [[case]] -id = "t001-num-num-380380" +id = "t001_num_num_380380" description = "a NUMBER(38,0) % b NUMBER(38,0). Catches: floor-based modulo (2 instead of -1): MOD truncates toward zero" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-num-num-102380" +id = "t002_num_num_102380" description = "a NUMBER(10,2) % b NUMBER(38,0). Catches: fractional remainder dropped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-num-num-102124" +id = "t003_num_num_102124" description = "a NUMBER(10,2) % b NUMBER(12,4). Catches: result scale taken from one operand" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-num-float-380" +id = "t004_num_float_380" description = "a NUMBER(38,0) % b FLOAT. Catches: FLOAT divisor rounded" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-float-float-" +id = "t005_float_float" description = "a FLOAT % b FLOAT. Catches: floor-based float modulo" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-null-a" +id = "t006_null_a" description = "NULL as a, the other operand set. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -75,7 +75,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-null-b" +id = "t007_null_b" description = "NULL as b, the other operand set. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -86,7 +86,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-both-negative" +id = "t008_both_negative" description = "Both operands negative. Catches: remainder made non-negative (Euclidean or ABS): 2 instead of -1 for -7 % -3, 4 instead of -1 for -1 % -5" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t009-mod-text" +id = "t009_mod_text" description = "A non-numeric string operand. Catches: the string silently read as 0 instead of the conversion error" columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -109,7 +109,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t010-zero-divisor" +id = "t010_zero_divisor" description = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error. Catches: NULL for x % 0 (DuckDB) where Snowflake raises division by zero" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.multiply.toml b/cases/translation/expressions/operators-arithmetic.multiply.toml index 53f1b2b28..802239ba5 100644 --- a/cases/translation/expressions/operators-arithmetic.multiply.toml +++ b/cases/translation/expressions/operators-arithmetic.multiply.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.multiply" session_profile = "utc-v1" [[case]] -id = "t001-num-num-380380" +id = "t001_num_num_380380" description = "a NUMBER(38,0) * b NUMBER(38,0). Catches: 32-bit multiplication overflowing" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -15,7 +15,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-num-num-102380" +id = "t002_num_num_102380" description = "a NUMBER(10,2) * b NUMBER(38,0). Catches: the NUMBER(10,2) scale lost" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -27,7 +27,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-num-num-102124" +id = "t003_num_num_102124" description = "a NUMBER(10,2) * b NUMBER(12,4). Catches: the product scale capped or taken from one side (1.5625 vs 1.56)" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-num-float-380" +id = "t004_num_float_380" description = "a NUMBER(38,0) * b FLOAT. Catches: FLOAT operand truncated" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-num-float-102" +id = "t005_num_float_102" description = "a NUMBER(10,2) * b FLOAT. Catches: fixed-point / FLOAT result type swapped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-float-float-" +id = "t006_float_float" description = "a FLOAT * b FLOAT. Catches: underflow to zero handled differently" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -76,7 +76,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-null-a" +id = "t007_null_a" description = "NULL as a, the other operand set. Catches: NULL treated as 1 or 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -87,7 +87,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-b" +id = "t008_null_b" description = "NULL as b, the other operand set. Catches: NULL treated as 1 or 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-float-big" +id = "t009_float_big" description = "FLOAT products near the double range. Catches: overflow to infinity treated differently" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -110,7 +110,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-scale-sum" +id = "t010_scale_sum" description = "Scales adding past 12 digits. Catches: product scale above Snowflake's cap kept or rounded wrongly" columns = [{ name = "a", type = "NUMBER(20,10)" }, { name = "b", type = "NUMBER(20,10)" }] rows = [ @@ -123,7 +123,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-overflow" +id = "t011_overflow" description = "A product beyond 38 digits. Catches: wrapping or silent FLOAT instead of the out-of-range error" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.subtract.toml b/cases/translation/expressions/operators-arithmetic.subtract.toml index 7c971102a..667eaac2f 100644 --- a/cases/translation/expressions/operators-arithmetic.subtract.toml +++ b/cases/translation/expressions/operators-arithmetic.subtract.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.subtract" session_profile = "utc-v1" [[case]] -id = "t001-num-num-380380" +id = "t001_num_num_380380" description = "a NUMBER(38,0) - b NUMBER(38,0). Catches: operands in the wrong order (7 instead of -7)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -15,7 +15,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-num-num-102380" +id = "t002_num_num_102380" description = "a NUMBER(10,2) - b NUMBER(38,0). Catches: scale of the NUMBER(10,2) side dropped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -27,7 +27,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-num-num-102124" +id = "t003_num_num_102124" description = "a NUMBER(10,2) - b NUMBER(12,4). Catches: the result scale taken from one operand only" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(12,4)" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-num-float-380" +id = "t004_num_float_380" description = "a NUMBER(38,0) - b FLOAT. Catches: FLOAT rounded to integer" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-num-float-102" +id = "t005_num_float_102" description = "a NUMBER(10,2) - b FLOAT. Catches: fixed-point / FLOAT result type swapped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "FLOAT" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-float-float-" +id = "t006_float_float" description = "a FLOAT - b FLOAT. Catches: float subtraction done in decimal" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -75,7 +75,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-date-date" +id = "t007_date_date" description = "DATE minus DATE: the page says the result is an integer number of days. Catches: an INTERVAL instead of an integer count of days, or days counted across the leap day wrongly (29, -730)" columns = [{ name = "a", type = "DATE" }, { name = "b", type = "DATE" }] rows = [ @@ -87,7 +87,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t008-null-a" +id = "t008_null_a" description = "NULL as a, the other operand set. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-null-b" +id = "t009_null_b" description = "NULL as b, the other operand set. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -109,7 +109,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t010-to-negative" +id = "t010_to_negative" description = "A smaller minus a larger value. Catches: absolute value taken" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -121,7 +121,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-overflow" +id = "t011_overflow" description = "The difference of the most negative and the largest 38-digit integers overflows. Catches: wrapping instead of the out-of-range error" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.unary-minus.toml b/cases/translation/expressions/operators-arithmetic.unary-minus.toml index 1784007be..00fdd560b 100644 --- a/cases/translation/expressions/operators-arithmetic.unary-minus.toml +++ b/cases/translation/expressions/operators-arithmetic.unary-minus.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.unary-minus" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "Negating integers. Catches: the sign dropped or the value doubled" columns = [{ name = "a", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "Negating NUMBER(10,2). Catches: the scale lost (-1 instead of -1.25)" columns = [{ name = "a", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "Negating FLOAT. Catches: negative zero or exponent handling" columns = [{ name = "a", type = "FLOAT" }] rows = [ @@ -41,7 +41,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "Negating NULL. Catches: NULL turned into 0" columns = [{ name = "a", type = "NUMBER(10,2)" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-max" +id = "t005_max" description = "Negating the largest 38-digit integer. Catches: a 64-bit negation that overflows" columns = [{ name = "a", type = "NUMBER(38,0)" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t006-double-neg" +id = "t006_double_neg" description = "Negation of a negation and of a subtraction. Catches: precedence: - a - b read as -(a - b)" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/expressions/operators-arithmetic.unary-plus.toml b/cases/translation/expressions/operators-arithmetic.unary-plus.toml index 7821cb9c6..03d3e94ae 100644 --- a/cases/translation/expressions/operators-arithmetic.unary-plus.toml +++ b/cases/translation/expressions/operators-arithmetic.unary-plus.toml @@ -3,7 +3,7 @@ construct = "sf.expressions.operators-arithmetic.unary-plus" session_profile = "utc-v1" [[case]] -id = "t001-text" +id = "t001_text" description = "Unary plus on numeric strings converts them. Catches: the string returned unchanged, so ordering and arithmetic stay textual" columns = [{ name = "a", type = "VARCHAR" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "Unary plus keeps a NUMBER(10,2) as it is. Catches: a cast that changes the scale" columns = [{ name = "a", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "Unary plus on FLOAT. Catches: conversion to fixed-point" columns = [{ name = "a", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "Unary plus on NULL text. Catches: NULL replaced or converted to 0" columns = [{ name = "a", type = "VARCHAR" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-empty" +id = "t005_empty" description = "Unary plus on an empty string. Catches: the empty string read as 0 when Snowflake errors, or the reverse" columns = [{ name = "a", type = "VARCHAR" }] rows = [ @@ -62,7 +62,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t006-bad-text" +id = "t006_bad_text" description = "Unary plus on a non-numeric string is an error. Catches: the string passed through unconverted" columns = [{ name = "a", type = "VARCHAR" }] rows = [ diff --git a/cases/translation/functions/abs.toml b/cases/translation/functions/abs.toml index 5a37d1cdc..fd5c434cf 100644 --- a/cases/translation/functions/abs.toml +++ b/cases/translation/functions/abs.toml @@ -3,7 +3,7 @@ construct = "sf.functions.abs" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "ABS of integers. Catches: the sign kept (ABS as identity) or SIGN computed" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "ABS of NUMBER(10,2), keeping its type. Catches: the scale lost or FLOAT returned" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "ABS of FLOAT. Catches: negative zero and tiny negatives" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -42,7 +42,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "ABS of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-extremes" +id = "t005_extremes" description = "The 38-digit and double extremes. Catches: 64-bit arithmetic overflowing" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/acos.toml b/cases/translation/functions/acos.toml index 269a7749b..ed678709a 100644 --- a/cases/translation/functions/acos.toml +++ b/cases/translation/functions/acos.toml @@ -3,7 +3,7 @@ construct = "sf.functions.acos" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ACOS of FLOAT values. Catches: ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -17,7 +17,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ACOS of NUMBER(12,4), converted to FLOAT. Catches: ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ACOS of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-edges" +id = "t004_edges" description = "The domain edges, 1 and -1. Catches: pi returned for 1 or 0 for -1 (sign of the edge)" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-outside" +id = "t005_outside" description = "ACOS at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/acosh.toml b/cases/translation/functions/acosh.toml index e891bec72..5f04ecb11 100644 --- a/cases/translation/functions/acosh.toml +++ b/cases/translation/functions/acosh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.acosh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ACOSH of FLOAT values. Catches: ASINH computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ACOSH of NUMBER(12,4), converted to FLOAT. Catches: ASINH computed; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ACOSH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-edge" +id = "t004_edge" description = "At 1, the domain edge, and large values. Catches: edge returned as NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-below-one" +id = "t005_below_one" description = "ACOSH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/asin.toml b/cases/translation/functions/asin.toml index 41d5598c8..1ab037541 100644 --- a/cases/translation/functions/asin.toml +++ b/cases/translation/functions/asin.toml @@ -3,7 +3,7 @@ construct = "sf.functions.asin" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ASIN of FLOAT values. Catches: ACOS computed instead, or degrees returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ASIN of NUMBER(12,4), converted to FLOAT. Catches: ACOS computed instead, or degrees returned; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ASIN of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-odd" +id = "t004_odd" description = "Odd symmetry at negatives. Catches: absolute value taken" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-outside" +id = "t005_outside" description = "ASIN at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/asinh.toml b/cases/translation/functions/asinh.toml index 46f98bd63..9788025a0 100644 --- a/cases/translation/functions/asinh.toml +++ b/cases/translation/functions/asinh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.asinh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ASINH of FLOAT values. Catches: ACOSH computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ASINH of NUMBER(12,4), converted to FLOAT. Catches: ACOSH computed; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ASINH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large arguments. Catches: precision loss in the log formula" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/atan.toml b/cases/translation/functions/atan.toml index 857d55610..6b298c0af 100644 --- a/cases/translation/functions/atan.toml +++ b/cases/translation/functions/atan.toml @@ -3,7 +3,7 @@ construct = "sf.functions.atan" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ATAN of FLOAT values. Catches: ATAN2 with a swapped argument, or degrees returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ATAN of NUMBER(12,4), converted to FLOAT. Catches: ATAN2 with a swapped argument, or degrees returned; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ATAN of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-huge" +id = "t004_huge" description = "Very large arguments approach pi/2. Catches: overflow handling" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/atan2.toml b/cases/translation/functions/atan2.toml index c85e25d88..6ec67d347 100644 --- a/cases/translation/functions/atan2.toml +++ b/cases/translation/functions/atan2.toml @@ -3,7 +3,7 @@ construct = "sf.functions.atan2" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ATAN2 in all four quadrants. Catches: arguments swapped (ATAN2(y, x) vs ATAN2(x, y)) or ATAN(y/x) losing the quadrant" columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -18,7 +18,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ATAN2 of NUMBER(12,4), converted to DOUBLE. Catches: the result left fixed-point" columns = [{ name = "y", type = "NUMBER(12,4)" }, { name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-text" +id = "t003_text" description = "ATAN2 of numeric strings, converted to DOUBLE. Catches: the strings rejected" columns = [{ name = "y", type = "VARCHAR" }, { name = "x", type = "VARCHAR" }] rows = [ @@ -42,7 +42,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null-y" +id = "t004_null_y" description = "NULL y. Catches: NULL turned into 0 (giving 0 or pi)" columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "NULL x. Catches: NULL turned into 0 (giving pi/2)" columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-zeros" +id = "t006_zeros" description = "Signed zeros on the negative x axis. Catches: the sign of zero lost (pi vs -pi)" columns = [{ name = "y", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -77,7 +77,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t007-bad-text" +id = "t007_bad_text" description = "A string that is not a number is an error. Catches: the string read as 0" columns = [{ name = "y", type = "VARCHAR" }, { name = "x", type = "FLOAT" }] rows = [ @@ -88,7 +88,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t008-boolean" +id = "t008_boolean" description = "A BOOLEAN argument is an error (\"any other data type\"). Catches: TRUE read as 1" columns = [{ name = "y", type = "BOOLEAN" }, { name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/atanh.toml b/cases/translation/functions/atanh.toml index ce1b08186..ace9eaff2 100644 --- a/cases/translation/functions/atanh.toml +++ b/cases/translation/functions/atanh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.atanh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "ATANH of FLOAT values. Catches: TANH computed, or the odd symmetry lost" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "ATANH of NUMBER(12,4), converted to FLOAT. Catches: TANH computed, or the odd symmetry lost; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "ATANH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-near-one" +id = "t004_near_one" description = "Close to the domain edges. Catches: a naive 0.5*LN((1+x)/(1-x)): 0 instead of 1e-17 near zero, or precision lost near the asymptote" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-edge" +id = "t005_edge" description = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -65,7 +65,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t006-outside" +id = "t006_outside" description = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/cbrt.toml b/cases/translation/functions/cbrt.toml index 87891babb..c351efa97 100644 --- a/cases/translation/functions/cbrt.toml +++ b/cases/translation/functions/cbrt.toml @@ -3,7 +3,7 @@ construct = "sf.functions.cbrt" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "CBRT of FLOAT values. Catches: SQRT computed, or POW(x, 1/3) giving NaN for negatives" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -17,7 +17,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "CBRT of NUMBER(12,4), converted to FLOAT. Catches: SQRT computed, or POW(x, 1/3) giving NaN for negatives; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-decfloat" +id = "t003_decfloat" description = "CBRT of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -42,7 +42,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "CBRT of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-neg" +id = "t005_neg" description = "Cube roots of negatives are negative. Catches: NaN from a fractional power of a negative" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/ceil.toml b/cases/translation/functions/ceil.toml index 7d824510d..7b67b68bb 100644 --- a/cases/translation/functions/ceil.toml +++ b/cases/translation/functions/ceil.toml @@ -3,7 +3,7 @@ construct = "sf.functions.ceil" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "CEIL of integers with a negative scale. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "CEIL of NUMBER(12,4) with and without a scale. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "CEIL of FLOAT returns FLOAT. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "CEIL of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.28 at scale 2 rounded up to 0.29" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -58,7 +58,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "CEIL of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -69,7 +69,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-scale" +id = "t006_null_scale" description = "CEIL with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -80,7 +80,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-scale-past" +id = "t007_scale_past" description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -93,7 +93,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-negatives" +id = "t008_negatives" description = "Negative values at the rounding step. Catches: FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/functions/cos.toml b/cases/translation/functions/cos.toml index 1f9370c56..f86fa2f26 100644 --- a/cases/translation/functions/cos.toml +++ b/cases/translation/functions/cos.toml @@ -3,7 +3,7 @@ construct = "sf.functions.cos" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "COS of FLOAT values. Catches: SIN computed, or the argument read in degrees" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -17,7 +17,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "COS of NUMBER(12,4), converted to FLOAT. Catches: SIN computed, or the argument read in degrees; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "COS of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large arguments. Catches: argument reduction differing" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/cosh.toml b/cases/translation/functions/cosh.toml index c2f378661..d15a432ab 100644 --- a/cases/translation/functions/cosh.toml +++ b/cases/translation/functions/cosh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.cosh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "COSH of FLOAT values. Catches: SINH computed (cosh is even, sinh odd)" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "COSH of NUMBER(12,4), converted to FLOAT. Catches: SINH computed (cosh is even, sinh odd); or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "COSH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-overflow-inf" +id = "t004_overflow_inf" description = "Arguments just past the double range (710.48). Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/cot.toml b/cases/translation/functions/cot.toml index d6862a893..6e011193f 100644 --- a/cases/translation/functions/cot.toml +++ b/cases/translation/functions/cot.toml @@ -3,7 +3,7 @@ construct = "sf.functions.cot" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "COT of FLOAT values. Catches: TAN computed instead of its reciprocal" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "COT of NUMBER(12,4), converted to FLOAT. Catches: TAN computed instead of its reciprocal; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "COT of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-small" +id = "t004_small" description = "Very small arguments give large cotangents. Catches: reciprocal computed in fixed-point" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-pole" +id = "t005_pole" description = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/degrees.toml b/cases/translation/functions/degrees.toml index 57fd49e3e..1aba89a0a 100644 --- a/cases/translation/functions/degrees.toml +++ b/cases/translation/functions/degrees.toml @@ -3,7 +3,7 @@ construct = "sf.functions.degrees" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "DEGREES of FLOAT values. Catches: RADIANS computed (the reverse conversion)" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "DEGREES of NUMBER(12,4), converted to FLOAT. Catches: RADIANS computed (the reverse conversion); or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "DEGREES of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large radian values. Catches: x*180 computed before dividing by pi, overflowing where the result is finite" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-overflow" +id = "t005_overflow" description = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: Infinity versus an error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/div0.toml b/cases/translation/functions/div0.toml index e1ffdd915..2bdf9539f 100644 --- a/cases/translation/functions/div0.toml +++ b/cases/translation/functions/div0.toml @@ -3,7 +3,7 @@ construct = "sf.functions.div0" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "DIV0 of integers, as / divides. Catches: integer division (3 instead of 3.500000)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "DIV0 of NUMBER(10,2) values. Catches: quotient scale differing from /" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "DIV0 of FLOAT values. Catches: infinity or NaN for a zero divisor instead of 0" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null-a" +id = "t004_null_a" description = "NULL dividend. Catches: NULL turned into 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-null-b" +id = "t005_null_b" description = "NULL divisor. Catches: a NULL divisor returning 0 (that is DIV0NULL)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-neg-zero" +id = "t006_neg_zero" description = "Negative dividends over zero. Catches: the sign of the dividend kept on the zero result (-0 instead of 0)" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/div0null.toml b/cases/translation/functions/div0null.toml index d7564c0f3..514d949b5 100644 --- a/cases/translation/functions/div0null.toml +++ b/cases/translation/functions/div0null.toml @@ -3,7 +3,7 @@ construct = "sf.functions.div0null" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "DIV0NULL of integers, as / divides. Catches: integer division (3 instead of 3.500000)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "DIV0NULL of NUMBER(10,2) values. Catches: quotient scale differing from /" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "DIV0NULL of FLOAT values. Catches: infinity or NaN for a zero divisor instead of 0" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null-a" +id = "t004_null_a" description = "NULL dividend. Catches: NULL turned into 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-null-b" +id = "t005_null_b" description = "NULL divisor. Catches: a NULL divisor returning NULL (that is DIV0)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-neg-zero" +id = "t006_neg_zero" description = "Negative dividends over zero. Catches: a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/functions/exp.toml b/cases/translation/functions/exp.toml index f7797f53c..d1b1f49fc 100644 --- a/cases/translation/functions/exp.toml +++ b/cases/translation/functions/exp.toml @@ -3,7 +3,7 @@ construct = "sf.functions.exp" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "EXP of FLOAT values. Catches: LN computed, or e^x read as 10^x" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "EXP of NUMBER(12,4), converted to FLOAT. Catches: LN computed, or e^x read as 10^x; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-decfloat" +id = "t003_decfloat" description = "EXP of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "EXP of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-overflow-inf" +id = "t005_overflow_inf" description = "Past the double range. Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/factorial.toml b/cases/translation/functions/factorial.toml index 9498d2c82..0a40cc646 100644 --- a/cases/translation/functions/factorial.toml +++ b/cases/translation/functions/factorial.toml @@ -3,7 +3,7 @@ construct = "sf.functions.factorial" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "FACTORIAL of integers. Catches: gamma(x) computed (fractional), or x! off by one (n-1)!" columns = [{ name = "n", type = "NUMBER(38,0)" }] rows = [ @@ -17,7 +17,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec-int" +id = "t002_dec_int" description = "FACTORIAL of an integral NUMBER(10,2). Catches: scale carried into the result" columns = [{ name = "n", type = "NUMBER(10,2)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "FACTORIAL of NULL. Catches: NULL turned into 1" columns = [{ name = "n", type = "NUMBER(38,0)" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large factorials that still fit 38 digits. Catches: 64-bit overflow (21! does not fit a BIGINT)" columns = [{ name = "n", type = "NUMBER(38,0)" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t005-negative" +id = "t005_negative" description = "A negative argument. Catches: a gamma value or 1 instead of the error" columns = [{ name = "n", type = "NUMBER(38,0)" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t006-too-big" +id = "t006_too_big" description = "A result past 38 digits. Catches: FLOAT or wrapping instead of the error" columns = [{ name = "n", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/functions/floor.toml b/cases/translation/functions/floor.toml index 9f7b811fd..8bf132627 100644 --- a/cases/translation/functions/floor.toml +++ b/cases/translation/functions/floor.toml @@ -3,7 +3,7 @@ construct = "sf.functions.floor" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "FLOOR of integers with a negative scale. Catches: CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "FLOOR of NUMBER(12,4) with and without a scale. Catches: CEIL or truncation used (FLOOR(-2.1) is -3)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "FLOOR of FLOAT returns FLOAT. Catches: CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "FLOOR of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -57,7 +57,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "FLOOR of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -68,7 +68,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-scale" +id = "t006_null_scale" description = "FLOOR with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -79,7 +79,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-scale-past" +id = "t007_scale_past" description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -92,7 +92,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-negatives" +id = "t008_negatives" description = "Negative values at the rounding step. Catches: CEIL or truncation used (FLOOR(-2.1) is -3)" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/functions/ln.toml b/cases/translation/functions/ln.toml index ec00d03c3..7dccd9b05 100644 --- a/cases/translation/functions/ln.toml +++ b/cases/translation/functions/ln.toml @@ -3,7 +3,7 @@ construct = "sf.functions.ln" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "LN of FLOAT values. Catches: LOG10 or LOG2 computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "LN of NUMBER(12,4), converted to FLOAT. Catches: LOG10 or LOG2 computed; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-decfloat" +id = "t003_decfloat" description = "LN of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "LN of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-tiny" +id = "t005_tiny" description = "Very small positive values. Catches: underflow to -infinity" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t006-zero" +id = "t006_zero" description = "LN of 0 or a negative value is an error. Catches: NULL, NaN or -infinity instead of the error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/log.toml b/cases/translation/functions/log.toml index 5e739858c..d1ce76244 100644 --- a/cases/translation/functions/log.toml +++ b/cases/translation/functions/log.toml @@ -3,7 +3,7 @@ construct = "sf.functions.log" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "LOG of integers. Catches: arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" columns = [{ name = "b", type = "NUMBER(38,0)" }, { name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "LOG of fixed-point values. Catches: base rounded to an integer" columns = [{ name = "b", type = "NUMBER(10,2)" }, { name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "LOG of FLOAT values. Catches: arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-mixed" +id = "t004_mixed" description = "LOG with an integer base and a FLOAT value. Catches: type promotion differing" columns = [{ name = "b", type = "NUMBER(38,0)" }, { name = "x", type = "FLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-base" +id = "t005_null_base" description = "NULL base. Catches: NULL turned into e or 10" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-expr" +id = "t006_null_expr" description = "NULL value. Catches: NULL turned into 1" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -74,7 +74,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-base-below-one" +id = "t007_base_below_one" description = "A base between 0 and 1 gives negative logs of values above 1. Catches: the sign lost" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -86,7 +86,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-base-one" +id = "t008_base_one" description = "A base of 1 is an error. Catches: infinity or NULL instead of the error" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -97,7 +97,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t009-base-neg" +id = "t009_base_neg" description = "A negative base is an error. Catches: NaN instead of the error" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ @@ -108,7 +108,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t010-expr-zero" +id = "t010_expr_zero" description = "A value of 0 is an error. Catches: -infinity instead of the error" columns = [{ name = "b", type = "FLOAT" }, { name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/mod.toml b/cases/translation/functions/mod.toml index 50ee8fac8..b65799529 100644 --- a/cases/translation/functions/mod.toml +++ b/cases/translation/functions/mod.toml @@ -3,7 +3,7 @@ construct = "sf.functions.mod" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "MOD of integers with negatives. Catches: floor-based modulo (MOD(-7, 3) is -1, not 2)" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "MOD of NUMBER(10,2) values. Catches: fractional remainder dropped" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-dec-int" +id = "t003_dec_int" description = "MOD of a scaled and an integer operand. Catches: result scale from one side" columns = [{ name = "a", type = "NUMBER(10,2)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-float" +id = "t004_float" description = "MOD of FLOAT values. Catches: floor-based float modulo" columns = [{ name = "a", type = "FLOAT" }, { name = "b", type = "FLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-a" +id = "t005_null_a" description = "NULL dividend. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-b" +id = "t006_null_b" description = "NULL divisor. Catches: NULL treated as 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -74,7 +74,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-order" +id = "t007_order" description = "Argument order with negatives. Catches: arguments swapped" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -86,7 +86,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-max" +id = "t008_max" description = "Remainders of 38-digit integers. Catches: 64-bit modulo" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ @@ -98,7 +98,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t009-zero-divisor" +id = "t009_zero_divisor" description = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error. Catches: a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/functions/pi.toml b/cases/translation/functions/pi.toml index 6f7489558..db408cf0c 100644 --- a/cases/translation/functions/pi.toml +++ b/cases/translation/functions/pi.toml @@ -3,7 +3,7 @@ construct = "sf.functions.pi" session_profile = "utc-v1" [[case]] -id = "t001-value" +id = "t001_value" description = "PI() in arithmetic, at full double precision. Catches: a rounded constant (3.14159) or a NUMBER" columns = [{ name = "k", type = "NUMBER(38,0)" }] rows = [ diff --git a/cases/translation/functions/pow.pow.toml b/cases/translation/functions/pow.pow.toml index 9f46114a3..d0144cfbe 100644 --- a/cases/translation/functions/pow.pow.toml +++ b/cases/translation/functions/pow.pow.toml @@ -3,7 +3,7 @@ construct = "sf.functions.pow.pow" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "POW of integers returns FLOAT. Catches: arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "POW with fractional exponents. Catches: the exponent rounded" columns = [{ name = "x", type = "NUMBER(10,2)" }, { name = "y", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "POW of FLOAT values. Catches: arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "POW of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" columns = [{ name = "x", type = "DECFLOAT" }, { name = "y", type = "DECFLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "NULL base. Catches: NULL^0 returning 1" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-y" +id = "t006_null_y" description = "NULL exponent. Catches: 1^NULL returning 1" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -76,7 +76,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-overflow-inf" +id = "t007_overflow_inf" description = "Powers past the double range, and 0^0. Catches: infinity versus error; 0^0 as 0" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -89,7 +89,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-neg-base" +id = "t008_neg_base" description = "Negative bases with integer exponents. Catches: the sign lost for odd exponents" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -102,7 +102,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t009-neg-frac" +id = "t009_neg_frac" description = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/pow.power.toml b/cases/translation/functions/pow.power.toml index 6f58d1127..13075d112 100644 --- a/cases/translation/functions/pow.power.toml +++ b/cases/translation/functions/pow.power.toml @@ -3,7 +3,7 @@ construct = "sf.functions.pow.power" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "POWER of integers returns FLOAT. Catches: arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "POWER with fractional exponents. Catches: the exponent rounded" columns = [{ name = "x", type = "NUMBER(10,2)" }, { name = "y", type = "NUMBER(10,2)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "POWER of FLOAT values. Catches: arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "POWER of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" columns = [{ name = "x", type = "DECFLOAT" }, { name = "y", type = "DECFLOAT" }] rows = [ @@ -52,7 +52,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "NULL base. Catches: NULL^0 returning 1" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -64,7 +64,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-y" +id = "t006_null_y" description = "NULL exponent. Catches: 1^NULL returning 1" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -76,7 +76,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-overflow-inf" +id = "t007_overflow_inf" description = "Powers past the double range, and 0^0. Catches: infinity versus error; 0^0 as 0" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -89,7 +89,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-neg-base" +id = "t008_neg_base" description = "Negative bases with integer exponents. Catches: the sign lost for odd exponents" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ @@ -102,7 +102,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t009-neg-frac" +id = "t009_neg_frac" description = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }, { name = "y", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/radians.toml b/cases/translation/functions/radians.toml index ff8ba8780..c138530b7 100644 --- a/cases/translation/functions/radians.toml +++ b/cases/translation/functions/radians.toml @@ -3,7 +3,7 @@ construct = "sf.functions.radians" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "RADIANS of FLOAT values. Catches: DEGREES computed (the reverse conversion)" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "RADIANS of NUMBER(12,4), converted to FLOAT. Catches: DEGREES computed (the reverse conversion); or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "RADIANS of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large degree values. Catches: x*pi computed before dividing by 180, overflowing where the result is finite" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/round.round-expr-scale.toml b/cases/translation/functions/round.round-expr-scale.toml index 39e1f7db2..6b5c5cbae 100644 --- a/cases/translation/functions/round.round-expr-scale.toml +++ b/cases/translation/functions/round.round-expr-scale.toml @@ -3,7 +3,7 @@ construct = "sf.functions.round.round-expr-scale" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "ROUND of integers with a negative scale. Catches: half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "ROUND of NUMBER(12,4) halves. Catches: banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "ROUND of FLOAT halves exact in binary. Catches: half-to-even for FLOAT, or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "ROUND of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -55,7 +55,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-mode-even" +id = "t005_mode_even" description = "rounding_mode 'HALF_TO_EVEN' on fixed-point. Catches: the mode ignored (2.5 to 3 instead of 2)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -69,7 +69,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-mode-away" +id = "t006_mode_away" description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -81,7 +81,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-null-x" +id = "t007_null_x" description = "ROUND of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -92,7 +92,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-scale" +id = "t008_null_scale" description = "ROUND with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -103,7 +103,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-scale" +id = "t009_scale" description = "Scales past the input scale and a carry across digits. Catches: carry lost (9.995 to 9.99) or scale capped" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -116,7 +116,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-max" +id = "t010_max" description = "Rounding 38-digit integers at scale -1, near the 38-digit limit. Catches: overflow to FLOAT" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -128,7 +128,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-mode-float" +id = "t011_mode_float" description = "A rounding_mode with a FLOAT input is an error. Catches: the mode applied to FLOAT instead of the documented error" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -139,7 +139,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t012-mode-bad" +id = "t012_mode_bad" description = "An unknown rounding_mode is an error. Catches: the mode ignored" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -150,7 +150,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t013-expr-no-scale" +id = "t013_expr_no_scale" description = "EXPR => without SCALE => is an error, as the page says. Catches: SCALE defaulted to 0" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ diff --git a/cases/translation/functions/round.round.toml b/cases/translation/functions/round.round.toml index e1e518c0f..78a38ad68 100644 --- a/cases/translation/functions/round.round.toml +++ b/cases/translation/functions/round.round.toml @@ -3,7 +3,7 @@ construct = "sf.functions.round.round" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "ROUND of integers with a negative scale. Catches: half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "ROUND of NUMBER(12,4) halves. Catches: banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "ROUND of FLOAT halves exact in binary. Catches: half-to-even for FLOAT, or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "ROUND of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -55,7 +55,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-mode-even" +id = "t005_mode_even" description = "rounding_mode 'HALF_TO_EVEN' on fixed-point. Catches: the mode ignored (2.5 to 3 instead of 2)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -69,7 +69,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t006-mode-away" +id = "t006_mode_away" description = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out. Catches: the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -81,7 +81,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t007-null-x" +id = "t007_null_x" description = "ROUND of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -92,7 +92,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-scale" +id = "t008_null_scale" description = "ROUND with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -103,7 +103,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-scale" +id = "t009_scale" description = "Scales past the input scale and a carry across digits. Catches: carry lost (9.995 to 9.99) or scale capped" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -116,7 +116,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-max" +id = "t010_max" description = "Rounding 38-digit integers at scale -1, near the 38-digit limit. Catches: overflow to FLOAT" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -128,7 +128,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t011-mode-float" +id = "t011_mode_float" description = "A rounding_mode with a FLOAT input is an error. Catches: the mode applied to FLOAT instead of the documented error" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -139,7 +139,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t012-mode-bad" +id = "t012_mode_bad" description = "An unknown rounding_mode is an error. Catches: the mode ignored" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ diff --git a/cases/translation/functions/sign.toml b/cases/translation/functions/sign.toml index dc3aff84c..cef9cabb9 100644 --- a/cases/translation/functions/sign.toml +++ b/cases/translation/functions/sign.toml @@ -3,7 +3,7 @@ construct = "sf.functions.sign" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "SIGN of integers. Catches: ABS computed (-7 gives 7 instead of -1)" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "SIGN of NUMBER(10,2), keeping its type. Catches: the scale lost or FLOAT returned" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "SIGN of FLOAT. Catches: negative zero and tiny negatives" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -42,7 +42,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "SIGN of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -53,7 +53,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-extremes" +id = "t005_extremes" description = "The 38-digit and double extremes. Catches: 64-bit arithmetic overflowing" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "y", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/sin.toml b/cases/translation/functions/sin.toml index f3233815e..1251e3bf6 100644 --- a/cases/translation/functions/sin.toml +++ b/cases/translation/functions/sin.toml @@ -3,7 +3,7 @@ construct = "sf.functions.sin" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "SIN of FLOAT values. Catches: COS computed, or the argument read in degrees" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -17,7 +17,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "SIN of NUMBER(12,4), converted to FLOAT. Catches: COS computed, or the argument read in degrees; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -29,7 +29,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "SIN of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-large" +id = "t004_large" description = "Large arguments. Catches: argument reduction differing" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/sinh.toml b/cases/translation/functions/sinh.toml index dde4176a1..911a5f828 100644 --- a/cases/translation/functions/sinh.toml +++ b/cases/translation/functions/sinh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.sinh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "SINH of FLOAT values. Catches: COSH computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "SINH of NUMBER(12,4), converted to FLOAT. Catches: COSH computed; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "SINH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-overflow-inf" +id = "t004_overflow_inf" description = "Arguments just past the double range (710.48). Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/sqrt.toml b/cases/translation/functions/sqrt.toml index 8a4505485..b4e383db9 100644 --- a/cases/translation/functions/sqrt.toml +++ b/cases/translation/functions/sqrt.toml @@ -3,7 +3,7 @@ construct = "sf.functions.sqrt" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "SQRT of FLOAT values. Catches: CBRT or squaring computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "SQRT of NUMBER(12,4), converted to FLOAT. Catches: CBRT or squaring computed; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-decfloat" +id = "t003_decfloat" description = "SQRT of DECFLOAT, which returns DECFLOAT. Catches: a DECFLOAT argument computed and returned as FLOAT" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -40,7 +40,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-null" +id = "t004_null" description = "SQRT of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t005-tiny-huge" +id = "t005_tiny_huge" description = "Very small and very large values. Catches: precision at the extremes" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -63,7 +63,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t006-negative" +id = "t006_negative" description = "SQRT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error. Catches: NaN, Infinity, NULL or an error differing from Snowflake" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/square.toml b/cases/translation/functions/square.toml index ff918ffe7..44b0040a7 100644 --- a/cases/translation/functions/square.toml +++ b/cases/translation/functions/square.toml @@ -3,7 +3,7 @@ construct = "sf.functions.square" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "SQUARE of integers returns FLOAT. Catches: x*x kept as NUMBER (the type differs), or SQRT computed" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -15,7 +15,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "SQUARE of NUMBER(10,2). Catches: the result left fixed-point" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -27,7 +27,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "SQUARE of FLOAT. Catches: SQRT computed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "SQUARE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -51,7 +51,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null" +id = "t005_null" description = "SQUARE of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -62,7 +62,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-overflow-inf" +id = "t006_overflow_inf" description = "Squares past the double range. Catches: infinity versus error" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/tan.toml b/cases/translation/functions/tan.toml index 461b1e067..d87ccec08 100644 --- a/cases/translation/functions/tan.toml +++ b/cases/translation/functions/tan.toml @@ -3,7 +3,7 @@ construct = "sf.functions.tan" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "TAN of FLOAT values. Catches: SIN or COT computed, or degrees" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "TAN of NUMBER(12,4), converted to FLOAT. Catches: SIN or COT computed, or degrees; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "TAN of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-near-pole" +id = "t004_near_pole" description = "Near pi/2. Catches: pole handling" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/tanh.toml b/cases/translation/functions/tanh.toml index 1e587bd40..342ec5a8c 100644 --- a/cases/translation/functions/tanh.toml +++ b/cases/translation/functions/tanh.toml @@ -3,7 +3,7 @@ construct = "sf.functions.tanh" session_profile = "utc-v1" [[case]] -id = "t001-float" +id = "t001_float" description = "TANH of FLOAT values. Catches: SINH or a clamped value" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-fixed" +id = "t002_fixed" description = "TANH of NUMBER(12,4), converted to FLOAT. Catches: SINH or a clamped value; or the result left fixed-point" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -28,7 +28,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-null" +id = "t003_null" description = "TANH of NULL. Catches: NULL turned into 0 or NaN" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -39,7 +39,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t004-saturate" +id = "t004_saturate" description = "Large arguments saturate to 1 and -1. Catches: NaN from exp overflow" columns = [{ name = "x", type = "FLOAT" }] rows = [ diff --git a/cases/translation/functions/trunc.trunc.toml b/cases/translation/functions/trunc.trunc.toml index 98658af56..3b1cc889f 100644 --- a/cases/translation/functions/trunc.trunc.toml +++ b/cases/translation/functions/trunc.trunc.toml @@ -3,7 +3,7 @@ construct = "sf.functions.trunc.trunc" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "TRUNC of integers with a negative scale. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "TRUNC of NUMBER(12,4) with and without a scale. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "TRUNC of FLOAT returns FLOAT. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "TRUNC of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -57,7 +57,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "TRUNC of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -68,7 +68,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-scale" +id = "t006_null_scale" description = "TRUNC with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -79,7 +79,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-scale-past" +id = "t007_scale_past" description = "Scales past the input scale and far negative. Catches: scale capped or wrapping" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -92,7 +92,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-negatives" +id = "t008_negatives" description = "Negative values at the rounding step. Catches: FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/functions/trunc.truncate.toml b/cases/translation/functions/trunc.truncate.toml index 5a4fe0ab3..7b250bd9e 100644 --- a/cases/translation/functions/trunc.truncate.toml +++ b/cases/translation/functions/trunc.truncate.toml @@ -3,7 +3,7 @@ construct = "sf.functions.trunc.truncate" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "TRUNCATE of integers with a negative scale. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a negative scale ignored" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -16,7 +16,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "TRUNCATE of NUMBER(12,4) with and without a scale. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2)" columns = [{ name = "x", type = "NUMBER(12,4)" }] rows = [ @@ -30,7 +30,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "TRUNCATE of FLOAT returns FLOAT. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a NUMBER returned" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -43,7 +43,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-decfloat" +id = "t004_decfloat" description = "TRUNCATE of DECFLOAT returns DECFLOAT. Catches: computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" columns = [{ name = "x", type = "DECFLOAT" }] rows = [ @@ -57,7 +57,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-x" +id = "t005_null_x" description = "TRUNCATE of NULL. Catches: NULL turned into 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -68,7 +68,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-scale" +id = "t006_null_scale" description = "TRUNCATE with a NULL scale. Catches: the NULL scale read as 0" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -79,7 +79,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-scale-past" +id = "t007_scale_past" description = "Scales past the input scale and far negative. Catches: a negative scale beyond the integer digits not giving 0, or a scale above the input scale changing the value" columns = [{ name = "x", type = "NUMBER(12,4)" }, { name = "s", type = "NUMBER(38,0)" }] rows = [ @@ -92,7 +92,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t008-negatives" +id = "t008_negatives" description = "Negative values at the rounding step. Catches: FLOOR used for negatives (TRUNCATE(-2.7) is -2)" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ diff --git a/cases/translation/functions/width-bucket.toml b/cases/translation/functions/width-bucket.toml index 205a8d8d2..2a75b6f62 100644 --- a/cases/translation/functions/width-bucket.toml +++ b/cases/translation/functions/width-bucket.toml @@ -3,7 +3,7 @@ construct = "sf.functions.width-bucket" session_profile = "utc-v1" [[case]] -id = "t001-int" +id = "t001_int" description = "WIDTH_BUCKET of integers, including below and above the range. Catches: buckets numbered from 0, or the overflow buckets 0 and n+1 missing" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -20,7 +20,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t002-dec" +id = "t002_dec" description = "WIDTH_BUCKET of fixed-point values on bucket edges. Catches: edges assigned to the lower bucket" columns = [{ name = "x", type = "NUMBER(10,2)" }] rows = [ @@ -34,7 +34,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t003-float" +id = "t003_float" description = "WIDTH_BUCKET of FLOAT values with FLOAT end points. Catches: integer end points assumed" columns = [{ name = "x", type = "FLOAT" }] rows = [ @@ -47,7 +47,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t004-reversed" +id = "t004_reversed" description = "End points given high to low. Catches: the end points swapped back, numbering buckets upwards" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -61,7 +61,7 @@ order = "ordered" tags = ["type-combination"] [[case]] -id = "t005-null-expr" +id = "t005_null_expr" description = "NULL value. Catches: NULL put in bucket 0" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -72,7 +72,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t006-null-min" +id = "t006_null_min" description = "NULL low end point. Catches: NULL read as 0" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "m", type = "NUMBER(38,0)" }] rows = [ @@ -83,7 +83,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t007-null-max" +id = "t007_null_max" description = "NULL high end point. Catches: NULL read as 0" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "m", type = "NUMBER(38,0)" }] rows = [ @@ -94,7 +94,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t008-null-n" +id = "t008_null_n" description = "NULL bucket count. Catches: NULL read as 1" columns = [{ name = "x", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] rows = [ @@ -105,7 +105,7 @@ order = "ordered" tags = ["null"] [[case]] -id = "t009-range-edge" +id = "t009_range_edge" description = "Values at the documented 2^53 - 1 limit. Catches: precision loss rounding the limit into another bucket" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -119,7 +119,7 @@ order = "ordered" tags = ["boundary"] [[case]] -id = "t010-zero-buckets" +id = "t010_zero_buckets" description = "num_buckets 0 is an error. Catches: bucket 0 returned" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ @@ -130,7 +130,7 @@ order = "ordered" tags = ["error"] [[case]] -id = "t011-equal-ends" +id = "t011_equal_ends" description = "Equal end points are an error. Catches: division by zero returning NULL or infinity" columns = [{ name = "x", type = "NUMBER(38,0)" }] rows = [ diff --git a/coverage/translation/obligations/expressions.toml b/coverage/translation/obligations/expressions.toml index b77bf88b1..60a13dc4a 100644 --- a/coverage/translation/obligations/expressions.toml +++ b/coverage/translation/obligations/expressions.toml @@ -10,7 +10,7 @@ target = "NUMBER(38,0) + NUMBER(38,0)" note = "a NUMBER(38,0) + b NUMBER(38,0)." catches = "operands swapped for subtraction or truncated to 32 bits" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t001-num-num-380380"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t001_num_num_380380"] required = true [[obligation]] @@ -21,7 +21,7 @@ target = "NUMBER(10,2) + NUMBER(38,0)" note = "a NUMBER(10,2) + b NUMBER(38,0)." catches = "the sum losing the NUMBER(10,2) scale (4 instead of 4.25)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t002-num-num-102380"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t002_num_num_102380"] required = true [[obligation]] @@ -32,7 +32,7 @@ target = "NUMBER(10,2) + NUMBER(12,4)" note = "a NUMBER(10,2) + b NUMBER(12,4)." catches = "the result scale taken from the left operand only (1.31 instead of 1.3125)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t003-num-num-102124"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t003_num_num_102124"] required = true [[obligation]] @@ -43,7 +43,7 @@ target = "NUMBER(38,0) + FLOAT" note = "a NUMBER(38,0) + b FLOAT." catches = "a FLOAT operand rounded to the integer side" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t004-num-float-380"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t004_num_float_380"] required = true [[obligation]] @@ -54,7 +54,7 @@ target = "NUMBER(10,2) + FLOAT" note = "a NUMBER(10,2) + b FLOAT." catches = "the FLOAT operand cast to NUMBER(10,2) (1.00 instead of 1.001) or a fixed-point result type" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t005-num-float-102"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t005_num_float_102"] required = true [[obligation]] @@ -65,7 +65,7 @@ target = "FLOAT + FLOAT" note = "a FLOAT + b FLOAT." catches = "float addition replaced by decimal addition (0.30000000000000004 vs 0.3)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t006-float-float-"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t006_float_float"] required = true [[obligation]] @@ -76,7 +76,7 @@ target = "a" note = "NULL as a, the other operand set." catches = "NULL treated as 0 instead of propagating" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t007-null-a"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t007_null_a"] required = true [[obligation]] @@ -87,7 +87,7 @@ target = "b" note = "NULL as b, the other operand set." catches = "NULL treated as 0 instead of propagating" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t008-null-b"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t008_null_b"] required = true [[obligation]] @@ -99,7 +99,7 @@ target = "magnitude" note = "38-digit integers that still fit." catches = "arithmetic done in 64 bits" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t009-max-38"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t009_max_38"] required = true [[obligation]] @@ -111,7 +111,7 @@ target = "negative" note = "Negative fixed-point sums crossing zero." catches = "sign lost or magnitude added" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t010-negatives"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t010_negatives"] required = true [[obligation]] @@ -122,7 +122,7 @@ target = "error" note = "The sum of two 38-digit integers overflows NUMBER(38,0)." catches = "wrapping or a silent switch to FLOAT instead of the out-of-range error" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t011-overflow"] +cases = ["cases/translation/expressions/operators-arithmetic.add.toml#t011_overflow"] required = true [[obligation]] @@ -133,7 +133,7 @@ target = "NUMBER(38,0) / NUMBER(38,0)" note = "a NUMBER(38,0) / b NUMBER(38,0)." catches = "integer division (3 instead of 3.5) or a truncated rather than rounded scale-6 quotient (0.666666 vs 0.666667)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t001-num-num-380380"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t001_num_num_380380"] required = true [[obligation]] @@ -144,7 +144,7 @@ target = "NUMBER(10,2) / NUMBER(38,0)" note = "a NUMBER(10,2) / b NUMBER(38,0)." catches = "the quotient scale (s+6) handled wrongly" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t002-num-num-102380"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t002_num_num_102380"] required = true [[obligation]] @@ -155,7 +155,7 @@ target = "NUMBER(10,2) / NUMBER(12,4)" note = "a NUMBER(10,2) / b NUMBER(12,4)." catches = "quotient scale taken from the divisor" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t003-num-num-102124"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t003_num_num_102124"] required = true [[obligation]] @@ -166,7 +166,7 @@ target = "NUMBER(38,0) / FLOAT" note = "a NUMBER(38,0) / b FLOAT." catches = "FLOAT divisor rounded" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t004-num-float-380"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t004_num_float_380"] required = true [[obligation]] @@ -177,7 +177,7 @@ target = "NUMBER(10,2) / FLOAT" note = "a NUMBER(10,2) / b FLOAT." catches = "fixed-point / FLOAT result type swapped" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t005-num-float-102"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t005_num_float_102"] required = true [[obligation]] @@ -188,7 +188,7 @@ target = "FLOAT / FLOAT" note = "a FLOAT / b FLOAT." catches = "float division done in decimal" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t006-float-float-"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t006_float_float"] required = true [[obligation]] @@ -199,7 +199,7 @@ target = "a" note = "NULL as a, the other operand set." catches = "NULL treated as 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t007-null-a"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t007_null_a"] required = true [[obligation]] @@ -210,7 +210,7 @@ target = "b" note = "NULL as b, the other operand set." catches = "NULL divisor turned into the division-by-zero error or 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t008-null-b"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t008_null_b"] required = true [[obligation]] @@ -222,7 +222,7 @@ target = "rounding_scale" note = "Scales near the 12-digit quotient cap." catches = "the quotient scale not capped at 12" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t009-scale-12"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t009_scale_12"] required = true [[obligation]] @@ -234,7 +234,7 @@ target = "negative" note = "Negative quotients that round at the last digit." catches = "rounding toward zero instead of half away from zero" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t010-neg-quotient"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t010_neg_quotient"] required = true [[obligation]] @@ -245,7 +245,7 @@ target = "error" note = "Division by zero is an error." catches = "NULL, 0 or infinity instead of the division-by-zero error" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t011-div-zero"] +cases = ["cases/translation/expressions/operators-arithmetic.divide.toml#t011_div_zero"] required = true [[obligation]] @@ -256,7 +256,7 @@ target = "NUMBER(38,0) % NUMBER(38,0)" note = "a NUMBER(38,0) % b NUMBER(38,0)." catches = "floor-based modulo (2 instead of -1): MOD truncates toward zero" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t001-num-num-380380"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t001_num_num_380380"] required = true [[obligation]] @@ -267,7 +267,7 @@ target = "NUMBER(10,2) % NUMBER(38,0)" note = "a NUMBER(10,2) % b NUMBER(38,0)." catches = "fractional remainder dropped" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t002-num-num-102380"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t002_num_num_102380"] required = true [[obligation]] @@ -278,7 +278,7 @@ target = "NUMBER(10,2) % NUMBER(12,4)" note = "a NUMBER(10,2) % b NUMBER(12,4)." catches = "result scale taken from one operand" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t003-num-num-102124"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t003_num_num_102124"] required = true [[obligation]] @@ -289,7 +289,7 @@ target = "NUMBER(38,0) % FLOAT" note = "a NUMBER(38,0) % b FLOAT." catches = "FLOAT divisor rounded" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t004-num-float-380"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t004_num_float_380"] required = true [[obligation]] @@ -300,7 +300,7 @@ target = "FLOAT % FLOAT" note = "a FLOAT % b FLOAT." catches = "floor-based float modulo" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t005-float-float-"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t005_float_float"] required = true [[obligation]] @@ -311,7 +311,7 @@ target = "a" note = "NULL as a, the other operand set." catches = "NULL treated as 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t006-null-a"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t006_null_a"] required = true [[obligation]] @@ -322,7 +322,7 @@ target = "b" note = "NULL as b, the other operand set." catches = "NULL treated as 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t007-null-b"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t007_null_b"] required = true [[obligation]] @@ -334,7 +334,7 @@ target = "negative" note = "Both operands negative." catches = "remainder made non-negative (Euclidean or ABS): 2 instead of -1 for -7 % -3, 4 instead of -1 for -1 % -5" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t008-both-negative"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t008_both_negative"] required = true [[obligation]] @@ -345,7 +345,7 @@ target = "error" note = "A non-numeric string operand." catches = "the string silently read as 0 instead of the conversion error" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t009-mod-text"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t009_mod_text"] required = true [[obligation]] @@ -356,7 +356,7 @@ target = "error" note = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error." catches = "NULL for x % 0 (DuckDB) where Snowflake raises division by zero" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t010-zero-divisor"] +cases = ["cases/translation/expressions/operators-arithmetic.modulo.toml#t010_zero_divisor"] required = true [[obligation]] @@ -367,7 +367,7 @@ target = "NUMBER(38,0) * NUMBER(38,0)" note = "a NUMBER(38,0) * b NUMBER(38,0)." catches = "32-bit multiplication overflowing" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t001-num-num-380380"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t001_num_num_380380"] required = true [[obligation]] @@ -378,7 +378,7 @@ target = "NUMBER(10,2) * NUMBER(38,0)" note = "a NUMBER(10,2) * b NUMBER(38,0)." catches = "the NUMBER(10,2) scale lost" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t002-num-num-102380"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t002_num_num_102380"] required = true [[obligation]] @@ -389,7 +389,7 @@ target = "NUMBER(10,2) * NUMBER(12,4)" note = "a NUMBER(10,2) * b NUMBER(12,4)." catches = "the product scale capped or taken from one side (1.5625 vs 1.56)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t003-num-num-102124"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t003_num_num_102124"] required = true [[obligation]] @@ -400,7 +400,7 @@ target = "NUMBER(38,0) * FLOAT" note = "a NUMBER(38,0) * b FLOAT." catches = "FLOAT operand truncated" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t004-num-float-380"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t004_num_float_380"] required = true [[obligation]] @@ -411,7 +411,7 @@ target = "NUMBER(10,2) * FLOAT" note = "a NUMBER(10,2) * b FLOAT." catches = "fixed-point / FLOAT result type swapped" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t005-num-float-102"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t005_num_float_102"] required = true [[obligation]] @@ -422,7 +422,7 @@ target = "FLOAT * FLOAT" note = "a FLOAT * b FLOAT." catches = "underflow to zero handled differently" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t006-float-float-"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t006_float_float"] required = true [[obligation]] @@ -433,7 +433,7 @@ target = "a" note = "NULL as a, the other operand set." catches = "NULL treated as 1 or 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t007-null-a"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t007_null_a"] required = true [[obligation]] @@ -444,7 +444,7 @@ target = "b" note = "NULL as b, the other operand set." catches = "NULL treated as 1 or 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t008-null-b"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t008_null_b"] required = true [[obligation]] @@ -456,7 +456,7 @@ target = "magnitude" note = "FLOAT products near the double range." catches = "overflow to infinity treated differently" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t009-float-big"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t009_float_big"] required = true [[obligation]] @@ -468,7 +468,7 @@ target = "rounding_scale" note = "Scales adding past 12 digits." catches = "product scale above Snowflake's cap kept or rounded wrongly" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t010-scale-sum"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t010_scale_sum"] required = true [[obligation]] @@ -479,7 +479,7 @@ target = "error" note = "A product beyond 38 digits." catches = "wrapping or silent FLOAT instead of the out-of-range error" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t011-overflow"] +cases = ["cases/translation/expressions/operators-arithmetic.multiply.toml#t011_overflow"] required = true [[obligation]] @@ -490,7 +490,7 @@ target = "NUMBER(38,0) - NUMBER(38,0)" note = "a NUMBER(38,0) - b NUMBER(38,0)." catches = "operands in the wrong order (7 instead of -7)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t001-num-num-380380"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t001_num_num_380380"] required = true [[obligation]] @@ -501,7 +501,7 @@ target = "NUMBER(10,2) - NUMBER(38,0)" note = "a NUMBER(10,2) - b NUMBER(38,0)." catches = "scale of the NUMBER(10,2) side dropped" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t002-num-num-102380"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t002_num_num_102380"] required = true [[obligation]] @@ -512,7 +512,7 @@ target = "NUMBER(10,2) - NUMBER(12,4)" note = "a NUMBER(10,2) - b NUMBER(12,4)." catches = "the result scale taken from one operand only" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t003-num-num-102124"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t003_num_num_102124"] required = true [[obligation]] @@ -523,7 +523,7 @@ target = "NUMBER(38,0) - FLOAT" note = "a NUMBER(38,0) - b FLOAT." catches = "FLOAT rounded to integer" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t004-num-float-380"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t004_num_float_380"] required = true [[obligation]] @@ -534,7 +534,7 @@ target = "NUMBER(10,2) - FLOAT" note = "a NUMBER(10,2) - b FLOAT." catches = "fixed-point / FLOAT result type swapped" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t005-num-float-102"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t005_num_float_102"] required = true [[obligation]] @@ -545,7 +545,7 @@ target = "FLOAT - FLOAT" note = "a FLOAT - b FLOAT." catches = "float subtraction done in decimal" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t006-float-float-"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t006_float_float"] required = true [[obligation]] @@ -556,7 +556,7 @@ target = "DATE - DATE" note = "DATE minus DATE: the page says the result is an integer number of days." catches = "an INTERVAL instead of an integer count of days, or days counted across the leap day wrongly (29, -730)" reading = "operators-arithmetic: \"a - b Subtracts one date expression ( b ) from another ( a ). The result is an integer number of days.\"" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t007-date-date"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t007_date_date"] required = true [[obligation]] @@ -567,7 +567,7 @@ target = "a" note = "NULL as a, the other operand set." catches = "NULL treated as 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t008-null-a"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t008_null_a"] required = true [[obligation]] @@ -578,7 +578,7 @@ target = "b" note = "NULL as b, the other operand set." catches = "NULL treated as 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t009-null-b"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t009_null_b"] required = true [[obligation]] @@ -590,7 +590,7 @@ target = "negative" note = "A smaller minus a larger value." catches = "absolute value taken" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t010-to-negative"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t010_to_negative"] required = true [[obligation]] @@ -601,7 +601,7 @@ target = "error" note = "The difference of the most negative and the largest 38-digit integers overflows." catches = "wrapping instead of the out-of-range error" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t011-overflow"] +cases = ["cases/translation/expressions/operators-arithmetic.subtract.toml#t011_overflow"] required = true [[obligation]] @@ -612,7 +612,7 @@ target = "NUMBER(38,0)" note = "Negating integers." catches = "the sign dropped or the value doubled" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t001-int"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t001_int"] required = true [[obligation]] @@ -623,7 +623,7 @@ target = "NUMBER(10,2)" note = "Negating NUMBER(10,2)." catches = "the scale lost (-1 instead of -1.25)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t002-dec"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t002_dec"] required = true [[obligation]] @@ -634,7 +634,7 @@ target = "FLOAT" note = "Negating FLOAT." catches = "negative zero or exponent handling" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t003-float"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t003_float"] required = true [[obligation]] @@ -645,7 +645,7 @@ target = "a" note = "Negating NULL." catches = "NULL turned into 0" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t004-null"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t004_null"] required = true [[obligation]] @@ -657,7 +657,7 @@ target = "magnitude" note = "Negating the largest 38-digit integer." catches = "a 64-bit negation that overflows" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t005-max"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t005_max"] required = true [[obligation]] @@ -669,7 +669,7 @@ target = "negative" note = "Negation of a negation and of a subtraction." catches = "precedence: - a - b read as -(a - b)" reading = "operators-arithmetic: a and b are numeric expressions; the page's scale and precision section covers fixed-point and floating-point operands, so the pairs are integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t006-double-neg"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-minus.toml#t006_double_neg"] required = true [[obligation]] @@ -680,7 +680,7 @@ target = "VARCHAR" note = "Unary plus on numeric strings converts them." catches = "the string returned unchanged, so ordering and arithmetic stay textual" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t001-text"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t001_text"] required = true [[obligation]] @@ -691,7 +691,7 @@ target = "NUMBER(10,2)" note = "Unary plus keeps a NUMBER(10,2) as it is." catches = "a cast that changes the scale" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t002-dec"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t002_dec"] required = true [[obligation]] @@ -702,7 +702,7 @@ target = "FLOAT" note = "Unary plus on FLOAT." catches = "conversion to fixed-point" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t003-float"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t003_float"] required = true [[obligation]] @@ -713,7 +713,7 @@ target = "a" note = "Unary plus on NULL text." catches = "NULL replaced or converted to 0" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t004-null"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t004_null"] required = true [[obligation]] @@ -725,7 +725,7 @@ target = "empty_string" note = "Unary plus on an empty string." catches = "the empty string read as 0 when Snowflake errors, or the reverse" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t005-empty"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t005_empty"] required = true [[obligation]] @@ -736,5 +736,5 @@ target = "error" note = "Unary plus on a non-numeric string is an error." catches = "the string passed through unconverted" reading = "operators-arithmetic: \"+a Returns a, which causes implicit conversion of a to a numeric value. If a is a string, but the string can't be converted to a numeric value, an error is returned.\"" -cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t006-bad-text"] +cases = ["cases/translation/expressions/operators-arithmetic.unary-plus.toml#t006_bad_text"] required = true diff --git a/coverage/translation/obligations/functions.toml b/coverage/translation/obligations/functions.toml index f8ab49764..b08731d31 100644 --- a/coverage/translation/obligations/functions.toml +++ b/coverage/translation/obligations/functions.toml @@ -10,7 +10,7 @@ target = "NUMBER(38,0)" note = "ABS of integers." catches = "the sign kept (ABS as identity) or SIGN computed" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/abs.toml#t001-int"] +cases = ["cases/translation/functions/abs.toml#t001_int"] required = true [[obligation]] @@ -21,7 +21,7 @@ target = "NUMBER(10,2)" note = "ABS of NUMBER(10,2), keeping its type." catches = "the scale lost or FLOAT returned" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/abs.toml#t002-dec"] +cases = ["cases/translation/functions/abs.toml#t002_dec"] required = true [[obligation]] @@ -32,7 +32,7 @@ target = "FLOAT" note = "ABS of FLOAT." catches = "negative zero and tiny negatives" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/abs.toml#t003-float"] +cases = ["cases/translation/functions/abs.toml#t003_float"] required = true [[obligation]] @@ -43,7 +43,7 @@ target = "x" note = "ABS of NULL." catches = "NULL turned into 0" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/abs.toml#t004-null"] +cases = ["cases/translation/functions/abs.toml#t004_null"] required = true [[obligation]] @@ -55,7 +55,7 @@ target = "magnitude" note = "The 38-digit and double extremes." catches = "64-bit arithmetic overflowing" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/abs.toml#t005-extremes"] +cases = ["cases/translation/functions/abs.toml#t005_extremes"] required = true [[obligation]] @@ -66,7 +66,7 @@ target = "FLOAT" note = "ACOS of FLOAT values." catches = "ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acos.toml#t001-float"] +cases = ["cases/translation/functions/acos.toml#t001_float"] required = true [[obligation]] @@ -77,7 +77,7 @@ target = "NUMBER(12,4)" note = "ACOS of NUMBER(12,4), converted to FLOAT." catches = "ASIN computed instead (acos(0.25) differs from asin(0.25)) or degrees returned; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acos.toml#t002-fixed"] +cases = ["cases/translation/functions/acos.toml#t002_fixed"] required = true [[obligation]] @@ -88,7 +88,7 @@ target = "x" note = "ACOS of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acos.toml#t003-null"] +cases = ["cases/translation/functions/acos.toml#t003_null"] required = true [[obligation]] @@ -100,7 +100,7 @@ target = "magnitude" note = "The domain edges, 1 and -1." catches = "pi returned for 1 or 0 for -1 (sign of the edge)" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acos.toml#t004-edges"] +cases = ["cases/translation/functions/acos.toml#t004_edges"] required = true [[obligation]] @@ -112,7 +112,7 @@ target = "magnitude" note = "ACOS at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acos.toml#t005-outside"] +cases = ["cases/translation/functions/acos.toml#t005_outside"] required = true [[obligation]] @@ -123,7 +123,7 @@ target = "FLOAT" note = "ACOSH of FLOAT values." catches = "ASINH computed" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acosh.toml#t001-float"] +cases = ["cases/translation/functions/acosh.toml#t001_float"] required = true [[obligation]] @@ -134,7 +134,7 @@ target = "NUMBER(12,4)" note = "ACOSH of NUMBER(12,4), converted to FLOAT." catches = "ASINH computed; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acosh.toml#t002-fixed"] +cases = ["cases/translation/functions/acosh.toml#t002_fixed"] required = true [[obligation]] @@ -145,7 +145,7 @@ target = "x" note = "ACOSH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acosh.toml#t003-null"] +cases = ["cases/translation/functions/acosh.toml#t003_null"] required = true [[obligation]] @@ -157,7 +157,7 @@ target = "magnitude" note = "At 1, the domain edge, and large values." catches = "edge returned as NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acosh.toml#t004-edge"] +cases = ["cases/translation/functions/acosh.toml#t004_edge"] required = true [[obligation]] @@ -169,7 +169,7 @@ target = "magnitude" note = "ACOSH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/acosh.toml#t005-below-one"] +cases = ["cases/translation/functions/acosh.toml#t005_below_one"] required = true [[obligation]] @@ -180,7 +180,7 @@ target = "FLOAT" note = "ASIN of FLOAT values." catches = "ACOS computed instead, or degrees returned" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asin.toml#t001-float"] +cases = ["cases/translation/functions/asin.toml#t001_float"] required = true [[obligation]] @@ -191,7 +191,7 @@ target = "NUMBER(12,4)" note = "ASIN of NUMBER(12,4), converted to FLOAT." catches = "ACOS computed instead, or degrees returned; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asin.toml#t002-fixed"] +cases = ["cases/translation/functions/asin.toml#t002_fixed"] required = true [[obligation]] @@ -202,7 +202,7 @@ target = "x" note = "ASIN of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asin.toml#t003-null"] +cases = ["cases/translation/functions/asin.toml#t003_null"] required = true [[obligation]] @@ -214,7 +214,7 @@ target = "negative" note = "Odd symmetry at negatives." catches = "absolute value taken" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asin.toml#t004-odd"] +cases = ["cases/translation/functions/asin.toml#t004_odd"] required = true [[obligation]] @@ -226,7 +226,7 @@ target = "magnitude" note = "ASIN at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asin.toml#t005-outside"] +cases = ["cases/translation/functions/asin.toml#t005_outside"] required = true [[obligation]] @@ -237,7 +237,7 @@ target = "FLOAT" note = "ASINH of FLOAT values." catches = "ACOSH computed" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asinh.toml#t001-float"] +cases = ["cases/translation/functions/asinh.toml#t001_float"] required = true [[obligation]] @@ -248,7 +248,7 @@ target = "NUMBER(12,4)" note = "ASINH of NUMBER(12,4), converted to FLOAT." catches = "ACOSH computed; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asinh.toml#t002-fixed"] +cases = ["cases/translation/functions/asinh.toml#t002_fixed"] required = true [[obligation]] @@ -259,7 +259,7 @@ target = "x" note = "ASINH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asinh.toml#t003-null"] +cases = ["cases/translation/functions/asinh.toml#t003_null"] required = true [[obligation]] @@ -271,7 +271,7 @@ target = "magnitude" note = "Large arguments." catches = "precision loss in the log formula" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/asinh.toml#t004-large"] +cases = ["cases/translation/functions/asinh.toml#t004_large"] required = true [[obligation]] @@ -282,7 +282,7 @@ target = "FLOAT" note = "ATAN of FLOAT values." catches = "ATAN2 with a swapped argument, or degrees returned" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atan.toml#t001-float"] +cases = ["cases/translation/functions/atan.toml#t001_float"] required = true [[obligation]] @@ -293,7 +293,7 @@ target = "NUMBER(12,4)" note = "ATAN of NUMBER(12,4), converted to FLOAT." catches = "ATAN2 with a swapped argument, or degrees returned; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atan.toml#t002-fixed"] +cases = ["cases/translation/functions/atan.toml#t002_fixed"] required = true [[obligation]] @@ -304,7 +304,7 @@ target = "x" note = "ATAN of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atan.toml#t003-null"] +cases = ["cases/translation/functions/atan.toml#t003_null"] required = true [[obligation]] @@ -316,7 +316,7 @@ target = "magnitude" note = "Very large arguments approach pi/2." catches = "overflow handling" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atan.toml#t004-huge"] +cases = ["cases/translation/functions/atan.toml#t004_huge"] required = true [[obligation]] @@ -327,7 +327,7 @@ target = "FLOAT" note = "ATAN2 in all four quadrants." catches = "arguments swapped (ATAN2(y, x) vs ATAN2(x, y)) or ATAN(y/x) losing the quadrant" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t001-float"] +cases = ["cases/translation/functions/atan2.toml#t001_float"] required = true [[obligation]] @@ -338,7 +338,7 @@ target = "NUMBER(12,4)" note = "ATAN2 of NUMBER(12,4), converted to DOUBLE." catches = "the result left fixed-point" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t002-fixed"] +cases = ["cases/translation/functions/atan2.toml#t002_fixed"] required = true [[obligation]] @@ -349,7 +349,7 @@ target = "VARCHAR" note = "ATAN2 of numeric strings, converted to DOUBLE." catches = "the strings rejected" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t003-text"] +cases = ["cases/translation/functions/atan2.toml#t003_text"] required = true [[obligation]] @@ -360,7 +360,7 @@ target = "y" note = "NULL y." catches = "NULL turned into 0 (giving 0 or pi)" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t004-null-y"] +cases = ["cases/translation/functions/atan2.toml#t004_null_y"] required = true [[obligation]] @@ -371,7 +371,7 @@ target = "x" note = "NULL x." catches = "NULL turned into 0 (giving pi/2)" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t005-null-x"] +cases = ["cases/translation/functions/atan2.toml#t005_null_x"] required = true [[obligation]] @@ -383,7 +383,7 @@ target = "negative" note = "Signed zeros on the negative x axis." catches = "the sign of zero lost (pi vs -pi)" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t006-zeros"] +cases = ["cases/translation/functions/atan2.toml#t006_zeros"] required = true [[obligation]] @@ -394,7 +394,7 @@ target = "error" note = "A string that is not a number is an error." catches = "the string read as 0" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t007-bad-text"] +cases = ["cases/translation/functions/atan2.toml#t007_bad_text"] required = true [[obligation]] @@ -405,7 +405,7 @@ target = "error" note = "A BOOLEAN argument is an error (\"any other data type\")." catches = "TRUE read as 1" reading = "the page: y and x \"must be FLOAT\"; \"If the data type of an argument is a numeric data type other than DOUBLE, then the value is converted to DOUBLE. If ... string, the value is converted to DOUBLE if possible. If ... any other data type, the function returns an error.\"" -cases = ["cases/translation/functions/atan2.toml#t008-boolean"] +cases = ["cases/translation/functions/atan2.toml#t008_boolean"] required = true [[obligation]] @@ -416,7 +416,7 @@ target = "FLOAT" note = "ATANH of FLOAT values." catches = "TANH computed, or the odd symmetry lost" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t001-float"] +cases = ["cases/translation/functions/atanh.toml#t001_float"] required = true [[obligation]] @@ -427,7 +427,7 @@ target = "NUMBER(12,4)" note = "ATANH of NUMBER(12,4), converted to FLOAT." catches = "TANH computed, or the odd symmetry lost; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t002-fixed"] +cases = ["cases/translation/functions/atanh.toml#t002_fixed"] required = true [[obligation]] @@ -438,7 +438,7 @@ target = "x" note = "ATANH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t003-null"] +cases = ["cases/translation/functions/atanh.toml#t003_null"] required = true [[obligation]] @@ -450,7 +450,7 @@ target = "magnitude" note = "Close to the domain edges." catches = "a naive 0.5*LN((1+x)/(1-x)): 0 instead of 1e-17 near zero, or precision lost near the asymptote" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t004-near-one"] +cases = ["cases/translation/functions/atanh.toml#t004_near_one"] required = true [[obligation]] @@ -462,7 +462,7 @@ target = "magnitude" note = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t005-edge"] +cases = ["cases/translation/functions/atanh.toml#t005_edge"] required = true [[obligation]] @@ -474,7 +474,7 @@ target = "magnitude" note = "ATANH at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/atanh.toml#t006-outside"] +cases = ["cases/translation/functions/atanh.toml#t006_outside"] required = true [[obligation]] @@ -485,7 +485,7 @@ target = "FLOAT" note = "CBRT of FLOAT values." catches = "SQRT computed, or POW(x, 1/3) giving NaN for negatives" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/cbrt.toml#t001-float"] +cases = ["cases/translation/functions/cbrt.toml#t001_float"] required = true [[obligation]] @@ -496,7 +496,7 @@ target = "NUMBER(12,4)" note = "CBRT of NUMBER(12,4), converted to FLOAT." catches = "SQRT computed, or POW(x, 1/3) giving NaN for negatives; or the result left fixed-point" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/cbrt.toml#t002-fixed"] +cases = ["cases/translation/functions/cbrt.toml#t002_fixed"] required = true [[obligation]] @@ -507,7 +507,7 @@ target = "DECFLOAT" note = "CBRT of DECFLOAT, which returns DECFLOAT." catches = "a DECFLOAT argument computed and returned as FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/cbrt.toml#t003-decfloat"] +cases = ["cases/translation/functions/cbrt.toml#t003_decfloat"] required = true [[obligation]] @@ -518,7 +518,7 @@ target = "x" note = "CBRT of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/cbrt.toml#t004-null"] +cases = ["cases/translation/functions/cbrt.toml#t004_null"] required = true [[obligation]] @@ -530,7 +530,7 @@ target = "negative" note = "Cube roots of negatives are negative." catches = "NaN from a fractional power of a negative" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/cbrt.toml#t005-neg"] +cases = ["cases/translation/functions/cbrt.toml#t005_neg"] required = true [[obligation]] @@ -541,7 +541,7 @@ target = "NUMBER(38,0)" note = "CEIL of integers with a negative scale." catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t001-int"] +cases = ["cases/translation/functions/ceil.toml#t001_int"] required = true [[obligation]] @@ -552,7 +552,7 @@ target = "NUMBER(12,4)" note = "CEIL of NUMBER(12,4) with and without a scale." catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t002-dec"] +cases = ["cases/translation/functions/ceil.toml#t002_dec"] required = true [[obligation]] @@ -563,7 +563,7 @@ target = "FLOAT" note = "CEIL of FLOAT returns FLOAT." catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3); or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t003-float"] +cases = ["cases/translation/functions/ceil.toml#t003_float"] required = true [[obligation]] @@ -574,7 +574,7 @@ target = "DECFLOAT" note = "CEIL of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.28 at scale 2 rounded up to 0.29" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t004-decfloat"] +cases = ["cases/translation/functions/ceil.toml#t004_decfloat"] required = true [[obligation]] @@ -585,7 +585,7 @@ target = "input_expr" note = "CEIL of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t005-null-x"] +cases = ["cases/translation/functions/ceil.toml#t005_null_x"] required = true [[obligation]] @@ -596,7 +596,7 @@ target = "scale_expr" note = "CEIL with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t006-null-scale"] +cases = ["cases/translation/functions/ceil.toml#t006_null_scale"] required = true [[obligation]] @@ -608,7 +608,7 @@ target = "rounding_scale" note = "Scales past the input scale and far negative." catches = "scale capped or wrapping" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t007-scale-past"] +cases = ["cases/translation/functions/ceil.toml#t007_scale_past"] required = true [[obligation]] @@ -620,7 +620,7 @@ target = "negative" note = "Negative values at the rounding step." catches = "FLOOR or truncation used (CEIL(-2.5) is -2, CEIL(2.1) is 3)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/ceil.toml#t008-negatives"] +cases = ["cases/translation/functions/ceil.toml#t008_negatives"] required = true [[obligation]] @@ -631,7 +631,7 @@ target = "FLOAT" note = "COS of FLOAT values." catches = "SIN computed, or the argument read in degrees" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t001-float"] +cases = ["cases/translation/functions/cos.toml#t001_float"] required = true [[obligation]] @@ -642,7 +642,7 @@ target = "NUMBER(12,4)" note = "COS of NUMBER(12,4), converted to FLOAT." catches = "SIN computed, or the argument read in degrees; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t002-fixed"] +cases = ["cases/translation/functions/cos.toml#t002_fixed"] required = true [[obligation]] @@ -653,7 +653,7 @@ target = "x" note = "COS of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t003-null"] +cases = ["cases/translation/functions/cos.toml#t003_null"] required = true [[obligation]] @@ -665,7 +665,7 @@ target = "magnitude" note = "Large arguments." catches = "argument reduction differing" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t004-large"] +cases = ["cases/translation/functions/cos.toml#t004_large"] required = true [[obligation]] @@ -676,7 +676,7 @@ target = "FLOAT" note = "COSH of FLOAT values." catches = "SINH computed (cosh is even, sinh odd)" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t001-float"] +cases = ["cases/translation/functions/cosh.toml#t001_float"] required = true [[obligation]] @@ -687,7 +687,7 @@ target = "NUMBER(12,4)" note = "COSH of NUMBER(12,4), converted to FLOAT." catches = "SINH computed (cosh is even, sinh odd); or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t002-fixed"] +cases = ["cases/translation/functions/cosh.toml#t002_fixed"] required = true [[obligation]] @@ -698,7 +698,7 @@ target = "x" note = "COSH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t003-null"] +cases = ["cases/translation/functions/cosh.toml#t003_null"] required = true [[obligation]] @@ -710,7 +710,7 @@ target = "magnitude" note = "Arguments just past the double range (710.48)." catches = "infinity versus error" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t004-overflow-inf"] +cases = ["cases/translation/functions/cosh.toml#t004_overflow_inf"] required = true [[obligation]] @@ -721,7 +721,7 @@ target = "FLOAT" note = "COT of FLOAT values." catches = "TAN computed instead of its reciprocal" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t001-float"] +cases = ["cases/translation/functions/cot.toml#t001_float"] required = true [[obligation]] @@ -732,7 +732,7 @@ target = "NUMBER(12,4)" note = "COT of NUMBER(12,4), converted to FLOAT." catches = "TAN computed instead of its reciprocal; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t002-fixed"] +cases = ["cases/translation/functions/cot.toml#t002_fixed"] required = true [[obligation]] @@ -743,7 +743,7 @@ target = "x" note = "COT of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t003-null"] +cases = ["cases/translation/functions/cot.toml#t003_null"] required = true [[obligation]] @@ -755,7 +755,7 @@ target = "magnitude" note = "Very small arguments give large cotangents." catches = "reciprocal computed in fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t004-small"] +cases = ["cases/translation/functions/cot.toml#t004_small"] required = true [[obligation]] @@ -767,7 +767,7 @@ target = "magnitude" note = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t005-pole"] +cases = ["cases/translation/functions/cot.toml#t005_pole"] required = true [[obligation]] @@ -778,7 +778,7 @@ target = "FLOAT" note = "DEGREES of FLOAT values." catches = "RADIANS computed (the reverse conversion)" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t001-float"] +cases = ["cases/translation/functions/degrees.toml#t001_float"] required = true [[obligation]] @@ -789,7 +789,7 @@ target = "NUMBER(12,4)" note = "DEGREES of NUMBER(12,4), converted to FLOAT." catches = "RADIANS computed (the reverse conversion); or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t002-fixed"] +cases = ["cases/translation/functions/degrees.toml#t002_fixed"] required = true [[obligation]] @@ -800,7 +800,7 @@ target = "x" note = "DEGREES of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t003-null"] +cases = ["cases/translation/functions/degrees.toml#t003_null"] required = true [[obligation]] @@ -812,7 +812,7 @@ target = "magnitude" note = "Large radian values." catches = "x*180 computed before dividing by pi, overflowing where the result is finite" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t004-large"] +cases = ["cases/translation/functions/degrees.toml#t004_large"] required = true [[obligation]] @@ -824,7 +824,7 @@ target = "magnitude" note = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "Infinity versus an error" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t005-overflow"] +cases = ["cases/translation/functions/degrees.toml#t005_overflow"] required = true [[obligation]] @@ -835,7 +835,7 @@ target = "NUMBER(38,0)" note = "DIV0 of integers, as / divides." catches = "integer division (3 instead of 3.500000)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t001-int"] +cases = ["cases/translation/functions/div0.toml#t001_int"] required = true [[obligation]] @@ -846,7 +846,7 @@ target = "NUMBER(10,2)" note = "DIV0 of NUMBER(10,2) values." catches = "quotient scale differing from /" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t002-dec"] +cases = ["cases/translation/functions/div0.toml#t002_dec"] required = true [[obligation]] @@ -857,7 +857,7 @@ target = "FLOAT" note = "DIV0 of FLOAT values." catches = "infinity or NaN for a zero divisor instead of 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t003-float"] +cases = ["cases/translation/functions/div0.toml#t003_float"] required = true [[obligation]] @@ -868,7 +868,7 @@ target = "dividend" note = "NULL dividend." catches = "NULL turned into 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t004-null-a"] +cases = ["cases/translation/functions/div0.toml#t004_null_a"] required = true [[obligation]] @@ -879,7 +879,7 @@ target = "divisor" note = "NULL divisor." catches = "a NULL divisor returning 0 (that is DIV0NULL)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t005-null-b"] +cases = ["cases/translation/functions/div0.toml#t005_null_b"] required = true [[obligation]] @@ -891,7 +891,7 @@ target = "negative" note = "Negative dividends over zero." catches = "the sign of the dividend kept on the zero result (-0 instead of 0)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t006-neg-zero"] +cases = ["cases/translation/functions/div0.toml#t006_neg_zero"] required = true [[obligation]] @@ -902,7 +902,7 @@ target = "NUMBER(38,0)" note = "DIV0NULL of integers, as / divides." catches = "integer division (3 instead of 3.500000)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t001-int"] +cases = ["cases/translation/functions/div0null.toml#t001_int"] required = true [[obligation]] @@ -913,7 +913,7 @@ target = "NUMBER(10,2)" note = "DIV0NULL of NUMBER(10,2) values." catches = "quotient scale differing from /" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t002-dec"] +cases = ["cases/translation/functions/div0null.toml#t002_dec"] required = true [[obligation]] @@ -924,7 +924,7 @@ target = "FLOAT" note = "DIV0NULL of FLOAT values." catches = "infinity or NaN for a zero divisor instead of 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t003-float"] +cases = ["cases/translation/functions/div0null.toml#t003_float"] required = true [[obligation]] @@ -935,7 +935,7 @@ target = "dividend" note = "NULL dividend." catches = "NULL turned into 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t004-null-a"] +cases = ["cases/translation/functions/div0null.toml#t004_null_a"] required = true [[obligation]] @@ -946,7 +946,7 @@ target = "divisor" note = "NULL divisor." catches = "a NULL divisor returning NULL (that is DIV0)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t005-null-b"] +cases = ["cases/translation/functions/div0null.toml#t005_null_b"] required = true [[obligation]] @@ -958,7 +958,7 @@ target = "negative" note = "Negative dividends over zero." catches = "a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t006-neg-zero"] +cases = ["cases/translation/functions/div0null.toml#t006_neg_zero"] required = true [[obligation]] @@ -969,7 +969,7 @@ target = "FLOAT" note = "EXP of FLOAT values." catches = "LN computed, or e^x read as 10^x" reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t001-float"] +cases = ["cases/translation/functions/exp.toml#t001_float"] required = true [[obligation]] @@ -980,7 +980,7 @@ target = "NUMBER(12,4)" note = "EXP of NUMBER(12,4), converted to FLOAT." catches = "LN computed, or e^x read as 10^x; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t002-fixed"] +cases = ["cases/translation/functions/exp.toml#t002_fixed"] required = true [[obligation]] @@ -991,7 +991,7 @@ target = "DECFLOAT" note = "EXP of DECFLOAT, which returns DECFLOAT." catches = "a DECFLOAT argument computed and returned as FLOAT" reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t003-decfloat"] +cases = ["cases/translation/functions/exp.toml#t003_decfloat"] required = true [[obligation]] @@ -1002,7 +1002,7 @@ target = "x" note = "EXP of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t004-null"] +cases = ["cases/translation/functions/exp.toml#t004_null"] required = true [[obligation]] @@ -1014,7 +1014,7 @@ target = "magnitude" note = "Past the double range." catches = "infinity versus error" reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t005-overflow-inf"] +cases = ["cases/translation/functions/exp.toml#t005_overflow_inf"] required = true [[obligation]] @@ -1025,7 +1025,7 @@ target = "NUMBER(38,0)" note = "FACTORIAL of integers." catches = "gamma(x) computed (fractional), or x! off by one (n-1)!" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t001-int"] +cases = ["cases/translation/functions/factorial.toml#t001_int"] required = true [[obligation]] @@ -1036,7 +1036,7 @@ target = "NUMBER(10,2)" note = "FACTORIAL of an integral NUMBER(10,2)." catches = "scale carried into the result" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t002-dec-int"] +cases = ["cases/translation/functions/factorial.toml#t002_dec_int"] required = true [[obligation]] @@ -1047,7 +1047,7 @@ target = "integer_expr" note = "FACTORIAL of NULL." catches = "NULL turned into 1" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t003-null"] +cases = ["cases/translation/functions/factorial.toml#t003_null"] required = true [[obligation]] @@ -1059,7 +1059,7 @@ target = "magnitude" note = "Large factorials that still fit 38 digits." catches = "64-bit overflow (21! does not fit a BIGINT)" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t004-large"] +cases = ["cases/translation/functions/factorial.toml#t004_large"] required = true [[obligation]] @@ -1070,7 +1070,7 @@ target = "error" note = "A negative argument." catches = "a gamma value or 1 instead of the error" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t005-negative"] +cases = ["cases/translation/functions/factorial.toml#t005_negative"] required = true [[obligation]] @@ -1081,7 +1081,7 @@ target = "error" note = "A result past 38 digits." catches = "FLOAT or wrapping instead of the error" reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t006-too-big"] +cases = ["cases/translation/functions/factorial.toml#t006_too_big"] required = true [[obligation]] @@ -1092,7 +1092,7 @@ target = "NUMBER(38,0)" note = "FLOOR of integers with a negative scale." catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t001-int"] +cases = ["cases/translation/functions/floor.toml#t001_int"] required = true [[obligation]] @@ -1103,7 +1103,7 @@ target = "NUMBER(12,4)" note = "FLOOR of NUMBER(12,4) with and without a scale." catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t002-dec"] +cases = ["cases/translation/functions/floor.toml#t002_dec"] required = true [[obligation]] @@ -1114,7 +1114,7 @@ target = "FLOAT" note = "FLOOR of FLOAT returns FLOAT." catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t003-float"] +cases = ["cases/translation/functions/floor.toml#t003_float"] required = true [[obligation]] @@ -1125,7 +1125,7 @@ target = "DECFLOAT" note = "FLOOR of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t004-decfloat"] +cases = ["cases/translation/functions/floor.toml#t004_decfloat"] required = true [[obligation]] @@ -1136,7 +1136,7 @@ target = "input_expr" note = "FLOOR of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t005-null-x"] +cases = ["cases/translation/functions/floor.toml#t005_null_x"] required = true [[obligation]] @@ -1147,7 +1147,7 @@ target = "scale_expr" note = "FLOOR with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t006-null-scale"] +cases = ["cases/translation/functions/floor.toml#t006_null_scale"] required = true [[obligation]] @@ -1159,7 +1159,7 @@ target = "rounding_scale" note = "Scales past the input scale and far negative." catches = "scale capped or wrapping" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t007-scale-past"] +cases = ["cases/translation/functions/floor.toml#t007_scale_past"] required = true [[obligation]] @@ -1171,7 +1171,7 @@ target = "negative" note = "Negative values at the rounding step." catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t008-negatives"] +cases = ["cases/translation/functions/floor.toml#t008_negatives"] required = true [[obligation]] @@ -1182,7 +1182,7 @@ target = "FLOAT" note = "LN of FLOAT values." catches = "LOG10 or LOG2 computed" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t001-float"] +cases = ["cases/translation/functions/ln.toml#t001_float"] required = true [[obligation]] @@ -1193,7 +1193,7 @@ target = "NUMBER(12,4)" note = "LN of NUMBER(12,4), converted to FLOAT." catches = "LOG10 or LOG2 computed; or the result left fixed-point" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t002-fixed"] +cases = ["cases/translation/functions/ln.toml#t002_fixed"] required = true [[obligation]] @@ -1204,7 +1204,7 @@ target = "DECFLOAT" note = "LN of DECFLOAT, which returns DECFLOAT." catches = "a DECFLOAT argument computed and returned as FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t003-decfloat"] +cases = ["cases/translation/functions/ln.toml#t003_decfloat"] required = true [[obligation]] @@ -1215,7 +1215,7 @@ target = "x" note = "LN of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t004-null"] +cases = ["cases/translation/functions/ln.toml#t004_null"] required = true [[obligation]] @@ -1227,7 +1227,7 @@ target = "magnitude" note = "Very small positive values." catches = "underflow to -infinity" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t005-tiny"] +cases = ["cases/translation/functions/ln.toml#t005_tiny"] required = true [[obligation]] @@ -1238,7 +1238,7 @@ target = "error" note = "LN of 0 or a negative value is an error." catches = "NULL, NaN or -infinity instead of the error" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t006-zero"] +cases = ["cases/translation/functions/ln.toml#t006_zero"] required = true [[obligation]] @@ -1249,7 +1249,7 @@ target = "NUMBER(38,0)" note = "LOG of integers." catches = "arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t001-int"] +cases = ["cases/translation/functions/log.toml#t001_int"] required = true [[obligation]] @@ -1260,7 +1260,7 @@ target = "NUMBER(10,2)" note = "LOG of fixed-point values." catches = "base rounded to an integer" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t002-dec"] +cases = ["cases/translation/functions/log.toml#t002_dec"] required = true [[obligation]] @@ -1271,7 +1271,7 @@ target = "FLOAT" note = "LOG of FLOAT values." catches = "arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t003-float"] +cases = ["cases/translation/functions/log.toml#t003_float"] required = true [[obligation]] @@ -1282,7 +1282,7 @@ target = "NUMBER(38,0), FLOAT" note = "LOG with an integer base and a FLOAT value." catches = "type promotion differing" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t004-mixed"] +cases = ["cases/translation/functions/log.toml#t004_mixed"] required = true [[obligation]] @@ -1293,7 +1293,7 @@ target = "base" note = "NULL base." catches = "NULL turned into e or 10" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t005-null-base"] +cases = ["cases/translation/functions/log.toml#t005_null_base"] required = true [[obligation]] @@ -1304,7 +1304,7 @@ target = "expr" note = "NULL value." catches = "NULL turned into 1" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t006-null-expr"] +cases = ["cases/translation/functions/log.toml#t006_null_expr"] required = true [[obligation]] @@ -1316,7 +1316,7 @@ target = "magnitude" note = "A base between 0 and 1 gives negative logs of values above 1." catches = "the sign lost" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t007-base-below-one"] +cases = ["cases/translation/functions/log.toml#t007_base_below_one"] required = true [[obligation]] @@ -1327,7 +1327,7 @@ target = "error" note = "A base of 1 is an error." catches = "infinity or NULL instead of the error" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t008-base-one"] +cases = ["cases/translation/functions/log.toml#t008_base_one"] required = true [[obligation]] @@ -1338,7 +1338,7 @@ target = "error" note = "A negative base is an error." catches = "NaN instead of the error" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t009-base-neg"] +cases = ["cases/translation/functions/log.toml#t009_base_neg"] required = true [[obligation]] @@ -1349,7 +1349,7 @@ target = "error" note = "A value of 0 is an error." catches = "-infinity instead of the error" reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t010-expr-zero"] +cases = ["cases/translation/functions/log.toml#t010_expr_zero"] required = true [[obligation]] @@ -1360,7 +1360,7 @@ target = "NUMBER(38,0)" note = "MOD of integers with negatives." catches = "floor-based modulo (MOD(-7, 3) is -1, not 2)" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t001-int"] +cases = ["cases/translation/functions/mod.toml#t001_int"] required = true [[obligation]] @@ -1371,7 +1371,7 @@ target = "NUMBER(10,2)" note = "MOD of NUMBER(10,2) values." catches = "fractional remainder dropped" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t002-dec"] +cases = ["cases/translation/functions/mod.toml#t002_dec"] required = true [[obligation]] @@ -1382,7 +1382,7 @@ target = "NUMBER(10,2), NUMBER(38,0)" note = "MOD of a scaled and an integer operand." catches = "result scale from one side" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t003-dec-int"] +cases = ["cases/translation/functions/mod.toml#t003_dec_int"] required = true [[obligation]] @@ -1393,7 +1393,7 @@ target = "FLOAT" note = "MOD of FLOAT values." catches = "floor-based float modulo" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t004-float"] +cases = ["cases/translation/functions/mod.toml#t004_float"] required = true [[obligation]] @@ -1404,7 +1404,7 @@ target = "expr1" note = "NULL dividend." catches = "NULL treated as 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t005-null-a"] +cases = ["cases/translation/functions/mod.toml#t005_null_a"] required = true [[obligation]] @@ -1415,7 +1415,7 @@ target = "expr2" note = "NULL divisor." catches = "NULL treated as 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t006-null-b"] +cases = ["cases/translation/functions/mod.toml#t006_null_b"] required = true [[obligation]] @@ -1427,7 +1427,7 @@ target = "negative" note = "Argument order with negatives." catches = "arguments swapped" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t007-order"] +cases = ["cases/translation/functions/mod.toml#t007_order"] required = true [[obligation]] @@ -1439,7 +1439,7 @@ target = "magnitude" note = "Remainders of 38-digit integers." catches = "64-bit modulo" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t008-max"] +cases = ["cases/translation/functions/mod.toml#t008_max"] required = true [[obligation]] @@ -1450,7 +1450,7 @@ target = "error" note = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error." catches = "a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t009-zero-divisor"] +cases = ["cases/translation/functions/mod.toml#t009_zero_divisor"] required = true [[obligation]] @@ -1461,7 +1461,7 @@ target = "()" note = "PI() in arithmetic, at full double precision." catches = "a rounded constant (3.14159) or a NUMBER" reading = "the page: PI() takes no argument and returns FLOAT" -cases = ["cases/translation/functions/pi.toml#t001-value"] +cases = ["cases/translation/functions/pi.toml#t001_value"] required = true [[obligation]] @@ -1472,7 +1472,7 @@ target = "NUMBER(38,0)" note = "POW of integers returns FLOAT." catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t001-int"] +cases = ["cases/translation/functions/pow.pow.toml#t001_int"] required = true [[obligation]] @@ -1483,7 +1483,7 @@ target = "NUMBER(10,2)" note = "POW with fractional exponents." catches = "the exponent rounded" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t002-dec"] +cases = ["cases/translation/functions/pow.pow.toml#t002_dec"] required = true [[obligation]] @@ -1494,7 +1494,7 @@ target = "FLOAT" note = "POW of FLOAT values." catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t003-float"] +cases = ["cases/translation/functions/pow.pow.toml#t003_float"] required = true [[obligation]] @@ -1505,7 +1505,7 @@ target = "DECFLOAT" note = "POW of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t004-decfloat"] +cases = ["cases/translation/functions/pow.pow.toml#t004_decfloat"] required = true [[obligation]] @@ -1516,7 +1516,7 @@ target = "x" note = "NULL base." catches = "NULL^0 returning 1" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t005-null-x"] +cases = ["cases/translation/functions/pow.pow.toml#t005_null_x"] required = true [[obligation]] @@ -1527,7 +1527,7 @@ target = "y" note = "NULL exponent." catches = "1^NULL returning 1" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t006-null-y"] +cases = ["cases/translation/functions/pow.pow.toml#t006_null_y"] required = true [[obligation]] @@ -1539,7 +1539,7 @@ target = "magnitude" note = "Powers past the double range, and 0^0." catches = "infinity versus error; 0^0 as 0" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t007-overflow-inf"] +cases = ["cases/translation/functions/pow.pow.toml#t007_overflow_inf"] required = true [[obligation]] @@ -1551,7 +1551,7 @@ target = "negative" note = "Negative bases with integer exponents." catches = "the sign lost for odd exponents" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t008-neg-base"] +cases = ["cases/translation/functions/pow.pow.toml#t008_neg_base"] required = true [[obligation]] @@ -1563,7 +1563,7 @@ target = "negative" note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, NULL or an error differing from Snowflake" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.pow.toml#t009-neg-frac"] +cases = ["cases/translation/functions/pow.pow.toml#t009_neg_frac"] required = true [[obligation]] @@ -1574,7 +1574,7 @@ target = "NUMBER(38,0)" note = "POWER of integers returns FLOAT." catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t001-int"] +cases = ["cases/translation/functions/pow.power.toml#t001_int"] required = true [[obligation]] @@ -1585,7 +1585,7 @@ target = "NUMBER(10,2)" note = "POWER with fractional exponents." catches = "the exponent rounded" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t002-dec"] +cases = ["cases/translation/functions/pow.power.toml#t002_dec"] required = true [[obligation]] @@ -1596,7 +1596,7 @@ target = "FLOAT" note = "POWER of FLOAT values." catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t003-float"] +cases = ["cases/translation/functions/pow.power.toml#t003_float"] required = true [[obligation]] @@ -1607,7 +1607,7 @@ target = "DECFLOAT" note = "POWER of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t004-decfloat"] +cases = ["cases/translation/functions/pow.power.toml#t004_decfloat"] required = true [[obligation]] @@ -1618,7 +1618,7 @@ target = "x" note = "NULL base." catches = "NULL^0 returning 1" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t005-null-x"] +cases = ["cases/translation/functions/pow.power.toml#t005_null_x"] required = true [[obligation]] @@ -1629,7 +1629,7 @@ target = "y" note = "NULL exponent." catches = "1^NULL returning 1" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t006-null-y"] +cases = ["cases/translation/functions/pow.power.toml#t006_null_y"] required = true [[obligation]] @@ -1641,7 +1641,7 @@ target = "magnitude" note = "Powers past the double range, and 0^0." catches = "infinity versus error; 0^0 as 0" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t007-overflow-inf"] +cases = ["cases/translation/functions/pow.power.toml#t007_overflow_inf"] required = true [[obligation]] @@ -1653,7 +1653,7 @@ target = "negative" note = "Negative bases with integer exponents." catches = "the sign lost for odd exponents" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t008-neg-base"] +cases = ["cases/translation/functions/pow.power.toml#t008_neg_base"] required = true [[obligation]] @@ -1665,7 +1665,7 @@ target = "negative" note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, NULL or an error differing from Snowflake" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" -cases = ["cases/translation/functions/pow.power.toml#t009-neg-frac"] +cases = ["cases/translation/functions/pow.power.toml#t009_neg_frac"] required = true [[obligation]] @@ -1676,7 +1676,7 @@ target = "FLOAT" note = "RADIANS of FLOAT values." catches = "DEGREES computed (the reverse conversion)" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/radians.toml#t001-float"] +cases = ["cases/translation/functions/radians.toml#t001_float"] required = true [[obligation]] @@ -1687,7 +1687,7 @@ target = "NUMBER(12,4)" note = "RADIANS of NUMBER(12,4), converted to FLOAT." catches = "DEGREES computed (the reverse conversion); or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/radians.toml#t002-fixed"] +cases = ["cases/translation/functions/radians.toml#t002_fixed"] required = true [[obligation]] @@ -1698,7 +1698,7 @@ target = "x" note = "RADIANS of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/radians.toml#t003-null"] +cases = ["cases/translation/functions/radians.toml#t003_null"] required = true [[obligation]] @@ -1710,7 +1710,7 @@ target = "magnitude" note = "Large degree values." catches = "x*pi computed before dividing by 180, overflowing where the result is finite" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/radians.toml#t004-large"] +cases = ["cases/translation/functions/radians.toml#t004_large"] required = true [[obligation]] @@ -1721,7 +1721,7 @@ target = "NUMBER(38,0)" note = "ROUND of integers with a negative scale." catches = "half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t001-int"] +cases = ["cases/translation/functions/round.round.toml#t001_int"] required = true [[obligation]] @@ -1732,7 +1732,7 @@ target = "NUMBER(12,4)" note = "ROUND of NUMBER(12,4) halves." catches = "banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t002-dec"] +cases = ["cases/translation/functions/round.round.toml#t002_dec"] required = true [[obligation]] @@ -1743,7 +1743,7 @@ target = "FLOAT" note = "ROUND of FLOAT halves exact in binary." catches = "half-to-even for FLOAT, or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t003-float"] +cases = ["cases/translation/functions/round.round.toml#t003_float"] required = true [[obligation]] @@ -1754,7 +1754,7 @@ target = "DECFLOAT" note = "ROUND of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t004-decfloat"] +cases = ["cases/translation/functions/round.round.toml#t004_decfloat"] required = true [[obligation]] @@ -1765,7 +1765,7 @@ target = "'HALF_TO_EVEN'" note = "rounding_mode 'HALF_TO_EVEN' on fixed-point." catches = "the mode ignored (2.5 to 3 instead of 2)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t005-mode-even"] +cases = ["cases/translation/functions/round.round.toml#t005_mode_even"] required = true [[obligation]] @@ -1776,7 +1776,7 @@ target = "'HALF_AWAY_FROM_ZERO'" note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." catches = "the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t006-mode-away"] +cases = ["cases/translation/functions/round.round.toml#t006_mode_away"] required = true [[obligation]] @@ -1787,7 +1787,7 @@ target = "input_expr" note = "ROUND of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t007-null-x"] +cases = ["cases/translation/functions/round.round.toml#t007_null_x"] required = true [[obligation]] @@ -1798,7 +1798,7 @@ target = "scale_expr" note = "ROUND with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t008-null-scale"] +cases = ["cases/translation/functions/round.round.toml#t008_null_scale"] required = true [[obligation]] @@ -1810,7 +1810,7 @@ target = "rounding_scale" note = "Scales past the input scale and a carry across digits." catches = "carry lost (9.995 to 9.99) or scale capped" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t009-scale"] +cases = ["cases/translation/functions/round.round.toml#t009_scale"] required = true [[obligation]] @@ -1822,7 +1822,7 @@ target = "magnitude" note = "Rounding 38-digit integers at scale -1, near the 38-digit limit." catches = "overflow to FLOAT" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t010-max"] +cases = ["cases/translation/functions/round.round.toml#t010_max"] required = true [[obligation]] @@ -1833,7 +1833,7 @@ target = "error" note = "A rounding_mode with a FLOAT input is an error." catches = "the mode applied to FLOAT instead of the documented error" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t011-mode-float"] +cases = ["cases/translation/functions/round.round.toml#t011_mode_float"] required = true [[obligation]] @@ -1844,7 +1844,7 @@ target = "error" note = "An unknown rounding_mode is an error." catches = "the mode ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\")" -cases = ["cases/translation/functions/round.round.toml#t012-mode-bad"] +cases = ["cases/translation/functions/round.round.toml#t012_mode_bad"] required = true [[obligation]] @@ -1855,7 +1855,7 @@ target = "NUMBER(38,0)" note = "ROUND of integers with a negative scale." catches = "half-to-even (1400) instead of half away from zero (1500), or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t001-int"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t001_int"] required = true [[obligation]] @@ -1866,7 +1866,7 @@ target = "NUMBER(12,4)" note = "ROUND of NUMBER(12,4) halves." catches = "banker's rounding (2.5 to 2) instead of half away from zero (3), or truncation" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t002-dec"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t002_dec"] required = true [[obligation]] @@ -1877,7 +1877,7 @@ target = "FLOAT" note = "ROUND of FLOAT halves exact in binary." catches = "half-to-even for FLOAT, or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t003-float"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t003_float"] required = true [[obligation]] @@ -1888,7 +1888,7 @@ target = "DECFLOAT" note = "ROUND of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT (1.005 rounds to 1.01 in DECFLOAT)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t004-decfloat"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t004_decfloat"] required = true [[obligation]] @@ -1899,7 +1899,7 @@ target = "'HALF_TO_EVEN'" note = "rounding_mode 'HALF_TO_EVEN' on fixed-point." catches = "the mode ignored (2.5 to 3 instead of 2)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t005-mode-even"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t005_mode_even"] required = true [[obligation]] @@ -1910,7 +1910,7 @@ target = "'HALF_AWAY_FROM_ZERO'" note = "rounding_mode 'HALF_AWAY_FROM_ZERO' spelled out." catches = "the mode read as half-even (-0.0250 to -0.02 instead of -0.03)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t006-mode-away"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t006_mode_away"] required = true [[obligation]] @@ -1921,7 +1921,7 @@ target = "input_expr" note = "ROUND of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t007-null-x"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t007_null_x"] required = true [[obligation]] @@ -1932,7 +1932,7 @@ target = "scale_expr" note = "ROUND with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t008-null-scale"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t008_null_scale"] required = true [[obligation]] @@ -1944,7 +1944,7 @@ target = "rounding_scale" note = "Scales past the input scale and a carry across digits." catches = "carry lost (9.995 to 9.99) or scale capped" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t009-scale"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t009_scale"] required = true [[obligation]] @@ -1956,7 +1956,7 @@ target = "magnitude" note = "Rounding 38-digit integers at scale -1, near the 38-digit limit." catches = "overflow to FLOAT" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t010-max"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t010_max"] required = true [[obligation]] @@ -1967,7 +1967,7 @@ target = "error" note = "A rounding_mode with a FLOAT input is an error." catches = "the mode applied to FLOAT instead of the documented error" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t011-mode-float"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t011_mode_float"] required = true [[obligation]] @@ -1978,7 +1978,7 @@ target = "error" note = "An unknown rounding_mode is an error." catches = "the mode ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t012-mode-bad"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t012_mode_bad"] required = true [[obligation]] @@ -1989,7 +1989,7 @@ target = "error" note = "EXPR => without SCALE => is an error, as the page says." catches = "SCALE defaulted to 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type; rounding_mode 'HALF_AWAY_FROM_ZERO' (default) or 'HALF_TO_EVEN', and with a rounding_mode the input must be fixed-point (\"FLOAT aren't supported with this argument\"); \"If you specify the EXPR => named argument, you must also specify the SCALE => named argument\"" -cases = ["cases/translation/functions/round.round-expr-scale.toml#t013-expr-no-scale"] +cases = ["cases/translation/functions/round.round-expr-scale.toml#t013_expr_no_scale"] required = true [[obligation]] @@ -2000,7 +2000,7 @@ target = "NUMBER(38,0)" note = "SIGN of integers." catches = "ABS computed (-7 gives 7 instead of -1)" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/sign.toml#t001-int"] +cases = ["cases/translation/functions/sign.toml#t001_int"] required = true [[obligation]] @@ -2011,7 +2011,7 @@ target = "NUMBER(10,2)" note = "SIGN of NUMBER(10,2), keeping its type." catches = "the scale lost or FLOAT returned" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/sign.toml#t002-dec"] +cases = ["cases/translation/functions/sign.toml#t002_dec"] required = true [[obligation]] @@ -2022,7 +2022,7 @@ target = "FLOAT" note = "SIGN of FLOAT." catches = "negative zero and tiny negatives" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/sign.toml#t003-float"] +cases = ["cases/translation/functions/sign.toml#t003_float"] required = true [[obligation]] @@ -2033,7 +2033,7 @@ target = "x" note = "SIGN of NULL." catches = "NULL turned into 0" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/sign.toml#t004-null"] +cases = ["cases/translation/functions/sign.toml#t004_null"] required = true [[obligation]] @@ -2045,7 +2045,7 @@ target = "magnitude" note = "The 38-digit and double extremes." catches = "64-bit arithmetic overflowing" reading = "the page names no argument type; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT" -cases = ["cases/translation/functions/sign.toml#t005-extremes"] +cases = ["cases/translation/functions/sign.toml#t005_extremes"] required = true [[obligation]] @@ -2056,7 +2056,7 @@ target = "FLOAT" note = "SIN of FLOAT values." catches = "COS computed, or the argument read in degrees" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sin.toml#t001-float"] +cases = ["cases/translation/functions/sin.toml#t001_float"] required = true [[obligation]] @@ -2067,7 +2067,7 @@ target = "NUMBER(12,4)" note = "SIN of NUMBER(12,4), converted to FLOAT." catches = "COS computed, or the argument read in degrees; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sin.toml#t002-fixed"] +cases = ["cases/translation/functions/sin.toml#t002_fixed"] required = true [[obligation]] @@ -2078,7 +2078,7 @@ target = "x" note = "SIN of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sin.toml#t003-null"] +cases = ["cases/translation/functions/sin.toml#t003_null"] required = true [[obligation]] @@ -2090,7 +2090,7 @@ target = "magnitude" note = "Large arguments." catches = "argument reduction differing" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sin.toml#t004-large"] +cases = ["cases/translation/functions/sin.toml#t004_large"] required = true [[obligation]] @@ -2101,7 +2101,7 @@ target = "FLOAT" note = "SINH of FLOAT values." catches = "COSH computed" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sinh.toml#t001-float"] +cases = ["cases/translation/functions/sinh.toml#t001_float"] required = true [[obligation]] @@ -2112,7 +2112,7 @@ target = "NUMBER(12,4)" note = "SINH of NUMBER(12,4), converted to FLOAT." catches = "COSH computed; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sinh.toml#t002-fixed"] +cases = ["cases/translation/functions/sinh.toml#t002_fixed"] required = true [[obligation]] @@ -2123,7 +2123,7 @@ target = "x" note = "SINH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sinh.toml#t003-null"] +cases = ["cases/translation/functions/sinh.toml#t003_null"] required = true [[obligation]] @@ -2135,7 +2135,7 @@ target = "magnitude" note = "Arguments just past the double range (710.48)." catches = "infinity versus error" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/sinh.toml#t004-overflow-inf"] +cases = ["cases/translation/functions/sinh.toml#t004_overflow_inf"] required = true [[obligation]] @@ -2146,7 +2146,7 @@ target = "FLOAT" note = "SQRT of FLOAT values." catches = "CBRT or squaring computed" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t001-float"] +cases = ["cases/translation/functions/sqrt.toml#t001_float"] required = true [[obligation]] @@ -2157,7 +2157,7 @@ target = "NUMBER(12,4)" note = "SQRT of NUMBER(12,4), converted to FLOAT." catches = "CBRT or squaring computed; or the result left fixed-point" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t002-fixed"] +cases = ["cases/translation/functions/sqrt.toml#t002_fixed"] required = true [[obligation]] @@ -2168,7 +2168,7 @@ target = "DECFLOAT" note = "SQRT of DECFLOAT, which returns DECFLOAT." catches = "a DECFLOAT argument computed and returned as FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t003-decfloat"] +cases = ["cases/translation/functions/sqrt.toml#t003_decfloat"] required = true [[obligation]] @@ -2179,7 +2179,7 @@ target = "x" note = "SQRT of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t004-null"] +cases = ["cases/translation/functions/sqrt.toml#t004_null"] required = true [[obligation]] @@ -2191,7 +2191,7 @@ target = "magnitude" note = "Very small and very large values." catches = "precision at the extremes" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t005-tiny-huge"] +cases = ["cases/translation/functions/sqrt.toml#t005_tiny_huge"] required = true [[obligation]] @@ -2203,7 +2203,7 @@ target = "negative" note = "SQRT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." catches = "NaN, Infinity, NULL or an error differing from Snowflake" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/sqrt.toml#t006-negative"] +cases = ["cases/translation/functions/sqrt.toml#t006_negative"] required = true [[obligation]] @@ -2214,7 +2214,7 @@ target = "NUMBER(38,0)" note = "SQUARE of integers returns FLOAT." catches = "x*x kept as NUMBER (the type differs), or SQRT computed" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t001-int"] +cases = ["cases/translation/functions/square.toml#t001_int"] required = true [[obligation]] @@ -2225,7 +2225,7 @@ target = "NUMBER(10,2)" note = "SQUARE of NUMBER(10,2)." catches = "the result left fixed-point" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t002-dec"] +cases = ["cases/translation/functions/square.toml#t002_dec"] required = true [[obligation]] @@ -2236,7 +2236,7 @@ target = "FLOAT" note = "SQUARE of FLOAT." catches = "SQRT computed" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t003-float"] +cases = ["cases/translation/functions/square.toml#t003_float"] required = true [[obligation]] @@ -2247,7 +2247,7 @@ target = "DECFLOAT" note = "SQUARE of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t004-decfloat"] +cases = ["cases/translation/functions/square.toml#t004_decfloat"] required = true [[obligation]] @@ -2258,7 +2258,7 @@ target = "x" note = "SQUARE of NULL." catches = "NULL turned into 0" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t005-null"] +cases = ["cases/translation/functions/square.toml#t005_null"] required = true [[obligation]] @@ -2270,7 +2270,7 @@ target = "magnitude" note = "Squares past the double range." catches = "infinity versus error" reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/square.toml#t006-overflow-inf"] +cases = ["cases/translation/functions/square.toml#t006_overflow_inf"] required = true [[obligation]] @@ -2281,7 +2281,7 @@ target = "FLOAT" note = "TAN of FLOAT values." catches = "SIN or COT computed, or degrees" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tan.toml#t001-float"] +cases = ["cases/translation/functions/tan.toml#t001_float"] required = true [[obligation]] @@ -2292,7 +2292,7 @@ target = "NUMBER(12,4)" note = "TAN of NUMBER(12,4), converted to FLOAT." catches = "SIN or COT computed, or degrees; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tan.toml#t002-fixed"] +cases = ["cases/translation/functions/tan.toml#t002_fixed"] required = true [[obligation]] @@ -2303,7 +2303,7 @@ target = "x" note = "TAN of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tan.toml#t003-null"] +cases = ["cases/translation/functions/tan.toml#t003_null"] required = true [[obligation]] @@ -2315,7 +2315,7 @@ target = "magnitude" note = "Near pi/2." catches = "pole handling" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tan.toml#t004-near-pole"] +cases = ["cases/translation/functions/tan.toml#t004_near_pole"] required = true [[obligation]] @@ -2326,7 +2326,7 @@ target = "FLOAT" note = "TANH of FLOAT values." catches = "SINH or a clamped value" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tanh.toml#t001-float"] +cases = ["cases/translation/functions/tanh.toml#t001_float"] required = true [[obligation]] @@ -2337,7 +2337,7 @@ target = "NUMBER(12,4)" note = "TANH of NUMBER(12,4), converted to FLOAT." catches = "SINH or a clamped value; or the result left fixed-point" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tanh.toml#t002-fixed"] +cases = ["cases/translation/functions/tanh.toml#t002_fixed"] required = true [[obligation]] @@ -2348,7 +2348,7 @@ target = "x" note = "TANH of NULL." catches = "NULL turned into 0 or NaN" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tanh.toml#t003-null"] +cases = ["cases/translation/functions/tanh.toml#t003_null"] required = true [[obligation]] @@ -2360,7 +2360,7 @@ target = "magnitude" note = "Large arguments saturate to 1 and -1." catches = "NaN from exp overflow" reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/tanh.toml#t004-saturate"] +cases = ["cases/translation/functions/tanh.toml#t004_saturate"] required = true [[obligation]] @@ -2371,7 +2371,7 @@ target = "NUMBER(38,0)" note = "TRUNC of integers with a negative scale." catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t001-int"] +cases = ["cases/translation/functions/trunc.trunc.toml#t001_int"] required = true [[obligation]] @@ -2382,7 +2382,7 @@ target = "NUMBER(12,4)" note = "TRUNC of NUMBER(12,4) with and without a scale." catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t002-dec"] +cases = ["cases/translation/functions/trunc.trunc.toml#t002_dec"] required = true [[obligation]] @@ -2393,7 +2393,7 @@ target = "FLOAT" note = "TRUNC of FLOAT returns FLOAT." catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen; or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t003-float"] +cases = ["cases/translation/functions/trunc.trunc.toml#t003_float"] required = true [[obligation]] @@ -2404,7 +2404,7 @@ target = "DECFLOAT" note = "TRUNC of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t004-decfloat"] +cases = ["cases/translation/functions/trunc.trunc.toml#t004_decfloat"] required = true [[obligation]] @@ -2415,7 +2415,7 @@ target = "input_expr" note = "TRUNC of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t005-null-x"] +cases = ["cases/translation/functions/trunc.trunc.toml#t005_null_x"] required = true [[obligation]] @@ -2426,7 +2426,7 @@ target = "scale_expr" note = "TRUNC with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t006-null-scale"] +cases = ["cases/translation/functions/trunc.trunc.toml#t006_null_scale"] required = true [[obligation]] @@ -2438,7 +2438,7 @@ target = "rounding_scale" note = "Scales past the input scale and far negative." catches = "scale capped or wrapping" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t007-scale-past"] +cases = ["cases/translation/functions/trunc.trunc.toml#t007_scale_past"] required = true [[obligation]] @@ -2450,7 +2450,7 @@ target = "negative" note = "Negative values at the rounding step." catches = "FLOOR used for negatives (TRUNC(-2.7) is -2), or the date TRUNC chosen" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.trunc.toml#t008-negatives"] +cases = ["cases/translation/functions/trunc.trunc.toml#t008_negatives"] required = true [[obligation]] @@ -2461,7 +2461,7 @@ target = "NUMBER(38,0)" note = "TRUNCATE of integers with a negative scale." catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a negative scale ignored" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t001-int"] +cases = ["cases/translation/functions/trunc.truncate.toml#t001_int"] required = true [[obligation]] @@ -2472,7 +2472,7 @@ target = "NUMBER(12,4)" note = "TRUNCATE of NUMBER(12,4) with and without a scale." catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t002-dec"] +cases = ["cases/translation/functions/trunc.truncate.toml#t002_dec"] required = true [[obligation]] @@ -2483,7 +2483,7 @@ target = "FLOAT" note = "TRUNCATE of FLOAT returns FLOAT." catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2); or a NUMBER returned" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t003-float"] +cases = ["cases/translation/functions/trunc.truncate.toml#t003_float"] required = true [[obligation]] @@ -2494,7 +2494,7 @@ target = "DECFLOAT" note = "TRUNCATE of DECFLOAT returns DECFLOAT." catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t004-decfloat"] +cases = ["cases/translation/functions/trunc.truncate.toml#t004_decfloat"] required = true [[obligation]] @@ -2505,7 +2505,7 @@ target = "input_expr" note = "TRUNCATE of NULL." catches = "NULL turned into 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t005-null-x"] +cases = ["cases/translation/functions/trunc.truncate.toml#t005_null_x"] required = true [[obligation]] @@ -2516,7 +2516,7 @@ target = "scale_expr" note = "TRUNCATE with a NULL scale." catches = "the NULL scale read as 0" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t006-null-scale"] +cases = ["cases/translation/functions/trunc.truncate.toml#t006_null_scale"] required = true [[obligation]] @@ -2528,7 +2528,7 @@ target = "rounding_scale" note = "Scales past the input scale and far negative." catches = "a negative scale beyond the integer digits not giving 0, or a scale above the input scale changing the value" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t007-scale-past"] +cases = ["cases/translation/functions/trunc.truncate.toml#t007_scale_past"] required = true [[obligation]] @@ -2540,7 +2540,7 @@ target = "negative" note = "Negative values at the rounding step." catches = "FLOOR used for negatives (TRUNCATE(-2.7) is -2)" reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/trunc.truncate.toml#t008-negatives"] +cases = ["cases/translation/functions/trunc.truncate.toml#t008_negatives"] required = true [[obligation]] @@ -2551,7 +2551,7 @@ target = "NUMBER(38,0)" note = "WIDTH_BUCKET of integers, including below and above the range." catches = "buckets numbered from 0, or the overflow buckets 0 and n+1 missing" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t001-int"] +cases = ["cases/translation/functions/width-bucket.toml#t001_int"] required = true [[obligation]] @@ -2562,7 +2562,7 @@ target = "NUMBER(10,2)" note = "WIDTH_BUCKET of fixed-point values on bucket edges." catches = "edges assigned to the lower bucket" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t002-dec"] +cases = ["cases/translation/functions/width-bucket.toml#t002_dec"] required = true [[obligation]] @@ -2573,7 +2573,7 @@ target = "FLOAT" note = "WIDTH_BUCKET of FLOAT values with FLOAT end points." catches = "integer end points assumed" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t003-float"] +cases = ["cases/translation/functions/width-bucket.toml#t003_float"] required = true [[obligation]] @@ -2584,7 +2584,7 @@ target = "min_value > max_value" note = "End points given high to low." catches = "the end points swapped back, numbering buckets upwards" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t004-reversed"] +cases = ["cases/translation/functions/width-bucket.toml#t004_reversed"] required = true [[obligation]] @@ -2595,7 +2595,7 @@ target = "expr" note = "NULL value." catches = "NULL put in bucket 0" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t005-null-expr"] +cases = ["cases/translation/functions/width-bucket.toml#t005_null_expr"] required = true [[obligation]] @@ -2606,7 +2606,7 @@ target = "min_value" note = "NULL low end point." catches = "NULL read as 0" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t006-null-min"] +cases = ["cases/translation/functions/width-bucket.toml#t006_null_min"] required = true [[obligation]] @@ -2617,7 +2617,7 @@ target = "max_value" note = "NULL high end point." catches = "NULL read as 0" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t007-null-max"] +cases = ["cases/translation/functions/width-bucket.toml#t007_null_max"] required = true [[obligation]] @@ -2628,7 +2628,7 @@ target = "num_buckets" note = "NULL bucket count." catches = "NULL read as 1" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t008-null-n"] +cases = ["cases/translation/functions/width-bucket.toml#t008_null_n"] required = true [[obligation]] @@ -2640,7 +2640,7 @@ target = "magnitude" note = "Values at the documented 2^53 - 1 limit." catches = "precision loss rounding the limit into another bucket" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t009-range-edge"] +cases = ["cases/translation/functions/width-bucket.toml#t009_range_edge"] required = true [[obligation]] @@ -2651,7 +2651,7 @@ target = "error" note = "num_buckets 0 is an error." catches = "bucket 0 returned" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t010-zero-buckets"] +cases = ["cases/translation/functions/width-bucket.toml#t010_zero_buckets"] required = true [[obligation]] @@ -2662,5 +2662,5 @@ target = "error" note = "Equal end points are an error." catches = "division by zero returning NULL or infinity" reading = "the page: expr, min_value and max_value numeric within -(2^53 - 1) to 2^53 - 1, the end points \"not be equal\"; num_buckets \"must be a positive integer value\"" -cases = ["cases/translation/functions/width-bucket.toml#t011-equal-ends"] +cases = ["cases/translation/functions/width-bucket.toml#t011_equal_ends"] required = true From 28714ad5980f138783fafbbdb46b6a6e34e29b4f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:54:34 +0000 Subject: [PATCH 05/95] Translation obligations: batch 2, string and binary functions 97 string forms, 541 obligations, one case each: type combinations from each page's argument text, NULL in each argument, boundaries (empty strings, negative and out-of-range positions, unsigned 64-bit digests) and error parity. Inputs are chosen so the named wrong behaviour gives a different answer: non-ASCII text where bytes and characters differ, asymmetric argument pairs, NULL beside a set row. --- cases/translation/functions/ascii.toml | 40 + .../functions/base64-decode-binary.toml | 75 + .../functions/base64-decode-string.toml | 75 + .../translation/functions/base64-encode.toml | 100 + cases/translation/functions/bit-length.toml | 51 + cases/translation/functions/charindex.toml | 100 + cases/translation/functions/chr.toml | 40 + .../functions/collate.collate.syntax.toml | 50 + .../functions/collate.collate.toml | 50 + cases/translation/functions/collation.toml | 26 + cases/translation/functions/compress.toml | 83 + cases/translation/functions/concat-ws.toml | 71 + .../concat.concat-subscript-dot.toml | 50 + .../translation/functions/concat.concat.toml | 61 + cases/translation/functions/contains.toml | 53 + .../functions/decompress-binary.toml | 72 + .../functions/decompress-string.toml | 72 + cases/translation/functions/editdistance.toml | 88 + cases/translation/functions/endswith.toml | 53 + .../hash-agg.hash-agg-over.syntax.toml | 29 + .../functions/hash-agg.hash-agg-over.toml | 43 + .../functions/hash-agg.hash-agg.toml | 67 + cases/translation/functions/hash.toml | 63 + .../functions/hex-decode-binary.toml | 62 + .../functions/hex-decode-string.toml | 61 + cases/translation/functions/hex-encode.toml | 84 + cases/translation/functions/ilike-any.toml | 78 + .../functions/ilike.ilike.syntax.toml | 120 + cases/translation/functions/ilike.ilike.toml | 118 + cases/translation/functions/initcap.toml | 63 + cases/translation/functions/insert.toml | 96 + .../functions/jarowinkler-similarity.toml | 53 + cases/translation/functions/left.toml | 63 + cases/translation/functions/length.len.toml | 52 + .../translation/functions/length.length.toml | 52 + cases/translation/functions/like-all.toml | 78 + cases/translation/functions/like-any.toml | 78 + .../functions/like.like.syntax.toml | 120 + cases/translation/functions/like.like.toml | 118 + cases/translation/functions/lower.toml | 38 + cases/translation/functions/lpad.toml | 107 + cases/translation/functions/ltrim.toml | 64 + cases/translation/functions/md5-binary.toml | 39 + .../functions/md5-number-lower64.toml | 41 + .../functions/md5-number-upper64.toml | 41 + cases/translation/functions/md5.md5-hex.toml | 39 + cases/translation/functions/md5.md5.toml | 39 + cases/translation/functions/octet-length.toml | 51 + cases/translation/functions/parse-ip.toml | 73 + cases/translation/functions/parse-url.toml | 61 + .../functions/position.position-in.toml | 63 + .../functions/position.position.toml | 100 + cases/translation/functions/regexp-count.toml | 111 + cases/translation/functions/regexp-instr.toml | 159 + cases/translation/functions/regexp-like.toml | 114 + .../translation/functions/regexp-replace.toml | 145 + .../functions/regexp-substr-all.toml | 134 + .../translation/functions/regexp-substr.toml | 134 + cases/translation/functions/regexp.toml | 102 + cases/translation/functions/repeat.toml | 62 + cases/translation/functions/replace.toml | 85 + cases/translation/functions/reverse.toml | 49 + cases/translation/functions/right.toml | 63 + .../functions/rlike.rlike.syntax.toml | 102 + cases/translation/functions/rlike.rlike.toml | 114 + cases/translation/functions/rpad.toml | 107 + cases/translation/functions/rtrim.toml | 64 + .../functions/rtrimmed-length.toml | 41 + cases/translation/functions/search-ip.toml | 51 + cases/translation/functions/search.toml | 64 + cases/translation/functions/sha1-binary.toml | 39 + .../translation/functions/sha1.sha1-hex.toml | 39 + cases/translation/functions/sha1.sha1.toml | 39 + cases/translation/functions/sha2-binary.toml | 75 + .../translation/functions/sha2.sha2-hex.toml | 75 + cases/translation/functions/sha2.sha2.toml | 75 + cases/translation/functions/soundex-p123.toml | 28 + cases/translation/functions/soundex.toml | 43 + cases/translation/functions/space.toml | 50 + cases/translation/functions/split-part.toml | 88 + .../translation/functions/split-to-table.toml | 51 + cases/translation/functions/split.toml | 51 + cases/translation/functions/startswith.toml | 53 + .../functions/strtok-split-to-table.toml | 63 + .../functions/strtok-to-array.toml | 62 + cases/translation/functions/strtok.toml | 87 + .../translation/functions/substr.substr.toml | 110 + .../functions/substr.substring.toml | 110 + cases/translation/functions/translate.toml | 74 + cases/translation/functions/trim.toml | 64 + .../functions/try-base64-decode-binary.toml | 74 + .../functions/try-base64-decode-string.toml | 74 + .../functions/try-hex-decode-binary.toml | 53 + .../functions/try-hex-decode-string.toml | 52 + cases/translation/functions/unicode.toml | 41 + cases/translation/functions/upper.toml | 49 + cases/translation/functions/uuid-string.toml | 39 + .../translation/obligations/functions.toml | 8226 ++++++++++++++--- 98 files changed, 13958 insertions(+), 1091 deletions(-) create mode 100644 cases/translation/functions/ascii.toml create mode 100644 cases/translation/functions/base64-decode-binary.toml create mode 100644 cases/translation/functions/base64-decode-string.toml create mode 100644 cases/translation/functions/base64-encode.toml create mode 100644 cases/translation/functions/bit-length.toml create mode 100644 cases/translation/functions/charindex.toml create mode 100644 cases/translation/functions/chr.toml create mode 100644 cases/translation/functions/collate.collate.syntax.toml create mode 100644 cases/translation/functions/collate.collate.toml create mode 100644 cases/translation/functions/collation.toml create mode 100644 cases/translation/functions/compress.toml create mode 100644 cases/translation/functions/concat-ws.toml create mode 100644 cases/translation/functions/concat.concat-subscript-dot.toml create mode 100644 cases/translation/functions/concat.concat.toml create mode 100644 cases/translation/functions/contains.toml create mode 100644 cases/translation/functions/decompress-binary.toml create mode 100644 cases/translation/functions/decompress-string.toml create mode 100644 cases/translation/functions/editdistance.toml create mode 100644 cases/translation/functions/endswith.toml create mode 100644 cases/translation/functions/hash-agg.hash-agg-over.syntax.toml create mode 100644 cases/translation/functions/hash-agg.hash-agg-over.toml create mode 100644 cases/translation/functions/hash-agg.hash-agg.toml create mode 100644 cases/translation/functions/hash.toml create mode 100644 cases/translation/functions/hex-decode-binary.toml create mode 100644 cases/translation/functions/hex-decode-string.toml create mode 100644 cases/translation/functions/hex-encode.toml create mode 100644 cases/translation/functions/ilike-any.toml create mode 100644 cases/translation/functions/ilike.ilike.syntax.toml create mode 100644 cases/translation/functions/ilike.ilike.toml create mode 100644 cases/translation/functions/initcap.toml create mode 100644 cases/translation/functions/insert.toml create mode 100644 cases/translation/functions/jarowinkler-similarity.toml create mode 100644 cases/translation/functions/left.toml create mode 100644 cases/translation/functions/length.len.toml create mode 100644 cases/translation/functions/length.length.toml create mode 100644 cases/translation/functions/like-all.toml create mode 100644 cases/translation/functions/like-any.toml create mode 100644 cases/translation/functions/like.like.syntax.toml create mode 100644 cases/translation/functions/like.like.toml create mode 100644 cases/translation/functions/lower.toml create mode 100644 cases/translation/functions/lpad.toml create mode 100644 cases/translation/functions/ltrim.toml create mode 100644 cases/translation/functions/md5-binary.toml create mode 100644 cases/translation/functions/md5-number-lower64.toml create mode 100644 cases/translation/functions/md5-number-upper64.toml create mode 100644 cases/translation/functions/md5.md5-hex.toml create mode 100644 cases/translation/functions/md5.md5.toml create mode 100644 cases/translation/functions/octet-length.toml create mode 100644 cases/translation/functions/parse-ip.toml create mode 100644 cases/translation/functions/parse-url.toml create mode 100644 cases/translation/functions/position.position-in.toml create mode 100644 cases/translation/functions/position.position.toml create mode 100644 cases/translation/functions/regexp-count.toml create mode 100644 cases/translation/functions/regexp-instr.toml create mode 100644 cases/translation/functions/regexp-like.toml create mode 100644 cases/translation/functions/regexp-replace.toml create mode 100644 cases/translation/functions/regexp-substr-all.toml create mode 100644 cases/translation/functions/regexp-substr.toml create mode 100644 cases/translation/functions/regexp.toml create mode 100644 cases/translation/functions/repeat.toml create mode 100644 cases/translation/functions/replace.toml create mode 100644 cases/translation/functions/reverse.toml create mode 100644 cases/translation/functions/right.toml create mode 100644 cases/translation/functions/rlike.rlike.syntax.toml create mode 100644 cases/translation/functions/rlike.rlike.toml create mode 100644 cases/translation/functions/rpad.toml create mode 100644 cases/translation/functions/rtrim.toml create mode 100644 cases/translation/functions/rtrimmed-length.toml create mode 100644 cases/translation/functions/search-ip.toml create mode 100644 cases/translation/functions/search.toml create mode 100644 cases/translation/functions/sha1-binary.toml create mode 100644 cases/translation/functions/sha1.sha1-hex.toml create mode 100644 cases/translation/functions/sha1.sha1.toml create mode 100644 cases/translation/functions/sha2-binary.toml create mode 100644 cases/translation/functions/sha2.sha2-hex.toml create mode 100644 cases/translation/functions/sha2.sha2.toml create mode 100644 cases/translation/functions/soundex-p123.toml create mode 100644 cases/translation/functions/soundex.toml create mode 100644 cases/translation/functions/space.toml create mode 100644 cases/translation/functions/split-part.toml create mode 100644 cases/translation/functions/split-to-table.toml create mode 100644 cases/translation/functions/split.toml create mode 100644 cases/translation/functions/startswith.toml create mode 100644 cases/translation/functions/strtok-split-to-table.toml create mode 100644 cases/translation/functions/strtok-to-array.toml create mode 100644 cases/translation/functions/strtok.toml create mode 100644 cases/translation/functions/substr.substr.toml create mode 100644 cases/translation/functions/substr.substring.toml create mode 100644 cases/translation/functions/translate.toml create mode 100644 cases/translation/functions/trim.toml create mode 100644 cases/translation/functions/try-base64-decode-binary.toml create mode 100644 cases/translation/functions/try-base64-decode-string.toml create mode 100644 cases/translation/functions/try-hex-decode-binary.toml create mode 100644 cases/translation/functions/try-hex-decode-string.toml create mode 100644 cases/translation/functions/unicode.toml create mode 100644 cases/translation/functions/upper.toml create mode 100644 cases/translation/functions/uuid-string.toml diff --git a/cases/translation/functions/ascii.toml b/cases/translation/functions/ascii.toml new file mode 100644 index 000000000..3aeebbd08 --- /dev/null +++ b/cases/translation/functions/ascii.toml @@ -0,0 +1,40 @@ +category = "translation" +construct = "sf.functions.ascii" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "ASCII of strings whose first and last characters differ. Catches: the code of the last character, or the string length, instead of the first character's code" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], + ["'Zed'"], + ["'7x'"], +] +query = "SELECT ASCII(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL input next to a set one. Catches: NULL read as an empty string (0 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'b'"], +] +query = "SELECT ASCII(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which the page says gives 0. Catches: NULL for an empty string instead of 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'A'"], +] +query = "SELECT ASCII(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/base64-decode-binary.toml b/cases/translation/functions/base64-decode-binary.toml new file mode 100644 index 000000000..0ba4b90f6 --- /dev/null +++ b/cases/translation/functions/base64-decode-binary.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.base64-decode-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "BASE64_DECODE_BINARY of standard Base64 with zero, one and two padding characters. Catches: a byte-order or sign error in the decoded bytes, a dropped final group, or UTF-8 text read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'+/8='"], + ["'AEhp'"], + ["'w6kA'"], +] +query = "SELECT BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_alphabet" +description = "A custom alphabet for indexes 62 and 63 and the padding character. Catches: the alphabet argument ignored (an error or other bytes for -_ and ~ padding)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'-_8~'", "'-_~'"], + ["'+$8='", "'+$'"], +] +query = "SELECT BASE64_DECODE_BINARY(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'YQ=='"], +] +query = "SELECT BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_alphabet" +description = "NULL alphabet next to a set one. Catches: a NULL alphabet treated as the default (a value instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'YQ=='", "NULL"], + ["'YQ=='", "'+/='"], +] +query = "SELECT BASE64_DECODE_BINARY(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "The empty string decodes to an empty value. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'YWI='"], +] +query = "SELECT BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_invalid" +description = "Text that is not Base64. Catches: NULL (the TRY_ behaviour) where Snowflake raises an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab$c'"], +] +query = "SELECT BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/base64-decode-string.toml b/cases/translation/functions/base64-decode-string.toml new file mode 100644 index 000000000..db95e1e8e --- /dev/null +++ b/cases/translation/functions/base64-decode-string.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.base64-decode-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "BASE64_DECODE_STRING of standard Base64 with zero, one and two padding characters. Catches: a byte-order or sign error in the decoded bytes, a dropped final group, or UTF-8 text read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'SMOpbGxvIHfDtnJsZA=='"], + ["'fn5+Pw=='"], + ["'YQ=='"], +] +query = "SELECT BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_alphabet" +description = "A custom alphabet for indexes 62 and 63 and the padding character. Catches: the alphabet argument ignored (an error or other bytes for -_ and ~ padding)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'fn5-Pw~~'", "'-_~'"], + ["'fn5+Pw=='", "'+$'"], +] +query = "SELECT BASE64_DECODE_STRING(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'YQ=='"], +] +query = "SELECT BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_alphabet" +description = "NULL alphabet next to a set one. Catches: a NULL alphabet treated as the default (a value instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'YQ=='", "NULL"], + ["'YQ=='", "'+/='"], +] +query = "SELECT BASE64_DECODE_STRING(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "The empty string decodes to an empty value. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'YWI='"], +] +query = "SELECT BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_invalid" +description = "Text that is not Base64. Catches: NULL (the TRY_ behaviour) where Snowflake raises an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab$c'"], +] +query = "SELECT BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/base64-encode.toml b/cases/translation/functions/base64-encode.toml new file mode 100644 index 000000000..821c6bf51 --- /dev/null +++ b/cases/translation/functions/base64-encode.toml @@ -0,0 +1,100 @@ +category = "translation" +construct = "sf.functions.base64-encode" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "BASE64_ENCODE of UTF-8 text of lengths 1, 2 and 3 modulo 3. Catches: Latin-1 instead of UTF-8 bytes (\u00e9 as one byte), or missing padding" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'\u00e9'"], + ["'ab'"], + ["'Snowflake'"], +] +query = "SELECT BASE64_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "BASE64_ENCODE of bytes that use indexes 62 and 63. Catches: the binary read as its hex text (encoding 'FBFF' as characters)" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'FBFF'"], + ["'000102'"], +] +query = "SELECT BASE64_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_max_line_length" +description = "A line length shorter than the output. Catches: max_line_length ignored (no line breaks) or applied to the input length" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'Hello World Hello World'", "8"], + ["'Hello World'", "0"], +] +query = "SELECT BASE64_ENCODE(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_alphabet" +description = "A custom alphabet with a custom padding character. Catches: the alphabet argument ignored (+/ and = in the output)" +columns = [{ name = "a", type = "BINARY" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'FBFF'", "'-_~'"], + ["'7E7E7E3F'", "'*!'"], +] +query = "SELECT BASE64_ENCODE(a, 0, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_input" +description = "NULL input next to a set one. Catches: NULL encoded as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT BASE64_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_max_line_length" +description = "NULL line length next to a set one. Catches: NULL read as 0 (no line breaks) instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "NULL"], + ["'abc'", "2"], +] +query = "SELECT BASE64_ENCODE(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_alphabet" +description = "NULL alphabet next to a set one. Catches: NULL read as the default alphabet" +columns = [{ name = "a", type = "BINARY" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'FBFF'", "NULL"], + ["'FBFF'", "'-_'"], +] +query = "SELECT BASE64_ENCODE(a, 0, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_empty" +description = "The empty string encodes to an empty string. Catches: NULL or padding for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'x'"], +] +query = "SELECT BASE64_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/bit-length.toml b/cases/translation/functions/bit-length.toml new file mode 100644 index 000000000..e46a1c6c7 --- /dev/null +++ b/cases/translation/functions/bit-length.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.bit-length" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "BIT_LENGTH of multi-byte text. Catches: the character count instead of UTF-8 bits (5 characters, 6 bytes)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'h\u00e9llo'"], + ["'\u20ac'"], + ["'ab'"], +] +query = "SELECT BIT_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "BIT_LENGTH of binary. Catches: the hex text length instead of the byte length" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'00FF10'"], +] +query = "SELECT BIT_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string_or_binary" +description = "NULL next to a set value. Catches: NULL read as 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT BIT_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'x'"], +] +query = "SELECT BIT_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/charindex.toml b/cases/translation/functions/charindex.toml new file mode 100644 index 000000000..5717500f5 --- /dev/null +++ b/cases/translation/functions/charindex.toml @@ -0,0 +1,100 @@ +category = "translation" +construct = "sf.functions.charindex" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Search for a substring after multi-byte characters. Catches: arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'an'", "'banana'"], + ["'l'", "'\u00e9\u00e9l'"], + ["'x'", "'abc'"], +] +query = "SELECT CHARINDEX(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_start_pos" +description = "A start position past the first match. Catches: the start position ignored (2 instead of 4) or the result counted from the start position" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'an'", "'banana'", "3"], + ["'an'", "'banana'", "5"], + ["'\u00e9'", "'\u00e9a\u00e9'", "2"], +] +query = "SELECT CHARINDEX(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "Search in binary values. Catches: a hex-text offset (3 instead of 2)" +columns = [{ name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }] +rows = [ + ["'FF'", "'00FF'"], +] +query = "SELECT CHARINDEX(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_expr1" +description = "NULL needle next to a set one. Catches: NULL read as an empty needle (1 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'abc'"], + ["'c'", "'abc'"], +] +query = "SELECT CHARINDEX(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_expr2" +description = "NULL haystack next to a set one. Catches: NULL read as an empty string (0 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "'ba'"], +] +query = "SELECT CHARINDEX(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty_needle" +description = "An empty needle, which the page says gives 1. Catches: NULL or 0 for an empty needle" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["''", "'abc'"], + ["''", "''"], +] +query = "SELECT CHARINDEX(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_null_start_pos" +description = "NULL start next to a set one. Catches: NULL read as the default 1" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "'ba'", "NULL"], + ["'a'", "'ba'", "1"], +] +query = "SELECT CHARINDEX(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_start_past_end" +description = "A start position past the end of the string. Catches: an error or the last match instead of 0" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "'aba'", "4"], + ["'a'", "'aba'", "99"], +] +query = "SELECT CHARINDEX(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/chr.toml b/cases/translation/functions/chr.toml new file mode 100644 index 000000000..ae38bfade --- /dev/null +++ b/cases/translation/functions/chr.toml @@ -0,0 +1,40 @@ +category = "translation" +construct = "sf.functions.chr" +session_profile = "utc-v1" + +[[case]] +id = "t001_number" +description = "CHR of ASCII and non-ASCII code points. Catches: a byte or Latin-1 conversion (233 and 8364 not giving \u00e9 and \u20ac), or an error past 255" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["65"], + ["233"], + ["8364"], + ["10052"], +] +query = "SELECT CHR(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL code point next to a set one. Catches: NULL read as 0 (a NUL character instead of NULL)" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["97"], +] +query = "SELECT CHR(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_negative" +description = "A negative code point: records what Snowflake returns there; it moves to boundary if Snowflake returns a value instead of an error. Catches: an empty string or a wrapped code point where Snowflake raises an error" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["-1"], +] +query = "SELECT CHR(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/collate.collate.syntax.toml b/cases/translation/functions/collate.collate.syntax.toml new file mode 100644 index 000000000..8b7c56531 --- /dev/null +++ b/cases/translation/functions/collate.collate.syntax.toml @@ -0,0 +1,50 @@ +category = "translation" +construct = "sf.functions.collate.collate.syntax" +session_profile = "utc-v1" + +[[case]] +id = "t001_case_insensitive" +description = "Equality under a case-insensitive collation. Catches: the collation dropped (FALSE for ABC = abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abd'"], +] +query = "SELECT (a COLLATE 'en-ci') = b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_locale_order" +description = "Ordering under a locale collation. Catches: binary order kept ('a' < 'B' is FALSE in byte order, TRUE in en)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "'B'"], + ["'b'", "'A'"], +] +query = "SELECT (a COLLATE 'en') < b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_value_unchanged" +description = "The returned string itself, with trailing blanks. Catches: the string case-folded or trimmed by the copy (the page: \"The string itself is unchanged\")" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'MiXed '"], +] +query = "SELECT a COLLATE 'en-ci-rtrim' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_string_expression" +description = "NULL string next to a set one. Catches: NULL compared as an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "''"], + ["'A'", "'a'"], +] +query = "SELECT (a COLLATE 'en-ci') = b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/collate.collate.toml b/cases/translation/functions/collate.collate.toml new file mode 100644 index 000000000..d600f43cc --- /dev/null +++ b/cases/translation/functions/collate.collate.toml @@ -0,0 +1,50 @@ +category = "translation" +construct = "sf.functions.collate.collate" +session_profile = "utc-v1" + +[[case]] +id = "t001_case_insensitive" +description = "Equality under a case-insensitive collation. Catches: the collation dropped (FALSE for ABC = abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abd'"], +] +query = "SELECT (COLLATE(a, 'en-ci')) = b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_locale_order" +description = "Ordering under a locale collation. Catches: binary order kept ('a' < 'B' is FALSE in byte order, TRUE in en)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "'B'"], + ["'b'", "'A'"], +] +query = "SELECT (COLLATE(a, 'en')) < b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_value_unchanged" +description = "The returned string itself, with trailing blanks. Catches: the string case-folded or trimmed by the copy (the page: \"The string itself is unchanged\")" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'MiXed '"], +] +query = "SELECT COLLATE(a, 'en-ci-rtrim') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_string_expression" +description = "NULL string next to a set one. Catches: NULL compared as an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "''"], + ["'A'", "'a'"], +] +query = "SELECT (COLLATE(a, 'en-ci')) = b AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/collation.toml b/cases/translation/functions/collation.toml new file mode 100644 index 000000000..98a074b04 --- /dev/null +++ b/cases/translation/functions/collation.toml @@ -0,0 +1,26 @@ +category = "translation" +construct = "sf.functions.collation" +session_profile = "utc-v1" + +[[case]] +id = "t001_collated" +description = "COLLATION of a collated and an uncollated string. Catches: a fixed or empty answer, or the collation of the wrong operand" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'x'"], +] +query = "SELECT COLLATION(COLLATE(a, 'de-ci')) AS r, COLLATION(a) AS u FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expression" +description = "NULL collated value. Catches: NULL propagated (NULL instead of 'de')" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'y'"], +] +query = "SELECT COLLATION(COLLATE(a, 'de')) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/compress.toml b/cases/translation/functions/compress.toml new file mode 100644 index 000000000..de46bdbb2 --- /dev/null +++ b/cases/translation/functions/compress.toml @@ -0,0 +1,83 @@ +category = "translation" +construct = "sf.functions.compress" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar_snappy" +description = "COMPRESS of the page's example and of multi-byte text with SNAPPY. Catches: another codec or framing (the page gives 0920536E6F77666C616B65)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'h\u00e9llo h\u00e9llo h\u00e9llo'"], +] +query = "SELECT COMPRESS(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "COMPRESS of a BINARY value. Catches: the binary read as its hex text" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'536E6F77666C616B65'"], +] +query = "SELECT COMPRESS(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_methods" +description = "Each method, with a level and lower-case names, read back through DECOMPRESS_STRING. Catches: a method name or level rejected, or compress and decompress disagreeing on the codec or its framing" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abcabcabcabc'"], +] +query = "SELECT DECOMPRESS_STRING(COMPRESS(a, 'zlib(1)'), 'ZLIB') AS z, DECOMPRESS_STRING(COMPRESS(a, 'ZSTD'), 'zstd') AS d, DECOMPRESS_STRING(COMPRESS(a, 'BZ2'), 'BZ2') AS b FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_input" +description = "NULL input next to a set one. Catches: NULL compressed as an empty value" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT COMPRESS(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_method" +description = "NULL method. Catches: NULL read as a default method" +columns = [{ name = "a", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'ab'", "NULL"], + ["'ab'", "'SNAPPY'"], +] +query = "SELECT COMPRESS(a, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "The empty string with SNAPPY. Catches: NULL for empty input instead of a length-0 block" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT COMPRESS(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_unknown_method" +description = "An unknown method, which the page says fails. Catches: a fallback codec or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab'"], +] +query = "SELECT COMPRESS(a, 'LZ4') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/concat-ws.toml b/cases/translation/functions/concat-ws.toml new file mode 100644 index 000000000..afe905cf7 --- /dev/null +++ b/cases/translation/functions/concat-ws.toml @@ -0,0 +1,71 @@ +category = "translation" +construct = "sf.functions.concat-ws" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "A multi-character separator between three strings. Catches: the separator after the last argument, the separator taken as the first value, or arguments reordered" +columns = [{ name = "d", type = "VARCHAR" }, { name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'-+'", "'a'", "'bc'", "'\u00e9'"], +] +query = "SELECT CONCAT_WS(d, a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_single" +description = "One argument after the separator. Catches: a separator appended" +columns = [{ name = "d", type = "VARCHAR" }, { name = "a", type = "VARCHAR" }] +rows = [ + ["','", "'only'"], +] +query = "SELECT CONCAT_WS(d, a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "Binary separator and values. Catches: hex text concatenated as VARCHAR" +columns = [{ name = "d", type = "BINARY" }, { name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }] +rows = [ + ["'FF'", "'00'", "'01'"], +] +query = "SELECT CONCAT_WS(d, a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_separator" +description = "NULL separator. Catches: NULL read as an empty separator (ab instead of NULL)" +columns = [{ name = "d", type = "VARCHAR" }, { name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'", "'b'"], + ["'/'", "'a'", "'b'"], +] +query = "SELECT CONCAT_WS(d, a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_expression" +description = "NULL value between set ones. Catches: NULL skipped (Postgres CONCAT_WS gives a,c) instead of NULL" +columns = [{ name = "d", type = "VARCHAR" }, { name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["','", "'a'", "NULL", "'c'"], +] +query = "SELECT CONCAT_WS(d, a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty values and an empty separator. Catches: empty values dropped (a,c instead of a,,c)" +columns = [{ name = "d", type = "VARCHAR" }, { name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["','", "'a'", "''", "'c'"], + ["''", "'a'", "'b'", "'c'"], +] +query = "SELECT CONCAT_WS(d, a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/concat.concat-subscript-dot.toml b/cases/translation/functions/concat.concat-subscript-dot.toml new file mode 100644 index 000000000..6c67129e4 --- /dev/null +++ b/cases/translation/functions/concat.concat-subscript-dot.toml @@ -0,0 +1,50 @@ +category = "translation" +construct = "sf.functions.concat.concat-subscript-dot" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Three strings in order. Catches: arguments reordered or a separator inserted" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'\u00e9'", "'b'", "'cd'"], + ["'x '", "' y'", "'z'"], +] +query = "SELECT a || b || c AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "Three binary values. Catches: the hex texts concatenated as a VARCHAR" +columns = [{ name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }, { name = "c", type = "BINARY" }] +rows = [ + ["'00'", "'FF'", "'10'"], +] +query = "SELECT a || b || c AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL in the middle argument. Catches: NULL skipped or read as '' (Postgres CONCAT gives 'ac')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL", "'c'"], + ["'a'", "'b'", "'c'"], +] +query = "SELECT a || b || c AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "Empty strings between set ones. Catches: an empty string treated as NULL (NULL instead of 'ac')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'a'", "''", "'c'"], + ["''", "''", "''"], +] +query = "SELECT a || b || c AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/concat.concat.toml b/cases/translation/functions/concat.concat.toml new file mode 100644 index 000000000..77fd880ad --- /dev/null +++ b/cases/translation/functions/concat.concat.toml @@ -0,0 +1,61 @@ +category = "translation" +construct = "sf.functions.concat.concat" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Three strings in order. Catches: arguments reordered or a separator inserted" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'\u00e9'", "'b'", "'cd'"], + ["'x '", "' y'", "'z'"], +] +query = "SELECT CONCAT(a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "Three binary values. Catches: the hex texts concatenated as a VARCHAR" +columns = [{ name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }, { name = "c", type = "BINARY" }] +rows = [ + ["'00'", "'FF'", "'10'"], +] +query = "SELECT CONCAT(a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL in the middle argument. Catches: NULL skipped or read as '' (Postgres CONCAT gives 'ac')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL", "'c'"], + ["'a'", "'b'", "'c'"], +] +query = "SELECT CONCAT(a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "Empty strings between set ones. Catches: an empty string treated as NULL (NULL instead of 'ac')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'a'", "''", "'c'"], + ["''", "''", "''"], +] +query = "SELECT CONCAT(a, b, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t005_single" +description = "CONCAT of one argument. Catches: one argument rejected or NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'solo'", "''", "''"], +] +query = "SELECT CONCAT(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/contains.toml b/cases/translation/functions/contains.toml new file mode 100644 index 000000000..5baf3e80a --- /dev/null +++ b/cases/translation/functions/contains.toml @@ -0,0 +1,53 @@ +category = "translation" +construct = "sf.functions.contains" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "CONTAINS with the arguments in both orders and in another case. Catches: arguments swapped (TRUE for CONTAINS(nan, banana)) or a case-insensitive match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'banana'", "'nan'"], + ["'banana'", "'NAN'"], + ["'nan'", "'banana'"], + ["'caf\u00e9'", "'\u00e9'"], +] +query = "SELECT CONTAINS(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expr1" +description = "NULL first argument. Catches: NULL read as an empty string (FALSE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'"], + ["'a'", "'a'"], +] +query = "SELECT CONTAINS(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_expr2" +description = "NULL second argument. Catches: NULL read as an empty string (TRUE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "NULL"], + ["'abc'", "'c'"], +] +query = "SELECT CONTAINS(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "An empty search string, and an empty subject. Catches: FALSE for the empty search string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "''"], + ["''", "'a'"], +] +query = "SELECT CONTAINS(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/decompress-binary.toml b/cases/translation/functions/decompress-binary.toml new file mode 100644 index 000000000..9d5bf18ce --- /dev/null +++ b/cases/translation/functions/decompress-binary.toml @@ -0,0 +1,72 @@ +category = "translation" +construct = "sf.functions.decompress-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_snappy" +description = "DECOMPRESS_BINARY of fixed Snappy blocks, one from the page. Catches: another codec or a framing offset (the varint length read as data)" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'0920536E6F77666C616B65'"], + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_BINARY(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_method_level" +description = "A lower-case method name with a level, which the page says is ignored. Catches: the level or the lower-case name rejected" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_BINARY(a, 'snappy(3)') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decompressed as an empty value" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["NULL"], + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_BINARY(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_method" +description = "NULL method. Catches: NULL read as a default method" +columns = [{ name = "a", type = "BINARY" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'051048656C6C6F'", "NULL"], + ["'051048656C6C6F'", "'SNAPPY'"], +] +query = "SELECT DECOMPRESS_BINARY(a, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_corrupt" +description = "Bytes that are not a valid Snappy block. Catches: NULL or partial output instead of an error" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'FF0102'"], +] +query = "SELECT DECOMPRESS_BINARY(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t006_unknown_method" +description = "An unknown method. Catches: a fallback codec instead of an error" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_BINARY(a, 'LZ4') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/decompress-string.toml b/cases/translation/functions/decompress-string.toml new file mode 100644 index 000000000..c9a92e700 --- /dev/null +++ b/cases/translation/functions/decompress-string.toml @@ -0,0 +1,72 @@ +category = "translation" +construct = "sf.functions.decompress-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_snappy" +description = "DECOMPRESS_STRING of fixed Snappy blocks, one from the page. Catches: another codec or a framing offset (the varint length read as data)" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'0920536E6F77666C616B65'"], + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_STRING(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_method_level" +description = "A lower-case method name with a level, which the page says is ignored. Catches: the level or the lower-case name rejected" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_STRING(a, 'snappy(3)') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decompressed as an empty value" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["NULL"], + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_STRING(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_method" +description = "NULL method. Catches: NULL read as a default method" +columns = [{ name = "a", type = "BINARY" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'051048656C6C6F'", "NULL"], + ["'051048656C6C6F'", "'SNAPPY'"], +] +query = "SELECT DECOMPRESS_STRING(a, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_corrupt" +description = "Bytes that are not a valid Snappy block. Catches: NULL or partial output instead of an error" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'FF0102'"], +] +query = "SELECT DECOMPRESS_STRING(a, 'SNAPPY') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t006_unknown_method" +description = "An unknown method. Catches: a fallback codec instead of an error" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'051048656C6C6F'"], +] +query = "SELECT DECOMPRESS_STRING(a, 'LZ4') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/editdistance.toml b/cases/translation/functions/editdistance.toml new file mode 100644 index 000000000..467f8f305 --- /dev/null +++ b/cases/translation/functions/editdistance.toml @@ -0,0 +1,88 @@ +category = "translation" +construct = "sf.functions.editdistance" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Levenshtein distances that differ from Hamming and LCS distances. Catches: Hamming or LCS distance, a byte-based distance (2 for caf\u00e9/cafe), or transpositions counted as 1" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'kitten'", "'sitting'"], + ["'flaw'", "'lawn'"], + ["'caf\u00e9'", "'cafe'"], + ["'ab'", "'ba'"], +] +query = "SELECT EDITDISTANCE(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_max_distance" +description = "A maximum below and above the distance. Catches: the maximum ignored (3 instead of 2) or applied when above the distance" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "m", type = "NUMBER(38,0)" }] +rows = [ + ["'kitten'", "'sitting'", "2"], + ["'kitten'", "'sitting'", "9"], +] +query = "SELECT EDITDISTANCE(a, b, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string_expr1" +description = "NULL first string. Catches: NULL read as an empty string (the other length)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'abc'"], + ["'ab'", "'abc'"], +] +query = "SELECT EDITDISTANCE(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_string_expr2" +description = "NULL second string. Catches: NULL read as an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "NULL"], + ["'abc'", "'abd'"], +] +query = "SELECT EDITDISTANCE(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_max_distance" +description = "NULL maximum. Catches: NULL read as no maximum (the full distance instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "m", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "'xyz'", "NULL"], + ["'abc'", "'xyz'", "1"], +] +query = "SELECT EDITDISTANCE(a, b, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_negative_max" +description = "A negative maximum, which the page says gives 0. Catches: the negative maximum returned or ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "m", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "'xyz'", "-2"], +] +query = "SELECT EDITDISTANCE(a, b, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t007_empty" +description = "An empty string against a set one. Catches: NULL or 0 instead of the other length" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["''", "'abcd'"], + ["''", "''"], +] +query = "SELECT EDITDISTANCE(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/endswith.toml b/cases/translation/functions/endswith.toml new file mode 100644 index 000000000..518694c6d --- /dev/null +++ b/cases/translation/functions/endswith.toml @@ -0,0 +1,53 @@ +category = "translation" +construct = "sf.functions.endswith" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "ENDSWITH with the arguments in both orders and in another case. Catches: arguments swapped (the page's own Returns text reads them reversed) or a case-insensitive match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'banana'", "'ana'"], + ["'ana'", "'banana'"], + ["'banana'", "'ANA'"], + ["'a\u00e9'", "'\u00e9'"], +] +query = "SELECT ENDSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expr1" +description = "NULL first argument. Catches: NULL read as an empty string (FALSE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'"], + ["'a'", "'a'"], +] +query = "SELECT ENDSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_expr2" +description = "NULL second argument. Catches: NULL read as an empty string (TRUE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "NULL"], + ["'abc'", "'c'"], +] +query = "SELECT ENDSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "An empty search string, and an empty subject. Catches: FALSE for the empty search string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "''"], + ["''", "'a'"], +] +query = "SELECT ENDSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/hash-agg.hash-agg-over.syntax.toml b/cases/translation/functions/hash-agg.hash-agg-over.syntax.toml new file mode 100644 index 000000000..a7b5015c4 --- /dev/null +++ b/cases/translation/functions/hash-agg.hash-agg-over.syntax.toml @@ -0,0 +1,29 @@ +category = "translation" +construct = "sf.functions.hash-agg.hash-agg-over.syntax" +session_profile = "utc-v1" + +[[case]] +id = "t001_star" +description = "HASH_AGG(*) per partition. Catches: the star expanded to a different column set or order (the ordinal column included or dropped)" +columns = [{ name = "g", type = "VARCHAR" }, { name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["'p'", "1"], + ["'q'", "1"], + ["'p'", "2"], +] +query = "SELECT g, HASH_AGG(*) OVER (PARTITION BY g) AS r FROM (SELECT g, a FROM input) ORDER BY g, r" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expr3" +description = "NULL partition key. Catches: NULL-keyed rows dropped or merged with another partition" +columns = [{ name = "g", type = "VARCHAR" }, { name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], + ["'p'", "1"], + ["NULL", "2"], +] +query = "SELECT g, HASH_AGG(*) OVER (PARTITION BY g) AS r FROM (SELECT g, a FROM input) ORDER BY g NULLS LAST, r" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/hash-agg.hash-agg-over.toml b/cases/translation/functions/hash-agg.hash-agg-over.toml new file mode 100644 index 000000000..de42af0b7 --- /dev/null +++ b/cases/translation/functions/hash-agg.hash-agg-over.toml @@ -0,0 +1,43 @@ +category = "translation" +construct = "sf.functions.hash-agg.hash-agg-over" +session_profile = "utc-v1" + +[[case]] +id = "t001_partition" +description = "HASH_AGG per partition with partitions of equal multisets. Catches: the whole input hashed instead of the partition, or partitions with the same rows hashed differently" +columns = [{ name = "g", type = "VARCHAR" }, { name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["'p'", "1"], + ["'q'", "2"], + ["'p'", "2"], + ["'q'", "1"], + ["'r'", "1"], +] +query = "SELECT g, HASH_AGG(a) OVER (PARTITION BY g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_no_partition" +description = "An empty OVER clause. Catches: a running (ordered) hash instead of the whole-input value" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["5"], + ["6"], +] +query = "SELECT HASH_AGG(a) OVER () AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL values inside a partition. Catches: NULLs skipped within the window" +columns = [{ name = "g", type = "VARCHAR" }, { name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["'p'", "NULL"], + ["'p'", "1"], + ["'q'", "1"], +] +query = "SELECT g, HASH_AGG(a) OVER (PARTITION BY g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/hash-agg.hash-agg.toml b/cases/translation/functions/hash-agg.hash-agg.toml new file mode 100644 index 000000000..af84868bb --- /dev/null +++ b/cases/translation/functions/hash-agg.hash-agg.toml @@ -0,0 +1,67 @@ +category = "translation" +construct = "sf.functions.hash-agg.hash-agg" +session_profile = "utc-v1" + +[[case]] +id = "t001_number" +description = "HASH_AGG over varied rows with a duplicate. Catches: another per-row hash or combining function" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["3"], + ["-7"], + ["3"], + ["100"], +] +query = "SELECT HASH_AGG(a) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_distinct" +description = "HASH_AGG(DISTINCT) against the plain aggregate on rows with duplicates. Catches: DISTINCT ignored (the two values equal)" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["3"], + ["3"], + ["5"], +] +query = "SELECT HASH_AGG(DISTINCT a) AS d, HASH_AGG(a) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_row_order" +description = "Two columns holding the same values in different row orders, and both column orders. Catches: a row-order-sensitive fold (r1 FALSE) or a column-order-insensitive one (r2 TRUE)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(38,0)" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["1", "3", "'x'"], + ["2", "1", "'y'"], + ["3", "2", "'z'"], +] +query = "SELECT HASH_AGG(a) = HASH_AGG(b) AS r1, HASH_AGG(a, c) = HASH_AGG(c, a) AS r2, HASH_AGG(a, c) AS h FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_expr" +description = "NULL rows, which the page says are not ignored. Catches: NULL rows skipped (equal to the aggregate without them) or a NULL result" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "k", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "1"], + ["4", "1"], + ["NULL", "2"], +] +query = "SELECT HASH_AGG(a) AS r, HASH_AGG(a) = HASH_AGG(CASE WHEN a IS NOT NULL THEN a END) AS same FROM input" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_no_rows" +description = "HASH_AGG over no rows. Catches: NULL for an empty input (the page: never NULL)" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["1"], +] +query = "SELECT HASH_AGG(a) AS r FROM input WHERE a > 5" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/hash.toml b/cases/translation/functions/hash.toml new file mode 100644 index 000000000..ba066ef57 --- /dev/null +++ b/cases/translation/functions/hash.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.hash" +session_profile = "utc-v1" + +[[case]] +id = "t001_number" +description = "HASH of integers, compared with Snowflake's own values. Catches: another hash function or seed" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["10"], + ["-1"], + ["123456789012345"], +] +query = "SELECT HASH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_varchar" +description = "HASH of strings, including multi-byte. Catches: another hash or a Latin-1 encoding" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'10'"], +] +query = "SELECT HASH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_scale_stable" +description = "HASH of 10 as NUMBER(38,0), NUMBER(10,2) and FLOAT, which the page says agree. Catches: the scale or the FLOAT bits hashed (FALSE)" +columns = [{ name = "a", type = "NUMBER(38,0)" }, { name = "b", type = "NUMBER(10,2)" }, { name = "c", type = "FLOAT" }] +rows = [ + ["10", "10.00", "10e0"], +] +query = "SELECT HASH(a) = HASH(b) AS r1, HASH(a) = HASH(c) AS r2, HASH(a) AS h FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_multi" +description = "HASH of two arguments in both orders. Catches: arguments combined order-independently" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "NUMBER(38,0)" }] +rows = [ + ["'x'", "1"], +] +query = "SELECT HASH(a, b) AS r1, HASH(b, a) AS r2 FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_expr" +description = "NULL input, which the page says still hashes. Catches: NULL propagated instead of a hash value" +columns = [{ name = "a", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["0"], +] +query = "SELECT HASH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/hex-decode-binary.toml b/cases/translation/functions/hex-decode-binary.toml new file mode 100644 index 000000000..34c26a3fa --- /dev/null +++ b/cases/translation/functions/hex-decode-binary.toml @@ -0,0 +1,62 @@ +category = "translation" +construct = "sf.functions.hex-decode-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "HEX_DECODE_BINARY of upper- and lower-case hex digits. Catches: lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'00FF10'"], + ["'c3a9'"], + ["'7F80'"], +] +query = "SELECT HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'41'"], +] +query = "SELECT HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'42'"], +] +query = "SELECT HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t004_invalid" +description = "A non-hex digit. Catches: NULL (the TRY_ behaviour) or a partial decode where Snowflake raises an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'4G'"], +] +query = "SELECT HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t005_odd_length" +description = "An odd number of hex digits. Catches: the last nibble dropped or zero-padded instead of an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'414'"], +] +query = "SELECT HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/hex-decode-string.toml b/cases/translation/functions/hex-decode-string.toml new file mode 100644 index 000000000..3df32d2eb --- /dev/null +++ b/cases/translation/functions/hex-decode-string.toml @@ -0,0 +1,61 @@ +category = "translation" +construct = "sf.functions.hex-decode-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "HEX_DECODE_STRING of upper- and lower-case hex digits. Catches: lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'48C3A96C6C6F'"], + ["'E282AC'"], +] +query = "SELECT HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'41'"], +] +query = "SELECT HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'42'"], +] +query = "SELECT HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t004_invalid" +description = "A non-hex digit. Catches: NULL (the TRY_ behaviour) or a partial decode where Snowflake raises an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'4G'"], +] +query = "SELECT HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t005_odd_length" +description = "An odd number of hex digits. Catches: the last nibble dropped or zero-padded instead of an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'414'"], +] +query = "SELECT HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/hex-encode.toml b/cases/translation/functions/hex-encode.toml new file mode 100644 index 000000000..4849c965d --- /dev/null +++ b/cases/translation/functions/hex-encode.toml @@ -0,0 +1,84 @@ +category = "translation" +construct = "sf.functions.hex-encode" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "HEX_ENCODE of multi-byte text. Catches: Latin-1 bytes (E9 for \u00e9) or lower-case digits by default" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'H\u00e9\u20ac'"], + ["'z'"], +] +query = "SELECT HEX_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "HEX_ENCODE of bytes including 00 and FF. Catches: the leading zero byte dropped or sign-extended" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'00FF10'"], +] +query = "SELECT HEX_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_case" +description = "The case argument 0 and 1. Catches: the case argument ignored or inverted" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "NUMBER(38,0)" }] +rows = [ + ["'\u00ff'", "0"], + ["'\u00ff'", "1"], +] +query = "SELECT HEX_ENCODE(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_input" +description = "NULL input next to a set one. Catches: NULL encoded as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT HEX_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_case" +description = "NULL case next to a set one. Catches: NULL read as the default (upper case) instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "NUMBER(38,0)" }] +rows = [ + ["'\u00ff'", "NULL"], + ["'\u00ff'", "0"], +] +query = "SELECT HEX_ENCODE(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "The empty string. Catches: NULL for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT HEX_ENCODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_bad_case" +description = "A case value other than 0 or 1. Catches: any non-zero value read as upper case instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "2"], +] +query = "SELECT HEX_ENCODE(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/ilike-any.toml b/cases/translation/functions/ilike-any.toml new file mode 100644 index 000000000..41325356b --- /dev/null +++ b/cases/translation/functions/ilike-any.toml @@ -0,0 +1,78 @@ +category = "translation" +construct = "sf.functions.ilike-any" +session_profile = "utc-v1" + +[[case]] +id = "t001_patterns" +description = "ILIKE ANY where one, both or neither pattern matches. Catches: ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "'%z'"], + ["'abz'", "'a%'", "'%z'"], + ["'xbc'", "'a%'", "'%z'"], + ["'b'", "'a%'", "'%b%'"], +] +query = "SELECT a ILIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Patterns in another case. Catches: case sensitivity reversed (FALSE for ABC)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a%'", "'%c'"], + ["'\u00c9a'", "'\u00e9%'", "'%A'"], +] +query = "SELECT a ILIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_escape" +description = "An escape character before %. Catches: the escape ignored (50 matches 5!%)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'", "'x'"], + ["'50'", "'5!%'", "'x'"], +] +query = "SELECT a ILIKE ANY (p, r) ESCAPE '!' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'", "'x'"], + ["''", "'%'", "'x'"], +] +query = "SELECT a ILIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "One NULL pattern beside a matching and a non-matching one. Catches: a NULL pattern making the result NULL even when the other pattern matches, or NULL matching NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "NULL"], + ["'xyz'", "'a%'", "NULL"], + ["NULL", "NULL", "'a'"], +] +query = "SELECT a ILIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "An empty subject against an empty and a % pattern. Catches: the empty pattern matching a non-empty subject" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["''", "''", "'%'"], + ["'a'", "''", "''"], +] +query = "SELECT a ILIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/ilike.ilike.syntax.toml b/cases/translation/functions/ilike.ilike.syntax.toml new file mode 100644 index 000000000..198a8f4f6 --- /dev/null +++ b/cases/translation/functions/ilike.ilike.syntax.toml @@ -0,0 +1,120 @@ +category = "translation" +construct = "sf.functions.ilike.ilike.syntax" +session_profile = "utc-v1" + +[[case]] +id = "t001_wildcards" +description = "The _ and % wildcards, anchored at both ends. Catches: an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a_c'"], + ["'abc'", "'b'"], + ["'ac'", "'a_c'"], + ["'xabcx'", "'%b%'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'a.c'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Letters in another case, including non-ASCII. Catches: case-sensitive matching (FALSE for ABC against abc), or ASCII-only folding (\u00c9 against \u00e9)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'\u00c9t\u00e9'", "'\u00e9t\u00e9'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_newline" +description = "A newline matched by _ and %, which the page documents. Catches: wildcards stopping at a newline" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a_b'"], + ["'a' || CHR(10) || 'b'", "'a%'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_no_default_escape" +description = "A backslash in the pattern with no ESCAPE clause. Catches: backslash used as a default escape (a\\b would then match only ab)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a\\\\b'", "'a\\\\b'"], + ["'ab'", "'a\\\\b'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'"], + ["''", "'%'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern, and NULL against NULL. Catches: NULL matching NULL (TRUE) or NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["NULL", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_empty" +description = "Empty subject and pattern. Catches: the empty pattern matching every string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], + ["''", "'%'"], +] +query = "SELECT a ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t008_escape" +description = "ESCAPE before % and _. Catches: the escape ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'"], + ["'50'", "'5!%'"], + ["'a_b'", "'a!_b'"], + ["'axb'", "'a!_b'"], +] +query = "SELECT a ILIKE p ESCAPE '!' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_not" +description = "NOT ILIKE with matching, non-matching and NULL rows. Catches: NOT applied as case-sensitive or dropped" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a%'"], + ["'xbc'", "'a%'"], + ["NULL", "'a%'"], +] +query = "SELECT a NOT ILIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/ilike.ilike.toml b/cases/translation/functions/ilike.ilike.toml new file mode 100644 index 000000000..9450b455a --- /dev/null +++ b/cases/translation/functions/ilike.ilike.toml @@ -0,0 +1,118 @@ +category = "translation" +construct = "sf.functions.ilike.ilike" +session_profile = "utc-v1" + +[[case]] +id = "t001_wildcards" +description = "The _ and % wildcards, anchored at both ends. Catches: an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a_c'"], + ["'abc'", "'b'"], + ["'ac'", "'a_c'"], + ["'xabcx'", "'%b%'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'a.c'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Letters in another case, including non-ASCII. Catches: case-sensitive matching (FALSE for ABC against abc), or ASCII-only folding (\u00c9 against \u00e9)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'\u00c9t\u00e9'", "'\u00e9t\u00e9'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_newline" +description = "A newline matched by _ and %, which the page documents. Catches: wildcards stopping at a newline" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a_b'"], + ["'a' || CHR(10) || 'b'", "'a%'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_no_default_escape" +description = "A backslash in the pattern with no ESCAPE clause. Catches: backslash used as a default escape (a\\b would then match only ab)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a\\\\b'", "'a\\\\b'"], + ["'ab'", "'a\\\\b'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'"], + ["''", "'%'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern, and NULL against NULL. Catches: NULL matching NULL (TRUE) or NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["NULL", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_empty" +description = "Empty subject and pattern. Catches: the empty pattern matching every string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], + ["''", "'%'"], +] +query = "SELECT ILIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t008_escape" +description = "An escape character before % and _. Catches: the escape ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'"], + ["'50'", "'5!%'"], + ["'a_b'", "'a!_b'"], + ["'axb'", "'a!_b'"], +] +query = "SELECT ILIKE(a, p, '!') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_null_escape" +description = "NULL escape. Catches: NULL read as no escape" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'A'"], +] +query = "SELECT ILIKE(a, p, NULL) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/initcap.toml b/cases/translation/functions/initcap.toml new file mode 100644 index 000000000..dac1cc499 --- /dev/null +++ b/cases/translation/functions/initcap.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.initcap" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "INITCAP with mixed case and default delimiters. Catches: the rest of each word not lower-cased, or a delimiter set without _ @ - (Ab_cd instead of Ab_Cd)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'hello wORLD-foo'"], + ["'ab_cd@ef.gh'"], + ["'o''neil 3rd \u00e9lan'"], +] +query = "SELECT INITCAP(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_delimiters" +description = "Given delimiters that replace the defaults. Catches: given delimiters added to the defaults instead of replacing them" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["'ab-cd ef'", "' '"], + ["'ab-cd ef'", "'-'"], +] +query = "SELECT INITCAP(a, d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT INITCAP(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_delimiters" +description = "NULL delimiters. Catches: NULL read as the defaults" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' '"], +] +query = "SELECT INITCAP('ab cd', d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty_delimiters" +description = "An empty delimiter string, which makes one word. Catches: the empty string read as the defaults" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'hELLO wORLD'"], +] +query = "SELECT INITCAP(a, '') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/insert.toml b/cases/translation/functions/insert.toml new file mode 100644 index 000000000..be7b1e9e2 --- /dev/null +++ b/cases/translation/functions/insert.toml @@ -0,0 +1,96 @@ +category = "translation" +construct = "sf.functions.insert" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Replace characters after a multi-byte character. Catches: a 0-based position, len ignored (pure insert), or byte offsets" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }, { name = "i", type = "VARCHAR" }] +rows = [ + ["'abcdef'", "2", "3", "'XY'"], + ["'\u00e9abc'", "2", "1", "'Z'"], +] +query = "SELECT INSERT(a, p, n, i) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_append" +description = "An append at length+1 with len 0. Catches: an error, or insertion before the last character" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], +] +query = "SELECT INSERT(a, 4, 0, 'Z') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "INSERT on binary. Catches: hex text positions instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "i", type = "BINARY" }] +rows = [ + ["'000102'", "'FF'"], +] +query = "SELECT INSERT(a, 2, 1, i) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_base_expr" +description = "NULL base. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT INSERT(a, 1, 0, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pos" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT INSERT('ab', p, 0, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_len" +description = "NULL length. Catches: NULL read as 0" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT INSERT('ab', 1, n, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_insert_expr" +description = "NULL insert string. Catches: NULL read as '' (a delete)" +columns = [{ name = "i", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["''"], +] +query = "SELECT INSERT('ab', 1, 1, i) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_delete" +description = "An empty insert string, which deletes. Catches: nothing deleted" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abcdef'"], +] +query = "SELECT INSERT(a, 2, 3, '') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/jarowinkler-similarity.toml b/cases/translation/functions/jarowinkler-similarity.toml new file mode 100644 index 000000000..2a22f0095 --- /dev/null +++ b/cases/translation/functions/jarowinkler-similarity.toml @@ -0,0 +1,53 @@ +category = "translation" +construct = "sf.functions.jarowinkler-similarity" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "The page's example pairs. Catches: plain Jaro without the prefix bonus, a 0-1 FLOAT instead of 0-100, or a byte-based comparison (\u015bwi\u0119ta)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'Snowflake'", "'Oracle'"], + ["'\u015bwi\u0119ta'", "'swieta'"], + ["'Gute nacht'", "'Ich weis nicht'"], + ["'MARTHA'", "'MARHTA'"], +] +query = "SELECT JAROWINKLER_SIMILARITY(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_string_expr1" +description = "NULL first string. Catches: NULL read as empty (0)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'"], + ["'a'", "'a'"], +] +query = "SELECT JAROWINKLER_SIMILARITY(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_string_expr2" +description = "NULL second string. Catches: NULL read as empty (0)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL"], + ["'ab'", "'a'"], +] +query = "SELECT JAROWINKLER_SIMILARITY(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "Two empty strings, which the example shows as 0. Catches: 100 for two identical empty strings" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["''", "'a'"], +] +query = "SELECT JAROWINKLER_SIMILARITY(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/left.toml b/cases/translation/functions/left.toml new file mode 100644 index 000000000..7b4b89a63 --- /dev/null +++ b/cases/translation/functions/left.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.left" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "LEFT of multi-byte text. Catches: a byte count (cutting \u00e9 in half) or the other end of the string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'h\u00e9llo'", "2"], + ["'abcdef'", "3"], + ["'ab'", "9"], +] +query = "SELECT LEFT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "LEFT of binary. Catches: hex characters counted instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'00FF10'", "2"], +] +query = "SELECT LEFT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "2"], + ["'ab'", "1"], +] +query = "SELECT LEFT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_length_expr" +description = "NULL length. Catches: NULL read as 0 (an empty string instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "NULL"], + ["'ab'", "0"], +] +query = "SELECT LEFT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_negative" +description = "A negative length, which the page says gives an empty string. Catches: an error, or Postgres semantics (all but the last characters)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "-2"], + ["'abcdef'", "0"], +] +query = "SELECT LEFT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/length.len.toml b/cases/translation/functions/length.len.toml new file mode 100644 index 000000000..bf4457fbd --- /dev/null +++ b/cases/translation/functions/length.len.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.length.len" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "LEN of multi-byte text and trailing blanks. Catches: a byte count (6 for h\u00e9llo) or trailing blanks trimmed" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'h\u00e9llo'"], + ["'ab '"], + ["'\u20ac'"], +] +query = "SELECT LEN(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "LEN of binary values. Catches: the hex text length (6 instead of 3)" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'00FF10'"], + ["'41'"], +] +query = "SELECT LEN(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expression" +description = "NULL next to a set value. Catches: NULL read as an empty string (0)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT LEN(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["' '"], +] +query = "SELECT LEN(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/length.length.toml b/cases/translation/functions/length.length.toml new file mode 100644 index 000000000..d6e6b08e7 --- /dev/null +++ b/cases/translation/functions/length.length.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.length.length" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "LENGTH of multi-byte text and trailing blanks. Catches: a byte count (6 for h\u00e9llo) or trailing blanks trimmed" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'h\u00e9llo'"], + ["'ab '"], + ["'\u20ac'"], +] +query = "SELECT LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "LENGTH of binary values. Catches: the hex text length (6 instead of 3)" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'00FF10'"], + ["'41'"], +] +query = "SELECT LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expression" +description = "NULL next to a set value. Catches: NULL read as an empty string (0)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["' '"], +] +query = "SELECT LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/like-all.toml b/cases/translation/functions/like-all.toml new file mode 100644 index 000000000..f79a9a6ee --- /dev/null +++ b/cases/translation/functions/like-all.toml @@ -0,0 +1,78 @@ +category = "translation" +construct = "sf.functions.like-all" +session_profile = "utc-v1" + +[[case]] +id = "t001_patterns" +description = "LIKE ALL where one, both or neither pattern matches. Catches: ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "'%z'"], + ["'abz'", "'a%'", "'%z'"], + ["'xbc'", "'a%'", "'%z'"], + ["'b'", "'a%'", "'%b%'"], +] +query = "SELECT a LIKE ALL (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Patterns in another case. Catches: case sensitivity reversed (TRUE for ABC against a%)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a%'", "'%c'"], + ["'\u00c9a'", "'\u00e9%'", "'%A'"], +] +query = "SELECT a LIKE ALL (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_escape" +description = "An escape character before %. Catches: the escape ignored (50 matches 5!%)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'", "'x'"], + ["'50'", "'5!%'", "'x'"], +] +query = "SELECT a LIKE ALL (p, r) ESCAPE '!' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'", "'x'"], + ["''", "'%'", "'x'"], +] +query = "SELECT a LIKE ALL (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "One NULL pattern beside a matching and a non-matching one. Catches: NULL patterns ignored (TRUE) where three-valued ALL gives NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "NULL"], + ["'xyz'", "'a%'", "NULL"], + ["NULL", "NULL", "'a'"], +] +query = "SELECT a LIKE ALL (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "An empty subject against an empty and a % pattern. Catches: the empty pattern matching a non-empty subject" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["''", "''", "'%'"], + ["'a'", "''", "''"], +] +query = "SELECT a LIKE ALL (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/like-any.toml b/cases/translation/functions/like-any.toml new file mode 100644 index 000000000..2e7ae26b8 --- /dev/null +++ b/cases/translation/functions/like-any.toml @@ -0,0 +1,78 @@ +category = "translation" +construct = "sf.functions.like-any" +session_profile = "utc-v1" + +[[case]] +id = "t001_patterns" +description = "LIKE ANY where one, both or neither pattern matches. Catches: ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "'%z'"], + ["'abz'", "'a%'", "'%z'"], + ["'xbc'", "'a%'", "'%z'"], + ["'b'", "'a%'", "'%b%'"], +] +query = "SELECT a LIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Patterns in another case. Catches: case sensitivity reversed (TRUE for ABC against a%)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'a%'", "'%c'"], + ["'\u00c9a'", "'\u00e9%'", "'%A'"], +] +query = "SELECT a LIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_escape" +description = "An escape character before %. Catches: the escape ignored (50 matches 5!%)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'", "'x'"], + ["'50'", "'5!%'", "'x'"], +] +query = "SELECT a LIKE ANY (p, r) ESCAPE '!' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'", "'x'"], + ["''", "'%'", "'x'"], +] +query = "SELECT a LIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "One NULL pattern beside a matching and a non-matching one. Catches: a NULL pattern making the result NULL even when the other pattern matches, or NULL matching NULL" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'", "NULL"], + ["'xyz'", "'a%'", "NULL"], + ["NULL", "NULL", "'a'"], +] +query = "SELECT a LIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "An empty subject against an empty and a % pattern. Catches: the empty pattern matching a non-empty subject" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["''", "''", "'%'"], + ["'a'", "''", "''"], +] +query = "SELECT a LIKE ANY (p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/like.like.syntax.toml b/cases/translation/functions/like.like.syntax.toml new file mode 100644 index 000000000..cbadd0050 --- /dev/null +++ b/cases/translation/functions/like.like.syntax.toml @@ -0,0 +1,120 @@ +category = "translation" +construct = "sf.functions.like.like.syntax" +session_profile = "utc-v1" + +[[case]] +id = "t001_wildcards" +description = "The _ and % wildcards, anchored at both ends. Catches: an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a_c'"], + ["'abc'", "'b'"], + ["'ac'", "'a_c'"], + ["'xabcx'", "'%b%'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'a.c'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Letters in another case, including non-ASCII. Catches: case-insensitive matching for LIKE or case-sensitive for ILIKE" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'\u00c9t\u00e9'", "'\u00e9t\u00e9'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_newline" +description = "A newline matched by _ and %, which the page documents. Catches: wildcards stopping at a newline" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a_b'"], + ["'a' || CHR(10) || 'b'", "'a%'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_no_default_escape" +description = "A backslash in the pattern with no ESCAPE clause. Catches: backslash used as a default escape (a\\b would then match only ab)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a\\\\b'", "'a\\\\b'"], + ["'ab'", "'a\\\\b'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'"], + ["''", "'%'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern, and NULL against NULL. Catches: NULL matching NULL (TRUE) or NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["NULL", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_empty" +description = "Empty subject and pattern. Catches: the empty pattern matching every string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], + ["''", "'%'"], +] +query = "SELECT a LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t008_escape" +description = "ESCAPE before % and _. Catches: the escape ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'"], + ["'50'", "'5!%'"], + ["'a_b'", "'a!_b'"], + ["'axb'", "'a!_b'"], +] +query = "SELECT a LIKE p ESCAPE '!' AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_not" +description = "NOT LIKE with matching, non-matching and NULL rows. Catches: NOT applied after NULL handling (TRUE for a NULL subject) or dropped" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a%'"], + ["'xbc'", "'a%'"], + ["NULL", "'a%'"], +] +query = "SELECT a NOT LIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/like.like.toml b/cases/translation/functions/like.like.toml new file mode 100644 index 000000000..0dd86377c --- /dev/null +++ b/cases/translation/functions/like.like.toml @@ -0,0 +1,118 @@ +category = "translation" +construct = "sf.functions.like.like" +session_profile = "utc-v1" + +[[case]] +id = "t001_wildcards" +description = "The _ and % wildcards, anchored at both ends. Catches: an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a_c'"], + ["'abc'", "'b'"], + ["'ac'", "'a_c'"], + ["'xabcx'", "'%b%'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'a.c'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Letters in another case, including non-ASCII. Catches: case-insensitive matching for LIKE or case-sensitive for ILIKE" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'\u00c9t\u00e9'", "'\u00e9t\u00e9'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_newline" +description = "A newline matched by _ and %, which the page documents. Catches: wildcards stopping at a newline" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a_b'"], + ["'a' || CHR(10) || 'b'", "'a%'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_no_default_escape" +description = "A backslash in the pattern with no ESCAPE clause. Catches: backslash used as a default escape (a\\b would then match only ab)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a\\\\b'", "'a\\\\b'"], + ["'ab'", "'a\\\\b'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against %)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'%'"], + ["''", "'%'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern, and NULL against NULL. Catches: NULL matching NULL (TRUE) or NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["NULL", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_empty" +description = "Empty subject and pattern. Catches: the empty pattern matching every string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], + ["''", "'%'"], +] +query = "SELECT LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t008_escape" +description = "An escape character before % and _. Catches: the escape ignored (5! % read as a wildcard) or the escape kept as a literal" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'5%'", "'5!%'"], + ["'50'", "'5!%'"], + ["'a_b'", "'a!_b'"], + ["'axb'", "'a!_b'"], +] +query = "SELECT LIKE(a, p, '!') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_null_escape" +description = "NULL escape. Catches: NULL read as no escape (a value instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'a'"], +] +query = "SELECT LIKE(a, p, NULL) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/lower.toml b/cases/translation/functions/lower.toml new file mode 100644 index 000000000..9ad8305cb --- /dev/null +++ b/cases/translation/functions/lower.toml @@ -0,0 +1,38 @@ +category = "translation" +construct = "sf.functions.lower" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "LOWER of non-ASCII capitals. Catches: ASCII-only lowering (\u00c0 \u00c9 \u03a9 kept)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'\u00c0\u00c9\u00ce \u03a9MEGA Stra\u00dfe'"], + ["'ABC123'"], +] +query = "SELECT LOWER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'A'"], +] +query = "SELECT LOWER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT LOWER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/lpad.toml b/cases/translation/functions/lpad.toml new file mode 100644 index 000000000..adcd4952e --- /dev/null +++ b/cases/translation/functions/lpad.toml @@ -0,0 +1,107 @@ +category = "translation" +construct = "sf.functions.lpad" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "LPAD with a multi-character pad that must be cut. Catches: the pad cut from the wrong end, or the length counted in bytes (\u00e9)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "6", "'xy'"], + ["'\u00e9'", "4", "'ab'"], +] +query = "SELECT LPAD(a, n, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_truncate" +description = "A base longer than the length. Catches: the base kept whole, or truncated from the right end for LPAD" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "3"], +] +query = "SELECT LPAD(a, n, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_default_pad" +description = "No pad argument. Catches: no padding, or a pad other than a blank" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "4"], +] +query = "SELECT LPAD(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_binary" +description = "LPAD of binary. Catches: hex text padded instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "n", type = "NUMBER(38,0)" }, { name = "p", type = "BINARY" }] +rows = [ + ["'01'", "3", "'FF00'"], +] +query = "SELECT LPAD(a, n, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_base" +description = "NULL base. Catches: NULL read as empty (pad only)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT LPAD(a, 3, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_length_expr" +description = "NULL length. Catches: NULL read as 0" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["3"], +] +query = "SELECT LPAD('a', n, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_pad" +description = "NULL pad. Catches: NULL read as the default blank" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'*'"], +] +query = "SELECT LPAD('a', 3, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_empty_pad" +description = "An empty pad, which adds nothing but still truncates. Catches: an infinite loop or error, or no truncation" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "5"], + ["'abcdef'", "2"], +] +query = "SELECT LPAD(a, n, '') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t009_negative_length" +description = "A negative length: records what Snowflake returns. Catches: an error or the untouched base where Snowflake returns its value" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], +] +query = "SELECT LPAD(a, -1, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/ltrim.toml b/cases/translation/functions/ltrim.toml new file mode 100644 index 000000000..2638c3879 --- /dev/null +++ b/cases/translation/functions/ltrim.toml @@ -0,0 +1,64 @@ +category = "translation" +construct = "sf.functions.ltrim" +session_profile = "utc-v1" + +[[case]] +id = "t001_characters" +description = "LTRIM with a character set given out of order. Catches: the set read as a substring, or characters removed from the right as well" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'xyyxabcxy'", "'yx'"], + ["'$.$a.b$.'", "'.$'"], + ["'\u00e9a\u00e9'", "'\u00e9'"], +] +query = "SELECT LTRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default" +description = "LTRIM with the default blank, beside tabs and newlines. Catches: all whitespace removed (the tab and newline kept by Snowflake)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' ab '"], + ["CHR(9) || ' ab ' || CHR(10)"], +] +query = "SELECT LTRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' a '"], +] +query = "SELECT LTRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_characters" +description = "NULL character set. Catches: NULL read as the default blank" +columns = [{ name = "c", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT LTRIM(' xax ', c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "An empty character set, and a string made only of trimmed characters. Catches: an empty set read as the default blank, or NULL for a fully trimmed string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["' a '", "''"], + ["'xxx'", "'x'"], +] +query = "SELECT LTRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/md5-binary.toml b/cases/translation/functions/md5-binary.toml new file mode 100644 index 000000000..51baa3bb5 --- /dev/null +++ b/cases/translation/functions/md5-binary.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.md5-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "MD5_BINARY of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT MD5_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT MD5_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT MD5_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/md5-number-lower64.toml b/cases/translation/functions/md5-number-lower64.toml new file mode 100644 index 000000000..0ed2d67ae --- /dev/null +++ b/cases/translation/functions/md5-number-lower64.toml @@ -0,0 +1,41 @@ +category = "translation" +construct = "sf.functions.md5-number-lower64" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "MD5_NUMBER_LOWER64 of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT MD5_NUMBER_LOWER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT MD5_NUMBER_LOWER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_high_bit" +description = "Messages whose 64-bit half has the top bit set (the empty string and 'Snowflake'). Catches: a signed 64-bit result (negative) or the other half of the digest" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'Snowflake'"], + ["'x'"], +] +query = "SELECT MD5_NUMBER_LOWER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/md5-number-upper64.toml b/cases/translation/functions/md5-number-upper64.toml new file mode 100644 index 000000000..eef151292 --- /dev/null +++ b/cases/translation/functions/md5-number-upper64.toml @@ -0,0 +1,41 @@ +category = "translation" +construct = "sf.functions.md5-number-upper64" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "MD5_NUMBER_UPPER64 of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT MD5_NUMBER_UPPER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT MD5_NUMBER_UPPER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_high_bit" +description = "Messages whose 64-bit half has the top bit set (the empty string and 'Snowflake'). Catches: a signed 64-bit result (negative) or the other half of the digest" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'Snowflake'"], + ["'x'"], +] +query = "SELECT MD5_NUMBER_UPPER64(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/md5.md5-hex.toml b/cases/translation/functions/md5.md5-hex.toml new file mode 100644 index 000000000..b6b9bdbd5 --- /dev/null +++ b/cases/translation/functions/md5.md5-hex.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.md5.md5-hex" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "MD5_HEX of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT MD5_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT MD5_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT MD5_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/md5.md5.toml b/cases/translation/functions/md5.md5.toml new file mode 100644 index 000000000..0041ef207 --- /dev/null +++ b/cases/translation/functions/md5.md5.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.md5.md5" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "MD5 of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT MD5(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT MD5(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT MD5(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/octet-length.toml b/cases/translation/functions/octet-length.toml new file mode 100644 index 000000000..cac4e9af6 --- /dev/null +++ b/cases/translation/functions/octet-length.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.octet-length" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "OCTET_LENGTH of multi-byte text. Catches: the character count instead of UTF-8 bytes (5 characters, 6 bytes)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'h\u00e9llo'"], + ["'\u20ac'"], + ["'ab'"], +] +query = "SELECT OCTET_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "OCTET_LENGTH of binary. Catches: the hex text length instead of the byte length" +columns = [{ name = "a", type = "BINARY" }] +rows = [ + ["'00FF10'"], +] +query = "SELECT OCTET_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string_or_binary" +description = "NULL next to a set value. Catches: NULL read as 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT OCTET_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'x'"], +] +query = "SELECT OCTET_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/parse-ip.toml b/cases/translation/functions/parse-ip.toml new file mode 100644 index 000000000..592223da6 --- /dev/null +++ b/cases/translation/functions/parse-ip.toml @@ -0,0 +1,73 @@ +category = "translation" +construct = "sf.functions.parse-ip" +session_profile = "utc-v1" + +[[case]] +id = "t001_inet" +description = "IPv4, IPv4 with a prefix, and IPv6. Catches: missing ipv4_range_start/end for a prefix, a signed ip_fields value, or IPv6 fields out of order" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'192.0.2.1'"], + ["'192.168.1.200/24'"], + ["'2001:db8::ff00:42:8329'"], +] +query = "SELECT PARSE_IP(a, 'INET') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_cidr" +description = "CIDR in lower case. Catches: the type name compared case-sensitively" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'10.1.2.3/8'"], +] +query = "SELECT PARSE_IP(a, 'cidr') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_permissive" +description = "An unparseable address with permissive 1. Catches: an error or NULL instead of an object with an error field" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'300.1.2.3'"], + ["'not an ip'"], +] +query = "SELECT PARSE_IP(a, 'INET', 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_expr" +description = "NULL address. Catches: an error object instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'1.2.3.4'"], +] +query = "SELECT PARSE_IP(a, 'INET') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_permissive" +description = "NULL permissive flag. Catches: NULL read as 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'1.2.3.4'"], +] +query = "SELECT PARSE_IP(a, 'INET', NULL) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_strict" +description = "An unparseable address with the default permissive 0. Catches: an error object or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'300.1.2.3'"], +] +query = "SELECT PARSE_IP(a, 'INET') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/parse-url.toml b/cases/translation/functions/parse-url.toml new file mode 100644 index 000000000..a203292cd --- /dev/null +++ b/cases/translation/functions/parse-url.toml @@ -0,0 +1,61 @@ +category = "translation" +construct = "sf.functions.parse-url" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "A URL with every part, and one with none of the optional parts. Catches: parameters not split into an object, a port as a number, or missing keys set to empty strings instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'https://user@ex.com:8080/a/b.html?x=1&y=2#frag'"], + ["'HTTP://Ex.com'"], + ["'ftp://h/p?q'"], +] +query = "SELECT PARSE_URL(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_permissive" +description = "An unparseable URL with permissive 1. Catches: an error instead of an error object" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'://bad'"], +] +query = "SELECT PARSE_URL(a, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string" +description = "NULL URL. Catches: an error object instead of NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'http://h'"], +] +query = "SELECT PARSE_URL(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_permissive" +description = "NULL permissive flag. Catches: NULL read as 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'http://h'"], +] +query = "SELECT PARSE_URL(a, NULL) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_strict" +description = "An unparseable URL with permissive 0. Catches: an error object or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'://bad'"], +] +query = "SELECT PARSE_URL(a, 0) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/position.position-in.toml b/cases/translation/functions/position.position-in.toml new file mode 100644 index 000000000..a9a805e9c --- /dev/null +++ b/cases/translation/functions/position.position-in.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.position.position-in" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Search for a substring after multi-byte characters. Catches: arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'an'", "'banana'"], + ["'l'", "'\u00e9\u00e9l'"], + ["'x'", "'abc'"], +] +query = "SELECT POSITION(a IN b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "Search in binary values. Catches: a hex-text offset (3 instead of 2)" +columns = [{ name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }] +rows = [ + ["'FF'", "'00FF'"], +] +query = "SELECT POSITION(a IN b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr1" +description = "NULL needle next to a set one. Catches: NULL read as an empty needle (1 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'abc'"], + ["'c'", "'abc'"], +] +query = "SELECT POSITION(a IN b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_expr2" +description = "NULL haystack next to a set one. Catches: NULL read as an empty string (0 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "'ba'"], +] +query = "SELECT POSITION(a IN b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty_needle" +description = "An empty needle, which the page says gives 1. Catches: NULL or 0 for an empty needle" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["''", "'abc'"], + ["''", "''"], +] +query = "SELECT POSITION(a IN b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/position.position.toml b/cases/translation/functions/position.position.toml new file mode 100644 index 000000000..d9a041d56 --- /dev/null +++ b/cases/translation/functions/position.position.toml @@ -0,0 +1,100 @@ +category = "translation" +construct = "sf.functions.position.position" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Search for a substring after multi-byte characters. Catches: arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'an'", "'banana'"], + ["'l'", "'\u00e9\u00e9l'"], + ["'x'", "'abc'"], +] +query = "SELECT POSITION(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_start_pos" +description = "A start position past the first match. Catches: the start position ignored (2 instead of 4) or the result counted from the start position" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'an'", "'banana'", "3"], + ["'an'", "'banana'", "5"], + ["'\u00e9'", "'\u00e9a\u00e9'", "2"], +] +query = "SELECT POSITION(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "Search in binary values. Catches: a hex-text offset (3 instead of 2)" +columns = [{ name = "a", type = "BINARY" }, { name = "b", type = "BINARY" }] +rows = [ + ["'FF'", "'00FF'"], +] +query = "SELECT POSITION(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_expr1" +description = "NULL needle next to a set one. Catches: NULL read as an empty needle (1 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'abc'"], + ["'c'", "'abc'"], +] +query = "SELECT POSITION(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_expr2" +description = "NULL haystack next to a set one. Catches: NULL read as an empty string (0 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "'ba'"], +] +query = "SELECT POSITION(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty_needle" +description = "An empty needle, which the page says gives 1. Catches: NULL or 0 for an empty needle" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["''", "'abc'"], + ["''", "''"], +] +query = "SELECT POSITION(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_null_start_pos" +description = "NULL start next to a set one. Catches: NULL read as the default 1" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "'ba'", "NULL"], + ["'a'", "'ba'", "1"], +] +query = "SELECT POSITION(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_start_past_end" +description = "A start position past the end of the string. Catches: an error or the last match instead of 0" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "'aba'", "4"], + ["'a'", "'aba'", "99"], +] +query = "SELECT POSITION(a, b, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/regexp-count.toml b/cases/translation/functions/regexp-count.toml new file mode 100644 index 000000000..ad94c12c5 --- /dev/null +++ b/cases/translation/functions/regexp-count.toml @@ -0,0 +1,111 @@ +category = "translation" +construct = "sf.functions.regexp-count" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Non-overlapping matches, unanchored. Catches: overlapping matches counted (3 for aa in aaaa) or an anchored match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abcabcab'", "'ab'"], + ["'aaaa'", "'aa'"], + ["'xyz'", "'q'"], + ["'\u00e9\u00e9\u00e9'", "'\u00e9'"], +] +query = "SELECT REGEXP_COUNT(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_position" +description = "A start position in characters past a multi-byte character. Catches: the position ignored, read 0-based, or as a byte offset" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcabcab'", "'ab'", "3"], + ["'\u00e9ab'", "'ab'", "2"], + ["'\u00e9ab'", "'ab'", "3"], +] +query = "SELECT REGEXP_COUNT(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_parameters" +description = "The i parameter. Catches: parameters ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'AbaB'", "'ab'", "'i'"], + ["'AbaB'", "'ab'", "'c'"], +] +query = "SELECT REGEXP_COUNT(a, p, 1, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as empty (0)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'"], + ["'a'", "'a'"], +] +query = "SELECT REGEXP_COUNT(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ab'", "NULL"], +] +query = "SELECT REGEXP_COUNT(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_position" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'aa'", "NULL"], + ["'aa'", "2"], +] +query = "SELECT REGEXP_COUNT(a, 'a', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_parameters" +description = "NULL parameters. Catches: NULL read as c" +columns = [{ name = "a", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'Aa'", "NULL"], +] +query = "SELECT REGEXP_COUNT(a, 'a', 1, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_empty_match" +description = "A pattern that can match the empty string. Catches: empty matches not counted, or counted twice at each position" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'x*'"], + ["''", "'x*'"], +] +query = "SELECT REGEXP_COUNT(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t009_position_zero" +description = "Position 0, outside the documented positive range: records what Snowflake returns there; it moves to boundary if Snowflake returns a value instead of an error. Catches: position 0 read as 1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'aa'"], +] +query = "SELECT REGEXP_COUNT(a, 'a', 0) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/regexp-instr.toml b/cases/translation/functions/regexp-instr.toml new file mode 100644 index 000000000..22e049945 --- /dev/null +++ b/cases/translation/functions/regexp-instr.toml @@ -0,0 +1,159 @@ +category = "translation" +construct = "sf.functions.regexp-instr" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "The first match after multi-byte characters. Catches: a 0-based or byte offset, or 0 for a match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'xyzabcabc'", "'abc'"], + ["'\u00e9\u00e9abc'", "'b'"], + ["'xyz'", "'q'"], +] +query = "SELECT REGEXP_INSTR(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_position_occurrence" +description = "Position and occurrence together. Catches: occurrence counted from the string start instead of the position, or ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["'abcabcabc'", "1", "2"], + ["'abcabcabc'", "2", "2"], + ["'abcabcabc'", "1", "4"], +] +query = "SELECT REGEXP_INSTR(a, 'abc', n, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_option" +description = "Option 0 and option 1. Catches: option 1 giving the last character of the match instead of the one after" +columns = [{ name = "a", type = "VARCHAR" }, { name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["'xxabcxx'", "0"], + ["'xxabcxx'", "1"], +] +query = "SELECT REGEXP_INSTR(a, 'abc', 1, 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_group" +description = "A capture group in the second match. Catches: the group number ignored (the match start) or the first match used" +columns = [{ name = "a", type = "VARCHAR" }, { name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["'k1=v1 k2=v22'", "2"], + ["'k1=v1 k2=v22'", "1"], +] +query = "SELECT REGEXP_INSTR(a, 'k([0-9])=v([0-9]+)', 1, 2, 0, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_parameters" +description = "The i parameter. Catches: parameters ignored" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'xxABC'"], +] +query = "SELECT REGEXP_INSTR(a, 'abc', 1, 1, 0, 'i') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006_null_subject" +description = "NULL subject. Catches: NULL read as empty (0)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT REGEXP_INSTR(a, 'b') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'b'"], +] +query = "SELECT REGEXP_INSTR('ab', p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_null_position" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_INSTR('abab', 'a', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009_null_occurrence" +description = "NULL occurrence. Catches: NULL read as 1" +columns = [{ name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_INSTR('abab', 'a', 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t010_null_option" +description = "NULL option. Catches: NULL read as 0" +columns = [{ name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT REGEXP_INSTR('abab', 'a', 1, 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t011_null_regexp_parameters" +description = "NULL parameters. Catches: NULL read as c" +columns = [{ name = "m", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'i'"], +] +query = "SELECT REGEXP_INSTR('xA', 'a', 1, 1, 0, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t012_null_group_num" +description = "NULL group. Catches: NULL read as the whole match" +columns = [{ name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT REGEXP_INSTR('k1=v1', 'k([0-9])', 1, 1, 0, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t013_occurrence_past_end" +description = "An occurrence past the last match. Catches: the last match returned instead of 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abab'"], +] +query = "SELECT REGEXP_INSTR(a, 'b', 1, 3) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/regexp-like.toml b/cases/translation/functions/regexp-like.toml new file mode 100644 index 000000000..cc088a4ac --- /dev/null +++ b/cases/translation/functions/regexp-like.toml @@ -0,0 +1,114 @@ +category = "translation" +construct = "sf.functions.regexp-like" +session_profile = "utc-v1" + +[[case]] +id = "t001_anchored" +description = "Patterns that match inside but not across the whole subject. Catches: an unanchored search (TRUE for abc against b) or a LIKE reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'b'"], + ["'abc'", "'a.c'"], + ["'abc'", "'a[b-d]+'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'ab'"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Case-sensitive by default. Catches: case-insensitive matching (TRUE for ABC against abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abc'"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_dot_newline" +description = "A dot against a newline without the s parameter. Catches: dot matching newline by default" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a.b'"], + ["'axb'", "'a.b'"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against .*)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'.*'"], + ["''", "'.*'"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty subject and pattern (the page: '' becomes ^$). Catches: the empty pattern matching everything" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_bad_pattern" +description = "An unbalanced bracket. Catches: FALSE or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'a['"], +] +query = "SELECT REGEXP_LIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t008_parameters" +description = "The i and s parameters, and c after i. Catches: parameters ignored, or the last of conflicting parameters not winning" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'", "'i'"], + ["'a' || CHR(10) || 'b'", "'a.b'", "'s'"], + ["'ABC'", "'abc'", "'ic'"], +] +query = "SELECT REGEXP_LIKE(a, p, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_null_parameters" +description = "NULL parameters. Catches: NULL read as the default c" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'a'", "'a'", "NULL"], + ["'A'", "'a'", "'i'"], +] +query = "SELECT REGEXP_LIKE(a, p, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/regexp-replace.toml b/cases/translation/functions/regexp-replace.toml new file mode 100644 index 000000000..44ce4a163 --- /dev/null +++ b/cases/translation/functions/regexp-replace.toml @@ -0,0 +1,145 @@ +category = "translation" +construct = "sf.functions.regexp-replace" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Replace every match. Catches: only the first match replaced, or the pattern anchored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'a1b22c333'", "'#'"], + ["'\u00e9\u00e91'", "'x'"], +] +query = "SELECT REGEXP_REPLACE(a, '[0-9]+', r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default_replacement" +description = "No replacement argument, which removes the matches. Catches: the pattern left in place or replaced with NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'a1b22c'"], +] +query = "SELECT REGEXP_REPLACE(a, '[0-9]') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_backreference" +description = "A backreference to a capture group. Catches: \\1 inserted literally, or $1 syntax expected" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'john smith'"], +] +query = "SELECT REGEXP_REPLACE(a, '([a-z]+) ([a-z]+)', '\\\\2, \\\\1') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_position_occurrence" +description = "Position and a single occurrence. Catches: occurrence ignored (all replaced) or counted from the string start instead of the position" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["'aXaXaXa'", "1", "2"], + ["'aXaXaXa'", "3", "2"], + ["'aXaXaXa'", "2", "0"], +] +query = "SELECT REGEXP_REPLACE(a, 'a', '-', n, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_parameters" +description = "The i parameter. Catches: parameters ignored" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'AbAb'"], +] +query = "SELECT REGEXP_REPLACE(a, 'a', '_', 1, 0, 'i') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t006_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT REGEXP_REPLACE(a, 'a', 'b') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT REGEXP_REPLACE('ab', p, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_null_replacement" +description = "NULL replacement. Catches: NULL read as '' (matches removed instead of NULL)" +columns = [{ name = "r", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["''"], +] +query = "SELECT REGEXP_REPLACE('ab', 'a', r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009_null_position" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_REPLACE('aa', 'a', 'x', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t010_null_occurrence" +description = "NULL occurrence. Catches: NULL read as 0 (all)" +columns = [{ name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT REGEXP_REPLACE('aa', 'a', 'x', 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t011_null_parameters" +description = "NULL parameters. Catches: NULL read as c" +columns = [{ name = "m", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'i'"], +] +query = "SELECT REGEXP_REPLACE('Aa', 'a', 'x', 1, 0, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t012_empty_match" +description = "A pattern matching the empty string. Catches: empty matches skipped, or replaced twice at each position" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], + ["''"], +] +query = "SELECT REGEXP_REPLACE(a, 'x*', '-') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/regexp-substr-all.toml b/cases/translation/functions/regexp-substr-all.toml new file mode 100644 index 000000000..70ea6ff12 --- /dev/null +++ b/cases/translation/functions/regexp-substr-all.toml @@ -0,0 +1,134 @@ +category = "translation" +construct = "sf.functions.regexp-substr-all" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Matches after multi-byte characters, and no match. Catches: an anchored match, a byte-offset slice, or NULL instead of an empty array for no match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'\u00e912 ab345'", "'[0-9]+'"], + ["'xyz'", "'[0-9]+'"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_position_occurrence" +description = "Position and occurrence. Catches: occurrence counted from the string start, or ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["'a1b2c3d4'", "1", "2"], + ["'a1b2c3d4'", "5", "2"], + ["'a1b2c3d4'", "1", "9"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, '[0-9]', n, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_group" +description = "The e parameter with the default and an explicit group. Catches: e ignored (the whole match returned) or the group number off by one" +columns = [{ name = "a", type = "VARCHAR" }, { name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["'k1=v1 k2=v22'", "1"], + ["'k1=v1 k2=v22'", "2"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, 'k([0-9])=v([0-9]+)', 1, 1, 'e') AS d, REGEXP_SUBSTR_ALL(a, 'k([0-9])=v([0-9]+)', 1, 1, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_parameters" +description = "The i parameter. Catches: parameters ignored" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'xABCx'"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, 'abc', 1, 1, 'i') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a1'"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, '[0-9]') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'b'"], +] +query = "SELECT REGEXP_SUBSTR_ALL('ab', p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_position" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_SUBSTR_ALL('a1a2', 'a.', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_null_occurrence" +description = "NULL occurrence. Catches: NULL read as 1" +columns = [{ name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_SUBSTR_ALL('a1a2', 'a.', 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009_null_regex_parameters" +description = "NULL parameters. Catches: NULL read as c" +columns = [{ name = "m", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'i'"], +] +query = "SELECT REGEXP_SUBSTR_ALL('xA', 'a', 1, 1, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t010_null_group_num" +description = "NULL group. Catches: NULL read as group 1" +columns = [{ name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT REGEXP_SUBSTR_ALL('k1=v1', 'k([0-9])', 1, 1, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t011_empty" +description = "An empty subject. Catches: an empty match returned" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT REGEXP_SUBSTR_ALL(a, '[0-9]+') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/regexp-substr.toml b/cases/translation/functions/regexp-substr.toml new file mode 100644 index 000000000..ca2ac9df2 --- /dev/null +++ b/cases/translation/functions/regexp-substr.toml @@ -0,0 +1,134 @@ +category = "translation" +construct = "sf.functions.regexp-substr" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Matches after multi-byte characters, and no match. Catches: an anchored match, a byte-offset slice, or '' instead of NULL for no match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'\u00e912 ab345'", "'[0-9]+'"], + ["'xyz'", "'[0-9]+'"], +] +query = "SELECT REGEXP_SUBSTR(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_position_occurrence" +description = "Position and occurrence. Catches: occurrence counted from the string start, or ignored" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["'a1b2c3d4'", "1", "2"], + ["'a1b2c3d4'", "5", "2"], + ["'a1b2c3d4'", "1", "9"], +] +query = "SELECT REGEXP_SUBSTR(a, '[0-9]', n, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_group" +description = "The e parameter with the default and an explicit group. Catches: e ignored (the whole match returned) or the group number off by one" +columns = [{ name = "a", type = "VARCHAR" }, { name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["'k1=v1 k2=v22'", "1"], + ["'k1=v1 k2=v22'", "2"], +] +query = "SELECT REGEXP_SUBSTR(a, 'k([0-9])=v([0-9]+)', 1, 1, 'e') AS d, REGEXP_SUBSTR(a, 'k([0-9])=v([0-9]+)', 1, 1, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_parameters" +description = "The i parameter. Catches: parameters ignored" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'xABCx'"], +] +query = "SELECT REGEXP_SUBSTR(a, 'abc', 1, 1, 'i') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a1'"], +] +query = "SELECT REGEXP_SUBSTR(a, '[0-9]') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'b'"], +] +query = "SELECT REGEXP_SUBSTR('ab', p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_position" +description = "NULL position. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_SUBSTR('a1a2', 'a.', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_null_occurrence" +description = "NULL occurrence. Catches: NULL read as 1" +columns = [{ name = "o", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT REGEXP_SUBSTR('a1a2', 'a.', 1, o) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t009_null_regex_parameters" +description = "NULL parameters. Catches: NULL read as c" +columns = [{ name = "m", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'i'"], +] +query = "SELECT REGEXP_SUBSTR('xA', 'a', 1, 1, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t010_null_group_num" +description = "NULL group. Catches: NULL read as group 1" +columns = [{ name = "g", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT REGEXP_SUBSTR('k1=v1', 'k([0-9])', 1, 1, 'e', g) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t011_empty" +description = "An empty subject. Catches: an empty match returned" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT REGEXP_SUBSTR(a, '[0-9]+') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/regexp.toml b/cases/translation/functions/regexp.toml new file mode 100644 index 000000000..519c67d57 --- /dev/null +++ b/cases/translation/functions/regexp.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.regexp" +session_profile = "utc-v1" + +[[case]] +id = "t001_anchored" +description = "Patterns that match inside but not across the whole subject. Catches: an unanchored search (TRUE for abc against b) or a LIKE reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'b'"], + ["'abc'", "'a.c'"], + ["'abc'", "'a[b-d]+'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'ab'"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Case-sensitive by default. Catches: case-insensitive matching (TRUE for ABC against abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abc'"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_dot_newline" +description = "A dot against a newline without the s parameter. Catches: dot matching newline by default" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a.b'"], + ["'axb'", "'a.b'"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against .*)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'.*'"], + ["''", "'.*'"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty subject and pattern (the page: '' becomes ^$). Catches: the empty pattern matching everything" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_bad_pattern" +description = "An unbalanced bracket. Catches: FALSE or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'a['"], +] +query = "SELECT a REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t008_not" +description = "The NOT form with matching, non-matching and NULL rows. Catches: NOT dropped, or TRUE for a NULL subject" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a.*'"], + ["'xbc'", "'a.*'"], + ["NULL", "'a.*'"], +] +query = "SELECT a NOT REGEXP p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/repeat.toml b/cases/translation/functions/repeat.toml new file mode 100644 index 000000000..5b4bf2deb --- /dev/null +++ b/cases/translation/functions/repeat.toml @@ -0,0 +1,62 @@ +category = "translation" +construct = "sf.functions.repeat" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "REPEAT of a multi-character and a multi-byte string. Catches: the count off by one, or the arguments swapped" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "3"], + ["'\u00e9-'", "2"], +] +query = "SELECT REPEAT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL input. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT REPEAT(a, 2) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_n" +description = "NULL count. Catches: NULL read as 0" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["0"], +] +query = "SELECT REPEAT('x', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_zero" +description = "A count of 0 and an empty input. Catches: NULL instead of an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "0"], + ["''", "5"], +] +query = "SELECT REPEAT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t005_negative" +description = "A negative count, below the documented minimum: records what Snowflake returns. Catches: an error where Snowflake returns a value, or the reverse" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab'"], +] +query = "SELECT REPEAT(a, -1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/replace.toml b/cases/translation/functions/replace.toml new file mode 100644 index 000000000..7a36a518f --- /dev/null +++ b/cases/translation/functions/replace.toml @@ -0,0 +1,85 @@ +category = "translation" +construct = "sf.functions.replace" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Overlapping candidates and a replacement that recreates the pattern. Catches: overlapping replacement ('bb' for aaa), a re-scan of the result, or only the first occurrence" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "r", type = "VARCHAR" }] +rows = [ + ["'aaa'", "'aa'", "'b'"], + ["'abab'", "'ab'", "'aab'"], + ["'\u00e9.\u00e9'", "'\u00e9'", "'e'"], +] +query = "SELECT REPLACE(a, p, r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_no_replacement" +description = "No replacement argument. Catches: an error, or NULL" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'a-b-c'"], +] +query = "SELECT REPLACE(a, '-') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_case" +description = "A pattern in another case. Catches: case-insensitive replacement" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'aAa'"], +] +query = "SELECT REPLACE(a, 'A', '_') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT REPLACE(a, 'a', 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern (subject unchanged)" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT REPLACE('ab', p, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_replacement" +description = "NULL replacement. Catches: NULL read as '' (a delete)" +columns = [{ name = "r", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["''"], +] +query = "SELECT REPLACE('ab', 'a', r) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_empty_pattern" +description = "An empty pattern: records what Snowflake returns. Catches: the replacement inserted between every character" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], +] +query = "SELECT REPLACE(a, '', '-') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/reverse.toml b/cases/translation/functions/reverse.toml new file mode 100644 index 000000000..a3be196f4 --- /dev/null +++ b/cases/translation/functions/reverse.toml @@ -0,0 +1,49 @@ +category = "translation" +construct = "sf.functions.reverse" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "REVERSE of multi-byte text. Catches: byte reversal (invalid UTF-8 for \u00e9, \u20ac)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Hello, world!'"], + ["'h\u00e9llo\u20ac'"], +] +query = "SELECT REVERSE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_date" +description = "REVERSE of a DATE, as the page shows. Catches: an error for a DATE, or another date format" +columns = [{ name = "d", type = "DATE" }] +rows = [ + ["'2019-05-22'"], +] +query = "SELECT REVERSE(d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT REVERSE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT REVERSE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/right.toml b/cases/translation/functions/right.toml new file mode 100644 index 000000000..2872c86bc --- /dev/null +++ b/cases/translation/functions/right.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.right" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "RIGHT of multi-byte text. Catches: a byte count (cutting \u00e9 in half) or the other end of the string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'h\u00e9llo'", "2"], + ["'abcdef'", "3"], + ["'ab'", "9"], +] +query = "SELECT RIGHT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_binary" +description = "RIGHT of binary. Catches: hex characters counted instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'00FF10'", "2"], +] +query = "SELECT RIGHT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL", "2"], + ["'ab'", "1"], +] +query = "SELECT RIGHT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_length_expr" +description = "NULL length. Catches: NULL read as 0 (an empty string instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "NULL"], + ["'ab'", "0"], +] +query = "SELECT RIGHT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_negative" +description = "A negative length, which the page says gives an empty string. Catches: an error, or Postgres semantics (all but the last characters)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "-2"], + ["'abcdef'", "0"], +] +query = "SELECT RIGHT(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/rlike.rlike.syntax.toml b/cases/translation/functions/rlike.rlike.syntax.toml new file mode 100644 index 000000000..dbb23a8cc --- /dev/null +++ b/cases/translation/functions/rlike.rlike.syntax.toml @@ -0,0 +1,102 @@ +category = "translation" +construct = "sf.functions.rlike.rlike.syntax" +session_profile = "utc-v1" + +[[case]] +id = "t001_anchored" +description = "Patterns that match inside but not across the whole subject. Catches: an unanchored search (TRUE for abc against b) or a LIKE reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'b'"], + ["'abc'", "'a.c'"], + ["'abc'", "'a[b-d]+'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'ab'"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Case-sensitive by default. Catches: case-insensitive matching (TRUE for ABC against abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abc'"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_dot_newline" +description = "A dot against a newline without the s parameter. Catches: dot matching newline by default" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a.b'"], + ["'axb'", "'a.b'"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against .*)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'.*'"], + ["''", "'.*'"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty subject and pattern (the page: '' becomes ^$). Catches: the empty pattern matching everything" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_bad_pattern" +description = "An unbalanced bracket. Catches: FALSE or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'a['"], +] +query = "SELECT a RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t008_not" +description = "The NOT form with matching, non-matching and NULL rows. Catches: NOT dropped, or TRUE for a NULL subject" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'a.*'"], + ["'xbc'", "'a.*'"], + ["NULL", "'a.*'"], +] +query = "SELECT a NOT RLIKE p AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/rlike.rlike.toml b/cases/translation/functions/rlike.rlike.toml new file mode 100644 index 000000000..5b6080164 --- /dev/null +++ b/cases/translation/functions/rlike.rlike.toml @@ -0,0 +1,114 @@ +category = "translation" +construct = "sf.functions.rlike.rlike" +session_profile = "utc-v1" + +[[case]] +id = "t001_anchored" +description = "Patterns that match inside but not across the whole subject. Catches: an unanchored search (TRUE for abc against b) or a LIKE reading of the pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "'b'"], + ["'abc'", "'a.c'"], + ["'abc'", "'a[b-d]+'"], + ["'a.c'", "'a.c'"], + ["'abc'", "'ab'"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_case" +description = "Case-sensitive by default. Catches: case-insensitive matching (TRUE for ABC against abc)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'"], + ["'abc'", "'abc'"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_dot_newline" +description = "A dot against a newline without the s parameter. Catches: dot matching newline by default" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a' || CHR(10) || 'b'", "'a.b'"], + ["'axb'", "'a.b'"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_subject" +description = "NULL subject. Catches: NULL read as an empty string (TRUE against .*)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["NULL", "'.*'"], + ["''", "'.*'"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_pattern" +description = "NULL pattern. Catches: NULL read as an empty pattern" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "NULL"], + ["'a'", "'a'"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty subject and pattern (the page: '' becomes ^$). Catches: the empty pattern matching everything" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["''", "''"], + ["'a'", "''"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_bad_pattern" +description = "An unbalanced bracket. Catches: FALSE or NULL instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'a'", "'a['"], +] +query = "SELECT RLIKE(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] + +[[case]] +id = "t008_parameters" +description = "The i and s parameters, and c after i. Catches: parameters ignored, or the last of conflicting parameters not winning" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'ABC'", "'abc'", "'i'"], + ["'a' || CHR(10) || 'b'", "'a.b'", "'s'"], + ["'ABC'", "'abc'", "'ic'"], +] +query = "SELECT RLIKE(a, p, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_null_parameters" +description = "NULL parameters. Catches: NULL read as the default c" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "VARCHAR" }, { name = "m", type = "VARCHAR" }] +rows = [ + ["'a'", "'a'", "NULL"], + ["'A'", "'a'", "'i'"], +] +query = "SELECT RLIKE(a, p, m) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/rpad.toml b/cases/translation/functions/rpad.toml new file mode 100644 index 000000000..44cf1e298 --- /dev/null +++ b/cases/translation/functions/rpad.toml @@ -0,0 +1,107 @@ +category = "translation" +construct = "sf.functions.rpad" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "RPAD with a multi-character pad that must be cut. Catches: the pad cut from the wrong end, or the length counted in bytes (\u00e9)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }, { name = "p", type = "VARCHAR" }] +rows = [ + ["'abc'", "6", "'xy'"], + ["'\u00e9'", "4", "'ab'"], +] +query = "SELECT RPAD(a, n, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_truncate" +description = "A base longer than the length. Catches: the base kept whole, or truncated from the right end for LPAD" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "3"], +] +query = "SELECT RPAD(a, n, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_default_pad" +description = "No pad argument. Catches: no padding, or a pad other than a blank" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'ab'", "4"], +] +query = "SELECT RPAD(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_binary" +description = "RPAD of binary. Catches: hex text padded instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "n", type = "NUMBER(38,0)" }, { name = "p", type = "BINARY" }] +rows = [ + ["'01'", "3", "'FF00'"], +] +query = "SELECT RPAD(a, n, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_base" +description = "NULL base. Catches: NULL read as empty (pad only)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT RPAD(a, 3, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_length_expr" +description = "NULL length. Catches: NULL read as 0" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["3"], +] +query = "SELECT RPAD('a', n, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_pad" +description = "NULL pad. Catches: NULL read as the default blank" +columns = [{ name = "p", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'*'"], +] +query = "SELECT RPAD('a', 3, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_empty_pad" +description = "An empty pad, which adds nothing but still truncates. Catches: an infinite loop or error, or no truncation" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "5"], + ["'abcdef'", "2"], +] +query = "SELECT RPAD(a, n, '') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t009_negative_length" +description = "A negative length: records what Snowflake returns. Catches: an error or the untouched base where Snowflake returns its value" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], +] +query = "SELECT RPAD(a, -1, '*') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/rtrim.toml b/cases/translation/functions/rtrim.toml new file mode 100644 index 000000000..4a8e917af --- /dev/null +++ b/cases/translation/functions/rtrim.toml @@ -0,0 +1,64 @@ +category = "translation" +construct = "sf.functions.rtrim" +session_profile = "utc-v1" + +[[case]] +id = "t001_characters" +description = "RTRIM with a character set given out of order. Catches: the set read as a substring, or characters removed from the left as well" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'xyyxabcxy'", "'yx'"], + ["'$.$a.b$.'", "'.$'"], + ["'\u00e9a\u00e9'", "'\u00e9'"], +] +query = "SELECT RTRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default" +description = "RTRIM with the default blank, beside tabs and newlines. Catches: all whitespace removed (the tab and newline kept by Snowflake)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' ab '"], + ["CHR(9) || ' ab ' || CHR(10)"], +] +query = "SELECT RTRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' a '"], +] +query = "SELECT RTRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_characters" +description = "NULL character set. Catches: NULL read as the default blank" +columns = [{ name = "c", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT RTRIM(' xax ', c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "An empty character set, and a string made only of trimmed characters. Catches: an empty set read as the default blank, or NULL for a fully trimmed string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["' a '", "''"], + ["'xxx'", "'x'"], +] +query = "SELECT RTRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/rtrimmed-length.toml b/cases/translation/functions/rtrimmed-length.toml new file mode 100644 index 000000000..f7e2ae01c --- /dev/null +++ b/cases/translation/functions/rtrimmed-length.toml @@ -0,0 +1,41 @@ +category = "translation" +construct = "sf.functions.rtrimmed-length" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "RTRIMMED_LENGTH with trailing and leading blanks and a trailing tab. Catches: LENGTH semantics (trailing blanks counted), leading blanks removed, or tabs treated as blanks" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab '"], + ["' ab'"], + ["'ab\t'"], + ["'\u00e9 '"], +] +query = "SELECT RTRIMMED_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_string_expr" +description = "NULL next to a set value. Catches: NULL read as 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a '"], +] +query = "SELECT RTRIMMED_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_blanks_only" +description = "The empty string and a blanks-only string. Catches: NULL for the empty string, or the blanks counted" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["' '"], +] +query = "SELECT RTRIMMED_LENGTH(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/search-ip.toml b/cases/translation/functions/search-ip.toml new file mode 100644 index 000000000..4e3de31fa --- /dev/null +++ b/cases/translation/functions/search-ip.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.search-ip" +session_profile = "utc-v1" + +[[case]] +id = "t001_ipv4" +description = "An address inside text, and a longer address with the same prefix. Catches: a substring match (TRUE for 192.0.2.10)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'host 192.0.2.1 up'"], + ["'192.0.2.10'"], + ["'none'"], +] +query = "SELECT SEARCH_IP(a, '192.0.2.1') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_cidr" +description = "A CIDR range. Catches: the range read as a single address" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'x 192.0.2.77'"], + ["'192.0.3.1'"], +] +query = "SELECT SEARCH_IP(a, '192.0.2.0/24') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_leading_zeros" +description = "A search string with leading zeros. Catches: leading zeros compared as text" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'192.0.2.1'"], +] +query = "SELECT SEARCH_IP(a, '192.000.002.001') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_search_data" +description = "NULL data. Catches: NULL read as empty (FALSE)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'10.0.0.1'"], +] +query = "SELECT SEARCH_IP(a, '10.0.0.1') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/search.toml b/cases/translation/functions/search.toml new file mode 100644 index 000000000..4a3f0bb80 --- /dev/null +++ b/cases/translation/functions/search.toml @@ -0,0 +1,64 @@ +category = "translation" +construct = "sf.functions.search" +session_profile = "utc-v1" + +[[case]] +id = "t001_or" +description = "The default OR mode, case-insensitive. Catches: a substring match (TRUE for quickly), case-sensitive tokens, or AND semantics" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'The Quick brown'"], + ["'a fox'"], + ["'quickly'"], + ["'nothing'"], +] +query = "SELECT SEARCH(a, 'quick fox') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_modes" +description = "AND, PHRASE and EXACT modes on the same data. Catches: SEARCH_MODE ignored (all three equal to OR)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'fox the quick'"], + ["'the quick fox'"], +] +query = "SELECT SEARCH(a, 'quick fox', SEARCH_MODE => 'AND') AS r_and, SEARCH(a, 'quick fox', SEARCH_MODE => 'PHRASE') AS r_phrase, SEARCH(a, 'quick fox', SEARCH_MODE => 'EXACT') AS r_exact FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_analyzer" +description = "The NO_OP_ANALYZER, which does not tokenize. Catches: the analyzer ignored (TRUE for a token inside the data)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'quick fox'"], + ["'quick'"], +] +query = "SELECT SEARCH(a, 'quick', ANALYZER => 'NO_OP_ANALYZER') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_search_data" +description = "NULL data. Catches: NULL read as empty (FALSE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT SEARCH(a, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "Empty data. Catches: NULL or TRUE for empty data" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SEARCH(a, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/sha1-binary.toml b/cases/translation/functions/sha1-binary.toml new file mode 100644 index 000000000..5ce28ee1e --- /dev/null +++ b/cases/translation/functions/sha1-binary.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.sha1-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA1_BINARY of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT SHA1_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA1_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA1_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/sha1.sha1-hex.toml b/cases/translation/functions/sha1.sha1-hex.toml new file mode 100644 index 000000000..1c9b42cf4 --- /dev/null +++ b/cases/translation/functions/sha1.sha1-hex.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.sha1.sha1-hex" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA1_HEX of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT SHA1_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA1_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA1_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/sha1.sha1.toml b/cases/translation/functions/sha1.sha1.toml new file mode 100644 index 000000000..4233d3659 --- /dev/null +++ b/cases/translation/functions/sha1.sha1.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.sha1.sha1" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA1 of ASCII and multi-byte text. Catches: another digest, upper-case hex, or Latin-1 bytes for \u00e9" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Snowflake'"], + ["'\u00e9'"], + ["'AbCd0'"], +] +query = "SELECT SHA1(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_msg" +description = "NULL message next to a set one. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA1(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which has a defined digest. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA1(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/sha2-binary.toml b/cases/translation/functions/sha2-binary.toml new file mode 100644 index 000000000..00e4971fd --- /dev/null +++ b/cases/translation/functions/sha2-binary.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.sha2-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA2_BINARY with the default size, including the page's AbCd0. Catches: SHA-1, SHA-512 or upper-case hex as the default" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'AbCd0'"], + ["'\u00e9'"], +] +query = "SELECT SHA2_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_digest_size" +description = "Each documented digest size. Catches: the size ignored or 224 read as SHA-512/224" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'Snowflake'", "224"], + ["'Snowflake'", "256"], + ["'Snowflake'", "384"], + ["'Snowflake'", "512"], +] +query = "SELECT SHA2_BINARY(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_msg" +description = "NULL message. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA2_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_digest_size" +description = "NULL size. Catches: NULL read as the default 256" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "512"], +] +query = "SELECT SHA2_BINARY(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA2_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_bad_size" +description = "An undocumented digest size. Catches: a fallback to 256 instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "100"], +] +query = "SELECT SHA2_BINARY(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/sha2.sha2-hex.toml b/cases/translation/functions/sha2.sha2-hex.toml new file mode 100644 index 000000000..2b0f7c318 --- /dev/null +++ b/cases/translation/functions/sha2.sha2-hex.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.sha2.sha2-hex" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA2_HEX with the default size, including the page's AbCd0. Catches: SHA-1, SHA-512 or upper-case hex as the default" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'AbCd0'"], + ["'\u00e9'"], +] +query = "SELECT SHA2_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_digest_size" +description = "Each documented digest size. Catches: the size ignored or 224 read as SHA-512/224" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'Snowflake'", "224"], + ["'Snowflake'", "256"], + ["'Snowflake'", "384"], + ["'Snowflake'", "512"], +] +query = "SELECT SHA2_HEX(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_msg" +description = "NULL message. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA2_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_digest_size" +description = "NULL size. Catches: NULL read as the default 256" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "512"], +] +query = "SELECT SHA2_HEX(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA2_HEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_bad_size" +description = "An undocumented digest size. Catches: a fallback to 256 instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "100"], +] +query = "SELECT SHA2_HEX(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/sha2.sha2.toml b/cases/translation/functions/sha2.sha2.toml new file mode 100644 index 000000000..95fb21917 --- /dev/null +++ b/cases/translation/functions/sha2.sha2.toml @@ -0,0 +1,75 @@ +category = "translation" +construct = "sf.functions.sha2.sha2" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SHA2 with the default size, including the page's AbCd0. Catches: SHA-1, SHA-512 or upper-case hex as the default" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'AbCd0'"], + ["'\u00e9'"], +] +query = "SELECT SHA2(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_digest_size" +description = "Each documented digest size. Catches: the size ignored or 224 read as SHA-512/224" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'Snowflake'", "224"], + ["'Snowflake'", "256"], + ["'Snowflake'", "384"], + ["'Snowflake'", "512"], +] +query = "SELECT SHA2(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_msg" +description = "NULL message. Catches: NULL hashed as an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SHA2(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_digest_size" +description = "NULL size. Catches: NULL read as the default 256" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "NULL"], + ["'a'", "512"], +] +query = "SELECT SHA2(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT SHA2(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_bad_size" +description = "An undocumented digest size. Catches: a fallback to 256 instead of an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a'", "100"], +] +query = "SELECT SHA2(a, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/soundex-p123.toml b/cases/translation/functions/soundex-p123.toml new file mode 100644 index 000000000..b4bff6aae --- /dev/null +++ b/cases/translation/functions/soundex-p123.toml @@ -0,0 +1,28 @@ +category = "translation" +construct = "sf.functions.soundex-p123" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "The page's examples and a name where both functions agree. Catches: plain SOUNDEX (P236, L300)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Pfister'"], + ["'Lloyd'"], + ["'Robert'"], +] +query = "SELECT SOUNDEX_P123(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_varchar_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'Ann'"], +] +query = "SELECT SOUNDEX_P123(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/cases/translation/functions/soundex.toml b/cases/translation/functions/soundex.toml new file mode 100644 index 000000000..572874edd --- /dev/null +++ b/cases/translation/functions/soundex.toml @@ -0,0 +1,43 @@ +category = "translation" +construct = "sf.functions.soundex" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SOUNDEX of names that exercise the H/W and same-code rules. Catches: the H/W rule missed (A226 instead of A261 for Ashcraft), adjacent same codes not merged, or no zero padding" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'Robert'"], + ["'Ashcraft'"], + ["'Tymczak'"], + ["'Pfister'"], + ["'Lloyd'"], + ["'Lee'"], +] +query = "SELECT SOUNDEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_varchar_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'Ann'"], +] +query = "SELECT SOUNDEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string and a string with no letters: records what Snowflake returns. Catches: a padded 000 code or an error where Snowflake returns empty, or the reverse" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'123'"], +] +query = "SELECT SOUNDEX(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/space.toml b/cases/translation/functions/space.toml new file mode 100644 index 000000000..42255fc31 --- /dev/null +++ b/cases/translation/functions/space.toml @@ -0,0 +1,50 @@ +category = "translation" +construct = "sf.functions.space" +session_profile = "utc-v1" + +[[case]] +id = "t001_number" +description = "SPACE with its length shown beside it. Catches: the count off by one or another character" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["3"], + ["1"], +] +query = "SELECT SPACE(n) AS r, LENGTH(SPACE(n)) AS l, '[' || SPACE(n) || ']' AS b FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_fraction" +description = "A fractional count: records Snowflake's rounding. Catches: truncation (2 blanks) where Snowflake rounds 2.7 to 3, or the reverse" +columns = [{ name = "n", type = "NUMBER(10,2)" }] +rows = [ + ["2.70"], + ["2.20"], +] +query = "SELECT LENGTH(SPACE(n)) AS l FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_n" +description = "NULL count. Catches: NULL read as 0" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["0"], +] +query = "SELECT SPACE(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_negative" +description = "A negative count: records what Snowflake returns. Catches: an error where Snowflake returns an empty string, or the reverse" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["-2"], +] +query = "SELECT SPACE(n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/split-part.toml b/cases/translation/functions/split-part.toml new file mode 100644 index 000000000..7cd76b5d8 --- /dev/null +++ b/cases/translation/functions/split-part.toml @@ -0,0 +1,88 @@ +category = "translation" +construct = "sf.functions.split-part" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Parts by positive and negative numbers with a multi-character delimiter. Catches: the delimiter read as a character set (STRTOK), a 0-based part, or negative parts rejected" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a.@b.c'", "'.@'", "2"], + ["'a,b,c'", "','", "-1"], + ["'\u00e9,\u00fc'", "','", "2"], +] +query = "SELECT SPLIT_PART(a, d, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_part_zero" +description = "Part 0, which the page says is treated as 1. Catches: NULL or an empty string for 0" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'x,y'"], +] +query = "SELECT SPLIT_PART(a, ',', 0) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SPLIT_PART(a, ',', 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_delimiter" +description = "NULL delimiter. Catches: NULL read as empty" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["','"], +] +query = "SELECT SPLIT_PART('a,b', d, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_partnumber" +description = "NULL part. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT SPLIT_PART('a,b', ',', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty_parts" +description = "Leading and trailing delimiters and an empty delimiter. Catches: empty parts skipped (STRTOK semantics) or the empty delimiter splitting characters" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["',a,'", "','", "1"], + ["',a,'", "','", "3"], + ["'a,b'", "''", "1"], +] +query = "SELECT SPLIT_PART(a, d, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_out_of_range" +description = "Part numbers past either end. Catches: NULL instead of '' or a clamped part" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["4"], + ["-4"], +] +query = "SELECT SPLIT_PART('a,b,c', ',', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/split-to-table.toml b/cases/translation/functions/split-to-table.toml new file mode 100644 index 000000000..94b73889f --- /dev/null +++ b/cases/translation/functions/split-to-table.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.split-to-table" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Parts with an empty one and a multi-character delimiter. Catches: empty parts dropped, a 0-based INDEX, or the delimiter read as a set" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["'a,,b'", "','"], + ["'x<>y'"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(SPLIT_TO_TABLE(input.a, input.d)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_string" +description = "NULL string, which the page says gives no rows. Catches: one row (NULL or empty) for the NULL input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'p,q'"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(SPLIT_TO_TABLE(input.a, ',')) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_delimiter" +description = "NULL delimiter. Catches: NULL read as no delimiter" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["','"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(SPLIT_TO_TABLE('a,b', input.d)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string, which the page says gives one empty row. Catches: no rows for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'z'"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(SPLIT_TO_TABLE(input.a, ',')) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/split.toml b/cases/translation/functions/split.toml new file mode 100644 index 000000000..75e9078e6 --- /dev/null +++ b/cases/translation/functions/split.toml @@ -0,0 +1,51 @@ +category = "translation" +construct = "sf.functions.split" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Empty parts and a multi-character separator. Catches: empty parts dropped, or the separator read as a character set" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["'a,b,,c,'", "','"], + ["'a<>b'"], +] +query = "SELECT SPLIT(a, d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_string" +description = "NULL string. Catches: NULL read as empty ([\"\"])" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT SPLIT(a, ',') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_separator" +description = "NULL separator. Catches: NULL read as empty" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["','"], +] +query = "SELECT SPLIT('a,b', d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "An empty string and an empty separator: records what Snowflake returns. Catches: an empty array for an empty string, or a per-character split for an empty separator" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["''", "','"], + ["'a,b'", "''"], +] +query = "SELECT SPLIT(a, d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/startswith.toml b/cases/translation/functions/startswith.toml new file mode 100644 index 000000000..5c4dea60d --- /dev/null +++ b/cases/translation/functions/startswith.toml @@ -0,0 +1,53 @@ +category = "translation" +construct = "sf.functions.startswith" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "STARTSWITH with the arguments in both orders and in another case. Catches: arguments swapped or a case-insensitive match" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'banana'", "'ban'"], + ["'ban'", "'banana'"], + ["'banana'", "'BAN'"], + ["'\u00e9a'", "'\u00e9'"], +] +query = "SELECT STARTSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_expr1" +description = "NULL first argument. Catches: NULL read as an empty string (FALSE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["NULL", "'a'"], + ["'a'", "'a'"], +] +query = "SELECT STARTSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_expr2" +description = "NULL second argument. Catches: NULL read as an empty string (TRUE instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "NULL"], + ["'abc'", "'c'"], +] +query = "SELECT STARTSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "An empty search string, and an empty subject. Catches: FALSE for the empty search string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "b", type = "VARCHAR" }] +rows = [ + ["'abc'", "''"], + ["''", "'a'"], +] +query = "SELECT STARTSWITH(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/strtok-split-to-table.toml b/cases/translation/functions/strtok-split-to-table.toml new file mode 100644 index 000000000..993599024 --- /dev/null +++ b/cases/translation/functions/strtok-split-to-table.toml @@ -0,0 +1,63 @@ +category = "translation" +construct = "sf.functions.strtok-split-to-table" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Tokens with a delimiter set and consecutive delimiters. Catches: empty tokens kept, a 0-based INDEX, or the delimiter list read as one string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["'a.b@@c'", "'@.'"], + ["'\u00e9 b'", "' '"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(STRTOK_SPLIT_TO_TABLE(input.a, input.d)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default" +description = "The default blank delimiter. Catches: another default" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' x y '"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(STRTOK_SPLIT_TO_TABLE(input.a)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string" +description = "NULL string. Catches: a row for NULL input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'p q'"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(STRTOK_SPLIT_TO_TABLE(input.a)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_delimiter_list" +description = "NULL delimiter list. Catches: NULL read as the default" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' '"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(STRTOK_SPLIT_TO_TABLE('a b', input.d)) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_no_tokens" +description = "Empty and delimiter-only strings, which give no rows. Catches: one empty row (SPLIT_TO_TABLE semantics)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["',,'"], + ["'k'"], +] +query = "SELECT input.ordinal, t.index AS i, t.value AS v FROM input, TABLE(STRTOK_SPLIT_TO_TABLE(input.a, ',')) t ORDER BY input.ordinal, t.index" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/strtok-to-array.toml b/cases/translation/functions/strtok-to-array.toml new file mode 100644 index 000000000..ed3cac27b --- /dev/null +++ b/cases/translation/functions/strtok-to-array.toml @@ -0,0 +1,62 @@ +category = "translation" +construct = "sf.functions.strtok-to-array" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Tokens with a delimiter set and consecutive delimiters. Catches: empty tokens kept or the delimiter read as one string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }] +rows = [ + ["'a.b@@c'", "'@.'"], + ["'\u00e9 b'", "' '"], +] +query = "SELECT STRTOK_TO_ARRAY(a, d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default" +description = "The default blank delimiter. Catches: another default delimiter" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' x y '"], +] +query = "SELECT STRTOK_TO_ARRAY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string" +description = "NULL string. Catches: NULL read as empty (an empty array)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT STRTOK_TO_ARRAY(a, ',') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_delimiter" +description = "NULL delimiter. Catches: NULL read as the default" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' '"], +] +query = "SELECT STRTOK_TO_ARRAY('a b', d) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_no_tokens" +description = "Strings made only of delimiters, and the empty string. Catches: NULL or [\"\"] instead of an empty array" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["',,'"], + ["''"], +] +query = "SELECT STRTOK_TO_ARRAY(a, ',') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/strtok.toml b/cases/translation/functions/strtok.toml new file mode 100644 index 000000000..7bd99500c --- /dev/null +++ b/cases/translation/functions/strtok.toml @@ -0,0 +1,87 @@ +category = "translation" +construct = "sf.functions.strtok" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "Tokens with a delimiter set and consecutive delimiters. Catches: the delimiter read as one string (SPLIT_PART), or empty tokens returned" +columns = [{ name = "a", type = "VARCHAR" }, { name = "d", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'a.b@c'", "'@.'", "3"], + ["'a..b'", "'.'", "2"], + ["'\u00e9 b'", "' '", "2"], +] +query = "SELECT STRTOK(a, d, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_defaults" +description = "The default blank delimiter and part 1. Catches: another default delimiter or part" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' hello world'"], +] +query = "SELECT STRTOK(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_string" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT STRTOK(a, ',', 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_delimiter" +description = "NULL delimiter. Catches: NULL read as the default" +columns = [{ name = "d", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' '"], +] +query = "SELECT STRTOK('a b', d, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_partnumber" +description = "NULL part. Catches: NULL read as 1" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT STRTOK('a b', ' ', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "Empty delimiter with empty and non-empty strings. Catches: '' instead of NULL for empty input, or a split on every character" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'ab'"], +] +query = "SELECT STRTOK(a, '', 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t007_out_of_range" +description = "A token number past the last token. Catches: '' instead of NULL" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["3"], + ["2"], +] +query = "SELECT STRTOK('a b', ' ', n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/substr.substr.toml b/cases/translation/functions/substr.substr.toml new file mode 100644 index 000000000..0240bf3bf --- /dev/null +++ b/cases/translation/functions/substr.substr.toml @@ -0,0 +1,110 @@ +category = "translation" +construct = "sf.functions.substr.substr" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SUBSTR of multi-byte text. Catches: a 0-based start or byte offsets" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'h\u00e9llo'", "2", "3"], + ["'abcdef'", "3", "2"], +] +query = "SELECT SUBSTR(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_no_length" +description = "SUBSTR with no length. Catches: only one character returned" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "3"], + ["'\u00e9ab'", "2"], +] +query = "SELECT SUBSTR(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "SUBSTR of binary. Catches: hex characters counted instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'00FF1020'", "2", "2"], +] +query = "SELECT SUBSTR(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_start_zero" +description = "Start 0, which the page says is treated as 1. Catches: standard SQL semantics (one character fewer: 'a' instead of 'ab')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "2"], +] +query = "SELECT SUBSTR(a, 0, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_base_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT SUBSTR(a, 1, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_start_expr" +description = "NULL start. Catches: NULL read as 1" +columns = [{ name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT SUBSTR('abc', p, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_length_expr" +description = "NULL length. Catches: NULL read as no length" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT SUBSTR('abc', 2, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_negative_start" +description = "A negative start counted from the end, and a negative length. Catches: a negative start clamped to 1, or a negative length raising an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "-3", "2"], + ["'abcdef'", "2", "-1"], + ["'abc'", "-9", "2"], +] +query = "SELECT SUBSTR(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t009_past_end" +description = "A start past the end. Catches: an error or NULL instead of an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "5"], + ["'abc'", "4"], +] +query = "SELECT SUBSTR(a, p, 2) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/substr.substring.toml b/cases/translation/functions/substr.substring.toml new file mode 100644 index 000000000..084a8bfb9 --- /dev/null +++ b/cases/translation/functions/substr.substring.toml @@ -0,0 +1,110 @@ +category = "translation" +construct = "sf.functions.substr.substring" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "SUBSTRING of multi-byte text. Catches: a 0-based start or byte offsets" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'h\u00e9llo'", "2", "3"], + ["'abcdef'", "3", "2"], +] +query = "SELECT SUBSTRING(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_no_length" +description = "SUBSTRING with no length. Catches: only one character returned" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "3"], + ["'\u00e9ab'", "2"], +] +query = "SELECT SUBSTRING(a, p) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_binary" +description = "SUBSTRING of binary. Catches: hex characters counted instead of bytes" +columns = [{ name = "a", type = "BINARY" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'00FF1020'", "2", "2"], +] +query = "SELECT SUBSTRING(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_start_zero" +description = "Start 0, which the page says is treated as 1. Catches: standard SQL semantics (one character fewer: 'a' instead of 'ab')" +columns = [{ name = "a", type = "VARCHAR" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "2"], +] +query = "SELECT SUBSTRING(a, 0, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t005_null_base_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'ab'"], +] +query = "SELECT SUBSTRING(a, 1, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_null_start_expr" +description = "NULL start. Catches: NULL read as 1" +columns = [{ name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["2"], +] +query = "SELECT SUBSTRING('abc', p, 1) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t007_null_length_expr" +description = "NULL length. Catches: NULL read as no length" +columns = [{ name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["NULL"], + ["1"], +] +query = "SELECT SUBSTRING('abc', 2, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t008_negative_start" +description = "A negative start counted from the end, and a negative length. Catches: a negative start clamped to 1, or a negative length raising an error" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }, { name = "n", type = "NUMBER(38,0)" }] +rows = [ + ["'abcdef'", "-3", "2"], + ["'abcdef'", "2", "-1"], + ["'abc'", "-9", "2"], +] +query = "SELECT SUBSTRING(a, p, n) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] + +[[case]] +id = "t009_past_end" +description = "A start past the end. Catches: an error or NULL instead of an empty string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "p", type = "NUMBER(38,0)" }] +rows = [ + ["'abc'", "5"], + ["'abc'", "4"], +] +query = "SELECT SUBSTRING(a, p, 2) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary"] diff --git a/cases/translation/functions/translate.toml b/cases/translation/functions/translate.toml new file mode 100644 index 000000000..4396d210f --- /dev/null +++ b/cases/translation/functions/translate.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.functions.translate" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "A shorter target that removes a character, and multi-byte characters. Catches: unmatched source characters kept (Oracle-style keep instead of remove), or byte positions (\u00e9)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "f", type = "VARCHAR" }, { name = "t", type = "VARCHAR" }] +rows = [ + ["'abcabc'", "'abc'", "'xy'"], + ["'\u00e9te'", "'\u00e9'", "'e'"], + ["'ba'", "'ab'", "'ba'"], +] +query = "SELECT TRANSLATE(a, f, t) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_subject" +description = "NULL subject. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT TRANSLATE(a, 'a', 'b') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_null_sourcealphabet" +description = "NULL source alphabet. Catches: NULL read as empty (subject unchanged)" +columns = [{ name = "f", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT TRANSLATE('ab', f, 'x') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_targetalphabet" +description = "NULL target alphabet. Catches: NULL read as empty (characters removed)" +columns = [{ name = "t", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["''"], +] +query = "SELECT TRANSLATE('ab', 'a', t) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty_target" +description = "An empty target, which removes every source character. Catches: the source characters kept" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'banana'"], +] +query = "SELECT TRANSLATE(a, 'an', '') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] + +[[case]] +id = "t006_target_longer" +description = "A target longer than the source, which the page says is an error. Catches: the extra target characters ignored" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'abc'"], +] +query = "SELECT TRANSLATE(a, 'a', 'xy') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["error"] diff --git a/cases/translation/functions/trim.toml b/cases/translation/functions/trim.toml new file mode 100644 index 000000000..182b1554a --- /dev/null +++ b/cases/translation/functions/trim.toml @@ -0,0 +1,64 @@ +category = "translation" +construct = "sf.functions.trim" +session_profile = "utc-v1" + +[[case]] +id = "t001_characters" +description = "TRIM with a character set given out of order. Catches: the set read as a substring, or characters removed from one side only" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["'xyyxabcxy'", "'yx'"], + ["'$.$a.b$.'", "'.$'"], + ["'\u00e9a\u00e9'", "'\u00e9'"], +] +query = "SELECT TRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_default" +description = "TRIM with the default blank, beside tabs and newlines. Catches: all whitespace removed (the tab and newline kept by Snowflake)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["' ab '"], + ["CHR(9) || ' ab ' || CHR(10)"], +] +query = "SELECT TRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["' a '"], +] +query = "SELECT TRIM(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_null_characters" +description = "NULL character set. Catches: NULL read as the default blank" +columns = [{ name = "c", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT TRIM(' xax ', c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_empty" +description = "An empty character set, and a string made only of trimmed characters. Catches: an empty set read as the default blank, or NULL for a fully trimmed string" +columns = [{ name = "a", type = "VARCHAR" }, { name = "c", type = "VARCHAR" }] +rows = [ + ["' a '", "''"], + ["'xxx'", "'x'"], +] +query = "SELECT TRIM(a, c) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/try-base64-decode-binary.toml b/cases/translation/functions/try-base64-decode-binary.toml new file mode 100644 index 000000000..0a59f57df --- /dev/null +++ b/cases/translation/functions/try-base64-decode-binary.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.functions.try-base64-decode-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "TRY_BASE64_DECODE_BINARY of valid Base64. Catches: wrong bytes or Latin-1 text for multi-byte input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'+/8='"], + ["'YWJj'"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_invalid" +description = "Invalid Base64 next to valid Base64. Catches: an error for the invalid row (the non-TRY behaviour) or NULL for the valid row too" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab$c'"], + ["'YQ'"], + ["'YQ=='"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_alphabet" +description = "A custom alphabet. Catches: the alphabet ignored (NULL for -_ input)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'-_8~'", "'-_~'"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'YQ=='"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_alphabet" +description = "NULL alphabet next to a set one. Catches: NULL read as the default alphabet" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'YQ=='", "NULL"], + ["'YQ=='", "'+/='"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "The empty string. Catches: NULL for empty input instead of an empty value" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT TRY_BASE64_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/try-base64-decode-string.toml b/cases/translation/functions/try-base64-decode-string.toml new file mode 100644 index 000000000..0baf00bc9 --- /dev/null +++ b/cases/translation/functions/try-base64-decode-string.toml @@ -0,0 +1,74 @@ +category = "translation" +construct = "sf.functions.try-base64-decode-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "TRY_BASE64_DECODE_STRING of valid Base64. Catches: wrong bytes or Latin-1 text for multi-byte input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'w7zigqw='"], + ["'YWJj'"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_invalid" +description = "Invalid Base64 next to valid Base64. Catches: an error for the invalid row (the non-TRY behaviour) or NULL for the valid row too" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'ab$c'"], + ["'YQ'"], + ["'YQ=='"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_alphabet" +description = "A custom alphabet. Catches: the alphabet ignored (NULL for -_ input)" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'fn5-Pw~~'", "'-_~'"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t004_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'YQ=='"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t005_null_alphabet" +description = "NULL alphabet next to a set one. Catches: NULL read as the default alphabet" +columns = [{ name = "a", type = "VARCHAR" }, { name = "al", type = "VARCHAR" }] +rows = [ + ["'YQ=='", "NULL"], + ["'YQ=='", "'+/='"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a, al) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t006_empty" +description = "The empty string. Catches: NULL for empty input instead of an empty value" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT TRY_BASE64_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/try-hex-decode-binary.toml b/cases/translation/functions/try-hex-decode-binary.toml new file mode 100644 index 000000000..376cacc02 --- /dev/null +++ b/cases/translation/functions/try-hex-decode-binary.toml @@ -0,0 +1,53 @@ +category = "translation" +construct = "sf.functions.try-hex-decode-binary" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "TRY_HEX_DECODE_BINARY of upper- and lower-case hex digits. Catches: lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'00FF10'"], + ["'c3a9'"], + ["'7F80'"], +] +query = "SELECT TRY_HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_invalid" +description = "Invalid hex next to valid hex. Catches: an error (the non-TRY behaviour) instead of NULL, or a partial decode of the odd-length row" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'4G'"], + ["'414'"], + ["'4142'"], +] +query = "SELECT TRY_HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'41'"], +] +query = "SELECT TRY_HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'42'"], +] +query = "SELECT TRY_HEX_DECODE_BINARY(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/try-hex-decode-string.toml b/cases/translation/functions/try-hex-decode-string.toml new file mode 100644 index 000000000..4cf749636 --- /dev/null +++ b/cases/translation/functions/try-hex-decode-string.toml @@ -0,0 +1,52 @@ +category = "translation" +construct = "sf.functions.try-hex-decode-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "TRY_HEX_DECODE_STRING of upper- and lower-case hex digits. Catches: lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'48C3A96C6C6F'"], + ["'E282AC'"], +] +query = "SELECT TRY_HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_invalid" +description = "Invalid hex next to valid hex. Catches: an error (the non-TRY behaviour) instead of NULL, or a partial decode of the odd-length row" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'4G'"], + ["'414'"], + ["'4142'"], +] +query = "SELECT TRY_HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_input" +description = "NULL input next to a set one. Catches: NULL decoded as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'41'"], +] +query = "SELECT TRY_HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL or an error for empty input" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'42'"], +] +query = "SELECT TRY_HEX_DECODE_STRING(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/unicode.toml b/cases/translation/functions/unicode.toml new file mode 100644 index 000000000..96f9d7dba --- /dev/null +++ b/cases/translation/functions/unicode.toml @@ -0,0 +1,41 @@ +category = "translation" +construct = "sf.functions.unicode" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "UNICODE of multi-byte and multi-character strings. Catches: the first UTF-8 byte (195 for \u00e9) instead of the code point, or the last character" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'\u00e9'"], + ["'\u20acx'"], + ["'cde'"], + ["'\u2744'"], +] +query = "SELECT UNICODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_null_input" +description = "NULL input next to a set one. Catches: NULL read as an empty string (0 instead of NULL)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT UNICODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t003_empty" +description = "The empty string, which the example shows as 0. Catches: NULL or an error for an empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], + ["'z'"], +] +query = "SELECT UNICODE(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/upper.toml b/cases/translation/functions/upper.toml new file mode 100644 index 000000000..ea2e26a29 --- /dev/null +++ b/cases/translation/functions/upper.toml @@ -0,0 +1,49 @@ +category = "translation" +construct = "sf.functions.upper" +session_profile = "utc-v1" + +[[case]] +id = "t001_varchar" +description = "UPPER of the page's mixed-script example. Catches: ASCII-only upper-casing, or \u00df kept instead of SS" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'1\u010d2\u04293\u00df4\u0118!-?abc@'"], + ["'\u00c4\u00e4\u00d6\u00f6\u00df\u00dc\u00fc'"], +] +query = "SELECT UPPER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_collation" +description = "UPPER under the Turkish collation, as the page shows. Catches: the collation ignored (I instead of \u0130)" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["'i'"], +] +query = "SELECT UPPER(a COLLATE 'tr') AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_expr" +description = "NULL string. Catches: NULL read as empty" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'a'"], +] +query = "SELECT UPPER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] + +[[case]] +id = "t004_empty" +description = "The empty string. Catches: NULL for the empty string" +columns = [{ name = "a", type = "VARCHAR" }] +rows = [ + ["''"], +] +query = "SELECT UPPER(a) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["boundary", "empty"] diff --git a/cases/translation/functions/uuid-string.toml b/cases/translation/functions/uuid-string.toml new file mode 100644 index 000000000..87366ff2a --- /dev/null +++ b/cases/translation/functions/uuid-string.toml @@ -0,0 +1,39 @@ +category = "translation" +construct = "sf.functions.uuid-string" +session_profile = "utc-v1" + +[[case]] +id = "t001_random" +description = "Properties of the random UUID: length, version and variant digits, lower case, and a new value per call. Catches: a constant folded once per query, another version, or upper case" +columns = [{ name = "k", type = "NUMBER(38,0)" }] +rows = [ + ["1"], + ["2"], +] +query = "SELECT LENGTH(u) AS n, SUBSTR(u, 15, 1) AS ver, SUBSTR(u, 20, 1) IN ('8', '9', 'a', 'b') AS variant, u = LOWER(u) AS lower_case, u <> UUID_STRING() AS fresh FROM (SELECT ordinal, UUID_STRING() AS u FROM input) ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t002_named" +description = "The page's version 5 example, and a second name. Catches: version 3 (MD5) or a random UUID, or the namespace and name swapped" +columns = [{ name = "ns", type = "VARCHAR" }, { name = "nm", type = "VARCHAR" }] +rows = [ + ["'fe971b24-9572-4005-b22f-351e9c09274d'", "'foo'"], + ["'fe971b24-9572-4005-b22f-351e9c09274d'", "'bar'"], +] +query = "SELECT UUID_STRING(ns, nm) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t003_null_name" +description = "NULL name for the version 5 form. Catches: a random UUID or the empty-name UUID instead of NULL" +columns = [{ name = "nm", type = "VARCHAR" }] +rows = [ + ["NULL"], + ["'x'"], +] +query = "SELECT UUID_STRING('fe971b24-9572-4005-b22f-351e9c09274d', nm) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["null"] diff --git a/coverage/translation/obligations/functions.toml b/coverage/translation/obligations/functions.toml index b08731d31..46ff4bcda 100644 --- a/coverage/translation/obligations/functions.toml +++ b/coverage/translation/obligations/functions.toml @@ -172,6 +172,40 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/acosh.toml#t005_below_one"] required = true +[[obligation]] +id = "sf.functions.ascii.t001" +form = "sf.functions.ascii" +kind = "type_combination" +target = "VARCHAR" +note = "ASCII of strings whose first and last characters differ." +catches = "the code of the last character, or the string length, instead of the first character's code" +reading = "the page: is a string; returns the integer code of its first character; \"The value 0 is returned for ... The string is empty\"" +cases = ["cases/translation/functions/ascii.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.ascii.t002" +form = "sf.functions.ascii" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL read as an empty string (0 instead of NULL)" +reading = "the page: is a string; returns the integer code of its first character; \"The value 0 is returned for ... The string is empty\"" +cases = ["cases/translation/functions/ascii.toml#t002_null_input"] +required = true + +[[obligation]] +id = "sf.functions.ascii.t003" +form = "sf.functions.ascii" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string, which the page says gives 0." +catches = "NULL for an empty string instead of 0" +reading = "the page: is a string; returns the integer code of its first character; \"The value 0 is returned for ... The string is empty\"" +cases = ["cases/translation/functions/ascii.toml#t003_empty"] +required = true + [[obligation]] id = "sf.functions.asin.t001" form = "sf.functions.asin" @@ -477,6 +511,274 @@ reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-poi cases = ["cases/translation/functions/atanh.toml#t006_outside"] required = true +[[obligation]] +id = "sf.functions.base64-decode-binary.t001" +form = "sf.functions.base64-decode-binary" +kind = "type_combination" +target = "VARCHAR -> BINARY" +note = "BASE64_DECODE_BINARY of standard Base64 with zero, one and two padding characters." +catches = "a byte-order or sign error in the decoded bytes, a dropped final group, or UTF-8 text read as Latin-1" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-binary.t002" +form = "sf.functions.base64-decode-binary" +kind = "type_combination" +target = "VARCHAR, alphabet -> BINARY" +note = "A custom alphabet for indexes 62 and 63 and the padding character." +catches = "the alphabet argument ignored (an error or other bytes for -_ and ~ padding)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t002_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-binary.t003" +form = "sf.functions.base64-decode-binary" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL decoded as empty instead of NULL" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t003_null_input"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-binary.t004" +form = "sf.functions.base64-decode-binary" +kind = "null_argument" +target = "alphabet" +note = "NULL alphabet next to a set one." +catches = "a NULL alphabet treated as the default (a value instead of NULL)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t004_null_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-binary.t005" +form = "sf.functions.base64-decode-binary" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string decodes to an empty value." +catches = "NULL or an error for empty input" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t005_empty"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-binary.t006" +form = "sf.functions.base64-decode-binary" +kind = "error_parity" +target = "error" +note = "Text that is not Base64." +catches = "NULL (the TRY_ behaviour) where Snowflake raises an error" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns BINARY" +cases = ["cases/translation/functions/base64-decode-binary.toml#t006_invalid"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t001" +form = "sf.functions.base64-decode-string" +kind = "type_combination" +target = "VARCHAR -> VARCHAR" +note = "BASE64_DECODE_STRING of standard Base64 with zero, one and two padding characters." +catches = "a byte-order or sign error in the decoded bytes, a dropped final group, or UTF-8 text read as Latin-1" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t002" +form = "sf.functions.base64-decode-string" +kind = "type_combination" +target = "VARCHAR, alphabet -> VARCHAR" +note = "A custom alphabet for indexes 62 and 63 and the padding character." +catches = "the alphabet argument ignored (an error or other bytes for -_ and ~ padding)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t002_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t003" +form = "sf.functions.base64-decode-string" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL decoded as empty instead of NULL" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t003_null_input"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t004" +form = "sf.functions.base64-decode-string" +kind = "null_argument" +target = "alphabet" +note = "NULL alphabet next to a set one." +catches = "a NULL alphabet treated as the default (a value instead of NULL)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t004_null_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t005" +form = "sf.functions.base64-decode-string" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string decodes to an empty value." +catches = "NULL or an error for empty input" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t005_empty"] +required = true + +[[obligation]] +id = "sf.functions.base64-decode-string.t006" +form = "sf.functions.base64-decode-string" +kind = "error_parity" +target = "error" +note = "Text that is not Base64." +catches = "NULL (the TRY_ behaviour) where Snowflake raises an error" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; returns VARCHAR" +cases = ["cases/translation/functions/base64-decode-string.toml#t006_invalid"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t001" +form = "sf.functions.base64-encode" +kind = "type_combination" +target = "VARCHAR" +note = "BASE64_ENCODE of UTF-8 text of lengths 1, 2 and 3 modulo 3." +catches = "Latin-1 instead of UTF-8 bytes (\u00e9 as one byte), or missing padding" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t002" +form = "sf.functions.base64-encode" +kind = "type_combination" +target = "BINARY" +note = "BASE64_ENCODE of bytes that use indexes 62 and 63." +catches = "the binary read as its hex text (encoding 'FBFF' as characters)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t003" +form = "sf.functions.base64-encode" +kind = "type_combination" +target = "VARCHAR, max_line_length" +note = "A line length shorter than the output." +catches = "max_line_length ignored (no line breaks) or applied to the input length" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t003_max_line_length"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t004" +form = "sf.functions.base64-encode" +kind = "type_combination" +target = "VARCHAR, max_line_length, alphabet" +note = "A custom alphabet with a custom padding character." +catches = "the alphabet argument ignored (+/ and = in the output)" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t004_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t005" +form = "sf.functions.base64-encode" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL encoded as an empty string" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t005_null_input"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t006" +form = "sf.functions.base64-encode" +kind = "null_argument" +target = "max_line_length" +note = "NULL line length next to a set one." +catches = "NULL read as 0 (no line breaks) instead of NULL" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t006_null_max_line_length"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t007" +form = "sf.functions.base64-encode" +kind = "null_argument" +target = "alphabet" +note = "NULL alphabet next to a set one." +catches = "NULL read as the default alphabet" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t007_null_alphabet"] +required = true + +[[obligation]] +id = "sf.functions.base64-encode.t008" +form = "sf.functions.base64-encode" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string encodes to an empty string." +catches = "NULL or padding for empty input" +reading = "the page: is a Base64 string (decode) or a string or binary (encode); sets indexes 62 and 63 and the padding character, positionally; is a positive integer, 0 meaning no line breaks; returns a string" +cases = ["cases/translation/functions/base64-encode.toml#t008_empty"] +required = true + +[[obligation]] +id = "sf.functions.bit-length.t001" +form = "sf.functions.bit-length" +kind = "type_combination" +target = "VARCHAR" +note = "BIT_LENGTH of multi-byte text." +catches = "the character count instead of UTF-8 bits (5 characters, 6 bytes)" +reading = "the page: is a string or binary; the length in bits" +cases = ["cases/translation/functions/bit-length.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.bit-length.t002" +form = "sf.functions.bit-length" +kind = "type_combination" +target = "BINARY" +note = "BIT_LENGTH of binary." +catches = "the hex text length instead of the byte length" +reading = "the page: is a string or binary; the length in bits" +cases = ["cases/translation/functions/bit-length.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.bit-length.t003" +form = "sf.functions.bit-length" +kind = "null_argument" +target = "string_or_binary" +note = "NULL next to a set value." +catches = "NULL read as 0" +reading = "the page: is a string or binary; the length in bits" +cases = ["cases/translation/functions/bit-length.toml#t003_null_string_or_binary"] +required = true + +[[obligation]] +id = "sf.functions.bit-length.t004" +form = "sf.functions.bit-length" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for an empty string" +reading = "the page: is a string or binary; the length in bits" +cases = ["cases/translation/functions/bit-length.toml#t004_empty"] +required = true + [[obligation]] id = "sf.functions.cbrt.t001" form = "sf.functions.cbrt" @@ -624,1653 +926,6936 @@ cases = ["cases/translation/functions/ceil.toml#t008_negatives"] required = true [[obligation]] -id = "sf.functions.cos.t001" -form = "sf.functions.cos" +id = "sf.functions.charindex.t001" +form = "sf.functions.charindex" kind = "type_combination" -target = "FLOAT" -note = "COS of FLOAT values." -catches = "SIN computed, or the argument read in degrees" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t001_float"] +target = "VARCHAR, VARCHAR" +note = "Search for a substring after multi-byte characters." +catches = "arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t001_varchar"] required = true [[obligation]] -id = "sf.functions.cos.t002" -form = "sf.functions.cos" +id = "sf.functions.charindex.t002" +form = "sf.functions.charindex" kind = "type_combination" -target = "NUMBER(12,4)" -note = "COS of NUMBER(12,4), converted to FLOAT." -catches = "SIN computed, or the argument read in degrees; or the result left fixed-point" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t002_fixed"] +target = "VARCHAR, VARCHAR, NUMBER" +note = "A start position past the first match." +catches = "the start position ignored (2 instead of 4) or the result counted from the start position" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t002_start_pos"] required = true [[obligation]] -id = "sf.functions.cos.t003" -form = "sf.functions.cos" -kind = "null_argument" -target = "x" -note = "COS of NULL." -catches = "NULL turned into 0 or NaN" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t003_null"] +id = "sf.functions.charindex.t003" +form = "sf.functions.charindex" +kind = "type_combination" +target = "BINARY, BINARY" +note = "Search in binary values." +catches = "a hex-text offset (3 instead of 2)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t003_binary"] required = true [[obligation]] -id = "sf.functions.cos.t004" -form = "sf.functions.cos" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Large arguments." -catches = "argument reduction differing" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cos.toml#t004_large"] +id = "sf.functions.charindex.t004" +form = "sf.functions.charindex" +kind = "null_argument" +target = "expr1" +note = "NULL needle next to a set one." +catches = "NULL read as an empty needle (1 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t004_null_expr1"] required = true [[obligation]] -id = "sf.functions.cosh.t001" -form = "sf.functions.cosh" -kind = "type_combination" -target = "FLOAT" -note = "COSH of FLOAT values." -catches = "SINH computed (cosh is even, sinh odd)" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t001_float"] +id = "sf.functions.charindex.t005" +form = "sf.functions.charindex" +kind = "null_argument" +target = "expr2" +note = "NULL haystack next to a set one." +catches = "NULL read as an empty string (0 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t005_null_expr2"] required = true [[obligation]] -id = "sf.functions.cosh.t002" -form = "sf.functions.cosh" -kind = "type_combination" -target = "NUMBER(12,4)" -note = "COSH of NUMBER(12,4), converted to FLOAT." -catches = "SINH computed (cosh is even, sinh odd); or the result left fixed-point" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t002_fixed"] +id = "sf.functions.charindex.t006" +form = "sf.functions.charindex" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty needle, which the page says gives 1." +catches = "NULL or 0 for an empty needle" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t006_empty_needle"] required = true [[obligation]] -id = "sf.functions.cosh.t003" -form = "sf.functions.cosh" +id = "sf.functions.charindex.t007" +form = "sf.functions.charindex" kind = "null_argument" -target = "x" -note = "COSH of NULL." -catches = "NULL turned into 0 or NaN" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t003_null"] +target = "start_pos" +note = "NULL start next to a set one." +catches = "NULL read as the default 1" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t007_null_start_pos"] required = true [[obligation]] -id = "sf.functions.cosh.t004" -form = "sf.functions.cosh" +id = "sf.functions.charindex.t008" +form = "sf.functions.charindex" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Arguments just past the double range (710.48)." -catches = "infinity versus error" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cosh.toml#t004_overflow_inf"] +note = "A start position past the end of the string." +catches = "an error or the last match instead of 0" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/charindex.toml#t008_start_past_end"] required = true [[obligation]] -id = "sf.functions.cot.t001" -form = "sf.functions.cot" +id = "sf.functions.chr.t001" +form = "sf.functions.chr" kind = "type_combination" -target = "FLOAT" -note = "COT of FLOAT values." -catches = "TAN computed instead of its reciprocal" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t001_float"] +target = "NUMBER" +note = "CHR of ASCII and non-ASCII code points." +catches = "a byte or Latin-1 conversion (233 and 8364 not giving \u00e9 and \u20ac), or an error past 255" +reading = "the page: is a Unicode code point; returns VARCHAR" +cases = ["cases/translation/functions/chr.toml#t001_number"] required = true [[obligation]] -id = "sf.functions.cot.t002" -form = "sf.functions.cot" +id = "sf.functions.chr.t002" +form = "sf.functions.chr" +kind = "null_argument" +target = "input" +note = "NULL code point next to a set one." +catches = "NULL read as 0 (a NUL character instead of NULL)" +reading = "the page: is a Unicode code point; returns VARCHAR" +cases = ["cases/translation/functions/chr.toml#t002_null_input"] +required = true + +[[obligation]] +id = "sf.functions.chr.t003" +form = "sf.functions.chr" +kind = "error_parity" +target = "error" +note = "A negative code point: records what Snowflake returns there; it moves to boundary if Snowflake returns a value instead of an error." +catches = "an empty string or a wrapped code point where Snowflake raises an error" +reading = "the page: is a Unicode code point; returns VARCHAR" +cases = ["cases/translation/functions/chr.toml#t003_negative"] +required = true + +[[obligation]] +id = "sf.functions.collate.collate.t001" +form = "sf.functions.collate.collate" kind = "type_combination" -target = "NUMBER(12,4)" -note = "COT of NUMBER(12,4), converted to FLOAT." -catches = "TAN computed instead of its reciprocal; or the result left fixed-point" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t002_fixed"] +target = "VARCHAR, 'en-ci'" +note = "Equality under a case-insensitive collation." +catches = "the collation dropped (FALSE for ABC = abc)" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.toml#t001_case_insensitive"] required = true [[obligation]] -id = "sf.functions.cot.t003" -form = "sf.functions.cot" -kind = "null_argument" -target = "x" -note = "COT of NULL." -catches = "NULL turned into 0 or NaN" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t003_null"] +id = "sf.functions.collate.collate.t002" +form = "sf.functions.collate.collate" +kind = "type_combination" +target = "VARCHAR, 'en'" +note = "Ordering under a locale collation." +catches = "binary order kept ('a' < 'B' is FALSE in byte order, TRUE in en)" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.toml#t002_locale_order"] required = true [[obligation]] -id = "sf.functions.cot.t004" -form = "sf.functions.cot" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Very small arguments give large cotangents." -catches = "reciprocal computed in fixed-point" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t004_small"] +id = "sf.functions.collate.collate.t003" +form = "sf.functions.collate.collate" +kind = "type_combination" +target = "VARCHAR, 'en-ci-rtrim'" +note = "The returned string itself, with trailing blanks." +catches = "the string case-folded or trimmed by the copy (the page: \"The string itself is unchanged\")" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.toml#t003_value_unchanged"] required = true [[obligation]] -id = "sf.functions.cot.t005" -form = "sf.functions.cot" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." -catches = "NaN, Infinity, NULL or an error differing from Snowflake" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/cot.toml#t005_pole"] +id = "sf.functions.collate.collate.t004" +form = "sf.functions.collate.collate" +kind = "null_argument" +target = "string_expression" +note = "NULL string next to a set one." +catches = "NULL compared as an empty string" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.toml#t004_null_string_expression"] required = true [[obligation]] -id = "sf.functions.degrees.t001" -form = "sf.functions.degrees" +id = "sf.functions.collate.collate.syntax.t001" +form = "sf.functions.collate.collate.syntax" kind = "type_combination" -target = "FLOAT" -note = "DEGREES of FLOAT values." -catches = "RADIANS computed (the reverse conversion)" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t001_float"] +target = "VARCHAR, 'en-ci'" +note = "Equality under a case-insensitive collation." +catches = "the collation dropped (FALSE for ABC = abc)" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.syntax.toml#t001_case_insensitive"] required = true [[obligation]] -id = "sf.functions.degrees.t002" -form = "sf.functions.degrees" +id = "sf.functions.collate.collate.syntax.t002" +form = "sf.functions.collate.collate.syntax" kind = "type_combination" -target = "NUMBER(12,4)" -note = "DEGREES of NUMBER(12,4), converted to FLOAT." -catches = "RADIANS computed (the reverse conversion); or the result left fixed-point" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t002_fixed"] +target = "VARCHAR, 'en'" +note = "Ordering under a locale collation." +catches = "binary order kept ('a' < 'B' is FALSE in byte order, TRUE in en)" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.syntax.toml#t002_locale_order"] required = true [[obligation]] -id = "sf.functions.degrees.t003" -form = "sf.functions.degrees" -kind = "null_argument" -target = "x" -note = "DEGREES of NULL." -catches = "NULL turned into 0 or NaN" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t003_null"] +id = "sf.functions.collate.collate.syntax.t003" +form = "sf.functions.collate.collate.syntax" +kind = "type_combination" +target = "VARCHAR, 'en-ci-rtrim'" +note = "The returned string itself, with trailing blanks." +catches = "the string case-folded or trimmed by the copy (the page: \"The string itself is unchanged\")" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.syntax.toml#t003_value_unchanged"] required = true [[obligation]] -id = "sf.functions.degrees.t004" -form = "sf.functions.degrees" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Large radian values." -catches = "x*180 computed before dividing by pi, overflowing where the result is finite" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t004_large"] +id = "sf.functions.collate.collate.syntax.t004" +form = "sf.functions.collate.collate.syntax" +kind = "null_argument" +target = "string_expression" +note = "NULL string next to a set one." +catches = "NULL compared as an empty string" +reading = "the page: COLLATE copies a string with a new collation specification; when used as an infix operator the specification must be a constant string" +cases = ["cases/translation/functions/collate.collate.syntax.toml#t004_null_string_expression"] required = true [[obligation]] -id = "sf.functions.degrees.t005" -form = "sf.functions.degrees" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." -catches = "Infinity versus an error" -reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" -cases = ["cases/translation/functions/degrees.toml#t005_overflow"] +id = "sf.functions.collation.t001" +form = "sf.functions.collation" +kind = "type_combination" +target = "VARCHAR" +note = "COLLATION of a collated and an uncollated string." +catches = "a fixed or empty answer, or the collation of the wrong operand" +reading = "the page: returns a VARCHAR holding the collation specification of the expression" +cases = ["cases/translation/functions/collation.toml#t001_collated"] required = true [[obligation]] -id = "sf.functions.div0.t001" -form = "sf.functions.div0" +id = "sf.functions.collation.t002" +form = "sf.functions.collation" +kind = "null_argument" +target = "expression" +note = "NULL collated value." +catches = "NULL propagated (NULL instead of 'de')" +reading = "the page: returns a VARCHAR holding the collation specification of the expression" +cases = ["cases/translation/functions/collation.toml#t002_null_expression"] +required = true + +[[obligation]] +id = "sf.functions.compress.t001" +form = "sf.functions.compress" kind = "type_combination" -target = "NUMBER(38,0)" -note = "DIV0 of integers, as / divides." -catches = "integer division (3 instead of 3.500000)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t001_int"] +target = "VARCHAR, 'SNAPPY'" +note = "COMPRESS of the page's example and of multi-byte text with SNAPPY." +catches = "another codec or framing (the page gives 0920536E6F77666C616B65)" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t001_varchar_snappy"] required = true [[obligation]] -id = "sf.functions.div0.t002" -form = "sf.functions.div0" +id = "sf.functions.compress.t002" +form = "sf.functions.compress" kind = "type_combination" -target = "NUMBER(10,2)" -note = "DIV0 of NUMBER(10,2) values." -catches = "quotient scale differing from /" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t002_dec"] +target = "BINARY, 'SNAPPY'" +note = "COMPRESS of a BINARY value." +catches = "the binary read as its hex text" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t002_binary"] required = true [[obligation]] -id = "sf.functions.div0.t003" -form = "sf.functions.div0" +id = "sf.functions.compress.t003" +form = "sf.functions.compress" kind = "type_combination" -target = "FLOAT" -note = "DIV0 of FLOAT values." -catches = "infinity or NaN for a zero divisor instead of 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t003_float"] +target = "VARCHAR, method" +note = "Each method, with a level and lower-case names, read back through DECOMPRESS_STRING." +catches = "a method name or level rejected, or compress and decompress disagreeing on the codec or its framing" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t003_methods"] required = true [[obligation]] -id = "sf.functions.div0.t004" -form = "sf.functions.div0" +id = "sf.functions.compress.t004" +form = "sf.functions.compress" kind = "null_argument" -target = "dividend" -note = "NULL dividend." -catches = "NULL turned into 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t004_null_a"] +target = "input" +note = "NULL input next to a set one." +catches = "NULL compressed as an empty value" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t004_null_input"] required = true [[obligation]] -id = "sf.functions.div0.t005" -form = "sf.functions.div0" +id = "sf.functions.compress.t005" +form = "sf.functions.compress" kind = "null_argument" -target = "divisor" -note = "NULL divisor." -catches = "a NULL divisor returning 0 (that is DIV0NULL)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t005_null_b"] +target = "method" +note = "NULL method." +catches = "NULL read as a default method" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t005_null_method"] required = true [[obligation]] -id = "sf.functions.div0.t006" -form = "sf.functions.div0" +id = "sf.functions.compress.t006" +form = "sf.functions.compress" kind = "boundary" -subkind = "negative" -target = "negative" -note = "Negative dividends over zero." -catches = "the sign of the dividend kept on the zero result (-0 instead of 0)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" -cases = ["cases/translation/functions/div0.toml#t006_neg_zero"] +subkind = "empty_string" +target = "empty_string" +note = "The empty string with SNAPPY." +catches = "NULL for empty input instead of a length-0 block" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t006_empty"] required = true [[obligation]] -id = "sf.functions.div0null.t001" -form = "sf.functions.div0null" +id = "sf.functions.compress.t007" +form = "sf.functions.compress" +kind = "error_parity" +target = "error" +note = "An unknown method, which the page says fails." +catches = "a fallback codec or NULL instead of an error" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; returns BINARY" +cases = ["cases/translation/functions/compress.toml#t007_unknown_method"] +required = true + +[[obligation]] +id = "sf.functions.concat-ws.t001" +form = "sf.functions.concat-ws" kind = "type_combination" -target = "NUMBER(38,0)" -note = "DIV0NULL of integers, as / divides." -catches = "integer division (3 instead of 3.500000)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t001_int"] +target = "VARCHAR..." +note = "A multi-character separator between three strings." +catches = "the separator after the last argument, the separator taken as the first value, or arguments reordered" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t001_varchar"] required = true [[obligation]] -id = "sf.functions.div0null.t002" -form = "sf.functions.div0null" +id = "sf.functions.concat-ws.t002" +form = "sf.functions.concat-ws" kind = "type_combination" -target = "NUMBER(10,2)" -note = "DIV0NULL of NUMBER(10,2) values." -catches = "quotient scale differing from /" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t002_dec"] +target = "VARCHAR, VARCHAR" +note = "One argument after the separator." +catches = "a separator appended" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t002_single"] required = true [[obligation]] -id = "sf.functions.div0null.t003" -form = "sf.functions.div0null" +id = "sf.functions.concat-ws.t003" +form = "sf.functions.concat-ws" kind = "type_combination" -target = "FLOAT" -note = "DIV0NULL of FLOAT values." -catches = "infinity or NaN for a zero divisor instead of 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t003_float"] +target = "BINARY..." +note = "Binary separator and values." +catches = "hex text concatenated as VARCHAR" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t003_binary"] required = true [[obligation]] -id = "sf.functions.div0null.t004" -form = "sf.functions.div0null" +id = "sf.functions.concat-ws.t004" +form = "sf.functions.concat-ws" kind = "null_argument" -target = "dividend" -note = "NULL dividend." -catches = "NULL turned into 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t004_null_a"] +target = "separator" +note = "NULL separator." +catches = "NULL read as an empty separator (ab instead of NULL)" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t004_null_separator"] required = true [[obligation]] -id = "sf.functions.div0null.t005" -form = "sf.functions.div0null" +id = "sf.functions.concat-ws.t005" +form = "sf.functions.concat-ws" kind = "null_argument" -target = "divisor" -note = "NULL divisor." -catches = "a NULL divisor returning NULL (that is DIV0)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t005_null_b"] +target = "expression" +note = "NULL value between set ones." +catches = "NULL skipped (Postgres CONCAT_WS gives a,c) instead of NULL" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t005_null_expression"] required = true [[obligation]] -id = "sf.functions.div0null.t006" -form = "sf.functions.div0null" +id = "sf.functions.concat-ws.t006" +form = "sf.functions.concat-ws" kind = "boundary" -subkind = "negative" -target = "negative" -note = "Negative dividends over zero." -catches = "a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" -cases = ["cases/translation/functions/div0null.toml#t006_neg_zero"] +subkind = "empty_string" +target = "empty_string" +note = "Empty values and an empty separator." +catches = "empty values dropped (a,c instead of a,,c)" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL; \"CONCAT_WS puts separators between arguments, not after the last\"" +cases = ["cases/translation/functions/concat-ws.toml#t006_empty"] required = true [[obligation]] -id = "sf.functions.exp.t001" -form = "sf.functions.exp" +id = "sf.functions.concat.concat.t001" +form = "sf.functions.concat.concat" kind = "type_combination" -target = "FLOAT" -note = "EXP of FLOAT values." -catches = "LN computed, or e^x read as 10^x" -reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t001_float"] +target = "VARCHAR..." +note = "Three strings in order." +catches = "arguments reordered or a separator inserted" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat.toml#t001_varchar"] required = true [[obligation]] -id = "sf.functions.exp.t002" -form = "sf.functions.exp" +id = "sf.functions.concat.concat.t002" +form = "sf.functions.concat.concat" kind = "type_combination" -target = "NUMBER(12,4)" -note = "EXP of NUMBER(12,4), converted to FLOAT." -catches = "LN computed, or e^x read as 10^x; or the result left fixed-point" -reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t002_fixed"] +target = "BINARY..." +note = "Three binary values." +catches = "the hex texts concatenated as a VARCHAR" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat.toml#t002_binary"] required = true [[obligation]] -id = "sf.functions.exp.t003" -form = "sf.functions.exp" -kind = "type_combination" -target = "DECFLOAT" -note = "EXP of DECFLOAT, which returns DECFLOAT." -catches = "a DECFLOAT argument computed and returned as FLOAT" -reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t003_decfloat"] +id = "sf.functions.concat.concat.t003" +form = "sf.functions.concat.concat" +kind = "null_argument" +target = "expr" +note = "NULL in the middle argument." +catches = "NULL skipped or read as '' (Postgres CONCAT gives 'ac')" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat.toml#t003_null_expr"] required = true [[obligation]] -id = "sf.functions.exp.t004" -form = "sf.functions.exp" -kind = "null_argument" -target = "x" -note = "EXP of NULL." -catches = "NULL turned into 0 or NaN" -reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t004_null"] +id = "sf.functions.concat.concat.t004" +form = "sf.functions.concat.concat" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty strings between set ones." +catches = "an empty string treated as NULL (NULL instead of 'ac')" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat.toml#t004_empty"] required = true [[obligation]] -id = "sf.functions.exp.t005" -form = "sf.functions.exp" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Past the double range." -catches = "infinity versus error" -reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" -cases = ["cases/translation/functions/exp.toml#t005_overflow_inf"] +id = "sf.functions.concat.concat.t005" +form = "sf.functions.concat.concat" +kind = "type_combination" +target = "VARCHAR" +note = "CONCAT of one argument." +catches = "one argument rejected or NULL" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat.toml#t005_single"] required = true [[obligation]] -id = "sf.functions.factorial.t001" -form = "sf.functions.factorial" +id = "sf.functions.concat.concat-subscript-dot.t001" +form = "sf.functions.concat.concat-subscript-dot" kind = "type_combination" -target = "NUMBER(38,0)" -note = "FACTORIAL of integers." -catches = "gamma(x) computed (fractional), or x! off by one (n-1)!" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t001_int"] +target = "VARCHAR..." +note = "Three strings in order." +catches = "arguments reordered or a separator inserted" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat-subscript-dot.toml#t001_varchar"] required = true [[obligation]] -id = "sf.functions.factorial.t002" -form = "sf.functions.factorial" +id = "sf.functions.concat.concat-subscript-dot.t002" +form = "sf.functions.concat.concat-subscript-dot" kind = "type_combination" -target = "NUMBER(10,2)" -note = "FACTORIAL of an integral NUMBER(10,2)." -catches = "scale carried into the result" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t002_dec_int"] +target = "BINARY..." +note = "Three binary values." +catches = "the hex texts concatenated as a VARCHAR" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat-subscript-dot.toml#t002_binary"] required = true [[obligation]] -id = "sf.functions.factorial.t003" -form = "sf.functions.factorial" +id = "sf.functions.concat.concat-subscript-dot.t003" +form = "sf.functions.concat.concat-subscript-dot" kind = "null_argument" -target = "integer_expr" -note = "FACTORIAL of NULL." -catches = "NULL turned into 1" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t003_null"] +target = "expr" +note = "NULL in the middle argument." +catches = "NULL skipped or read as '' (Postgres CONCAT gives 'ac')" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat-subscript-dot.toml#t003_null_expr"] required = true [[obligation]] -id = "sf.functions.factorial.t004" -form = "sf.functions.factorial" +id = "sf.functions.concat.concat-subscript-dot.t004" +form = "sf.functions.concat.concat-subscript-dot" kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Large factorials that still fit 38 digits." -catches = "64-bit overflow (21! does not fit a BIGINT)" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t004_large"] +subkind = "empty_string" +target = "empty_string" +note = "Empty strings between set ones." +catches = "an empty string treated as NULL (NULL instead of 'ac')" +reading = "the page: the inputs must be all strings or all binary; the result has their type; any NULL input gives NULL" +cases = ["cases/translation/functions/concat.concat-subscript-dot.toml#t004_empty"] required = true [[obligation]] -id = "sf.functions.factorial.t005" -form = "sf.functions.factorial" -kind = "error_parity" -target = "error" -note = "A negative argument." -catches = "a gamma value or 1 instead of the error" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t005_negative"] +id = "sf.functions.contains.t001" +form = "sf.functions.contains" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "CONTAINS with the arguments in both orders and in another case." +catches = "arguments swapped (TRUE for CONTAINS(nan, banana)) or a case-insensitive match" +reading = "the page: CONTAINS(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/contains.toml#t001_varchar"] required = true [[obligation]] -id = "sf.functions.factorial.t006" -form = "sf.functions.factorial" -kind = "error_parity" -target = "error" -note = "A result past 38 digits." -catches = "FLOAT or wrapping instead of the error" -reading = "the page names only ; the examples use 0 to 10" -cases = ["cases/translation/functions/factorial.toml#t006_too_big"] +id = "sf.functions.contains.t002" +form = "sf.functions.contains" +kind = "null_argument" +target = "expr1" +note = "NULL first argument." +catches = "NULL read as an empty string (FALSE instead of NULL)" +reading = "the page: CONTAINS(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/contains.toml#t002_null_expr1"] required = true [[obligation]] -id = "sf.functions.floor.t001" -form = "sf.functions.floor" -kind = "type_combination" -target = "NUMBER(38,0)" -note = "FLOOR of integers with a negative scale." -catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t001_int"] +id = "sf.functions.contains.t003" +form = "sf.functions.contains" +kind = "null_argument" +target = "expr2" +note = "NULL second argument." +catches = "NULL read as an empty string (TRUE instead of NULL)" +reading = "the page: CONTAINS(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/contains.toml#t003_null_expr2"] required = true [[obligation]] -id = "sf.functions.floor.t002" -form = "sf.functions.floor" -kind = "type_combination" -target = "NUMBER(12,4)" -note = "FLOOR of NUMBER(12,4) with and without a scale." -catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t002_dec"] +id = "sf.functions.contains.t004" +form = "sf.functions.contains" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty search string, and an empty subject." +catches = "FALSE for the empty search string" +reading = "the page: CONTAINS(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/contains.toml#t004_empty"] required = true [[obligation]] -id = "sf.functions.floor.t003" -form = "sf.functions.floor" +id = "sf.functions.cos.t001" +form = "sf.functions.cos" kind = "type_combination" target = "FLOAT" -note = "FLOOR of FLOAT returns FLOAT." -catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t003_float"] +note = "COS of FLOAT values." +catches = "SIN computed, or the argument read in degrees" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t001_float"] required = true [[obligation]] -id = "sf.functions.floor.t004" -form = "sf.functions.floor" +id = "sf.functions.cos.t002" +form = "sf.functions.cos" kind = "type_combination" -target = "DECFLOAT" -note = "FLOOR of DECFLOAT returns DECFLOAT." -catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t004_decfloat"] +target = "NUMBER(12,4)" +note = "COS of NUMBER(12,4), converted to FLOAT." +catches = "SIN computed, or the argument read in degrees; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t002_fixed"] required = true [[obligation]] -id = "sf.functions.floor.t005" -form = "sf.functions.floor" -kind = "null_argument" -target = "input_expr" -note = "FLOOR of NULL." -catches = "NULL turned into 0" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t005_null_x"] -required = true - -[[obligation]] -id = "sf.functions.floor.t006" -form = "sf.functions.floor" +id = "sf.functions.cos.t003" +form = "sf.functions.cos" kind = "null_argument" -target = "scale_expr" -note = "FLOOR with a NULL scale." -catches = "the NULL scale read as 0" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t006_null_scale"] -required = true - -[[obligation]] -id = "sf.functions.floor.t007" -form = "sf.functions.floor" -kind = "boundary" -subkind = "rounding_scale" -target = "rounding_scale" -note = "Scales past the input scale and far negative." -catches = "scale capped or wrapping" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t007_scale_past"] +target = "x" +note = "COS of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t003_null"] required = true [[obligation]] -id = "sf.functions.floor.t008" -form = "sf.functions.floor" +id = "sf.functions.cos.t004" +form = "sf.functions.cos" kind = "boundary" -subkind = "negative" -target = "negative" -note = "Negative values at the rounding step." -catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" -reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" -cases = ["cases/translation/functions/floor.toml#t008_negatives"] +subkind = "magnitude" +target = "magnitude" +note = "Large arguments." +catches = "argument reduction differing" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cos.toml#t004_large"] required = true [[obligation]] -id = "sf.functions.ln.t001" -form = "sf.functions.ln" +id = "sf.functions.cosh.t001" +form = "sf.functions.cosh" kind = "type_combination" target = "FLOAT" -note = "LN of FLOAT values." -catches = "LOG10 or LOG2 computed" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t001_float"] +note = "COSH of FLOAT values." +catches = "SINH computed (cosh is even, sinh odd)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t001_float"] required = true [[obligation]] -id = "sf.functions.ln.t002" -form = "sf.functions.ln" +id = "sf.functions.cosh.t002" +form = "sf.functions.cosh" kind = "type_combination" target = "NUMBER(12,4)" -note = "LN of NUMBER(12,4), converted to FLOAT." -catches = "LOG10 or LOG2 computed; or the result left fixed-point" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t002_fixed"] -required = true - -[[obligation]] -id = "sf.functions.ln.t003" -form = "sf.functions.ln" -kind = "type_combination" -target = "DECFLOAT" -note = "LN of DECFLOAT, which returns DECFLOAT." -catches = "a DECFLOAT argument computed and returned as FLOAT" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t003_decfloat"] +note = "COSH of NUMBER(12,4), converted to FLOAT." +catches = "SINH computed (cosh is even, sinh odd); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t002_fixed"] required = true [[obligation]] -id = "sf.functions.ln.t004" -form = "sf.functions.ln" +id = "sf.functions.cosh.t003" +form = "sf.functions.cosh" kind = "null_argument" target = "x" -note = "LN of NULL." +note = "COSH of NULL." catches = "NULL turned into 0 or NaN" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t004_null"] +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t003_null"] required = true [[obligation]] -id = "sf.functions.ln.t005" -form = "sf.functions.ln" +id = "sf.functions.cosh.t004" +form = "sf.functions.cosh" kind = "boundary" subkind = "magnitude" target = "magnitude" -note = "Very small positive values." -catches = "underflow to -infinity" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t005_tiny"] +note = "Arguments just past the double range (710.48)." +catches = "infinity versus error" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cosh.toml#t004_overflow_inf"] required = true [[obligation]] -id = "sf.functions.ln.t006" -form = "sf.functions.ln" -kind = "error_parity" -target = "error" -note = "LN of 0 or a negative value is an error." -catches = "NULL, NaN or -infinity instead of the error" -reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" -cases = ["cases/translation/functions/ln.toml#t006_zero"] +id = "sf.functions.cot.t001" +form = "sf.functions.cot" +kind = "type_combination" +target = "FLOAT" +note = "COT of FLOAT values." +catches = "TAN computed instead of its reciprocal" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t001_float"] required = true [[obligation]] -id = "sf.functions.log.t001" -form = "sf.functions.log" +id = "sf.functions.cot.t002" +form = "sf.functions.cot" kind = "type_combination" -target = "NUMBER(38,0)" -note = "LOG of integers." -catches = "arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t001_int"] +target = "NUMBER(12,4)" +note = "COT of NUMBER(12,4), converted to FLOAT." +catches = "TAN computed instead of its reciprocal; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t002_fixed"] required = true [[obligation]] -id = "sf.functions.log.t002" -form = "sf.functions.log" -kind = "type_combination" -target = "NUMBER(10,2)" -note = "LOG of fixed-point values." -catches = "base rounded to an integer" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t002_dec"] +id = "sf.functions.cot.t003" +form = "sf.functions.cot" +kind = "null_argument" +target = "x" +note = "COT of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t003_null"] required = true [[obligation]] -id = "sf.functions.log.t003" -form = "sf.functions.log" -kind = "type_combination" -target = "FLOAT" -note = "LOG of FLOAT values." -catches = "arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t003_float"] +id = "sf.functions.cot.t004" +form = "sf.functions.cot" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very small arguments give large cotangents." +catches = "reciprocal computed in fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t004_small"] required = true [[obligation]] -id = "sf.functions.log.t004" -form = "sf.functions.log" +id = "sf.functions.cot.t005" +form = "sf.functions.cot" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "COT at or outside its domain: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, Infinity, NULL or an error differing from Snowflake" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/cot.toml#t005_pole"] +required = true + +[[obligation]] +id = "sf.functions.decompress-binary.t001" +form = "sf.functions.decompress-binary" kind = "type_combination" -target = "NUMBER(38,0), FLOAT" -note = "LOG with an integer base and a FLOAT value." -catches = "type promotion differing" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t004_mixed"] +target = "BINARY, 'SNAPPY'" +note = "DECOMPRESS_BINARY of fixed Snappy blocks, one from the page." +catches = "another codec or a framing offset (the varint length read as data)" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t001_snappy"] required = true [[obligation]] -id = "sf.functions.log.t005" -form = "sf.functions.log" -kind = "null_argument" -target = "base" -note = "NULL base." -catches = "NULL turned into e or 10" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t005_null_base"] +id = "sf.functions.decompress-binary.t002" +form = "sf.functions.decompress-binary" +kind = "type_combination" +target = "BINARY, 'snappy(3)'" +note = "A lower-case method name with a level, which the page says is ignored." +catches = "the level or the lower-case name rejected" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t002_method_level"] required = true [[obligation]] -id = "sf.functions.log.t006" -form = "sf.functions.log" +id = "sf.functions.decompress-binary.t003" +form = "sf.functions.decompress-binary" kind = "null_argument" -target = "expr" -note = "NULL value." -catches = "NULL turned into 1" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t006_null_expr"] +target = "input" +note = "NULL input next to a set one." +catches = "NULL decompressed as an empty value" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t003_null_input"] required = true [[obligation]] -id = "sf.functions.log.t007" -form = "sf.functions.log" -kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "A base between 0 and 1 gives negative logs of values above 1." -catches = "the sign lost" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t007_base_below_one"] +id = "sf.functions.decompress-binary.t004" +form = "sf.functions.decompress-binary" +kind = "null_argument" +target = "method" +note = "NULL method." +catches = "NULL read as a default method" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t004_null_method"] required = true [[obligation]] -id = "sf.functions.log.t008" -form = "sf.functions.log" +id = "sf.functions.decompress-binary.t005" +form = "sf.functions.decompress-binary" kind = "error_parity" target = "error" -note = "A base of 1 is an error." -catches = "infinity or NULL instead of the error" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t008_base_one"] +note = "Bytes that are not a valid Snappy block." +catches = "NULL or partial output instead of an error" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t005_corrupt"] required = true [[obligation]] -id = "sf.functions.log.t009" -form = "sf.functions.log" +id = "sf.functions.decompress-binary.t006" +form = "sf.functions.decompress-binary" kind = "error_parity" target = "error" -note = "A negative base is an error." -catches = "NaN instead of the error" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t009_base_neg"] +note = "An unknown method." +catches = "a fallback codec instead of an error" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns BINARY" +cases = ["cases/translation/functions/decompress-binary.toml#t006_unknown_method"] required = true [[obligation]] -id = "sf.functions.log.t010" -form = "sf.functions.log" -kind = "error_parity" -target = "error" -note = "A value of 0 is an error." -catches = "-infinity instead of the error" -reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" -cases = ["cases/translation/functions/log.toml#t010_expr_zero"] +id = "sf.functions.decompress-string.t001" +form = "sf.functions.decompress-string" +kind = "type_combination" +target = "BINARY, 'SNAPPY'" +note = "DECOMPRESS_STRING of fixed Snappy blocks, one from the page." +catches = "another codec or a framing offset (the varint length read as data)" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t001_snappy"] required = true [[obligation]] -id = "sf.functions.mod.t001" -form = "sf.functions.mod" +id = "sf.functions.decompress-string.t002" +form = "sf.functions.decompress-string" kind = "type_combination" -target = "NUMBER(38,0)" -note = "MOD of integers with negatives." -catches = "floor-based modulo (MOD(-7, 3) is -1, not 2)" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t001_int"] +target = "BINARY, 'snappy(3)'" +note = "A lower-case method name with a level, which the page says is ignored." +catches = "the level or the lower-case name rejected" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t002_method_level"] required = true [[obligation]] -id = "sf.functions.mod.t002" -form = "sf.functions.mod" -kind = "type_combination" -target = "NUMBER(10,2)" -note = "MOD of NUMBER(10,2) values." -catches = "fractional remainder dropped" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t002_dec"] +id = "sf.functions.decompress-string.t003" +form = "sf.functions.decompress-string" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL decompressed as an empty value" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t003_null_input"] required = true [[obligation]] -id = "sf.functions.mod.t003" -form = "sf.functions.mod" -kind = "type_combination" -target = "NUMBER(10,2), NUMBER(38,0)" -note = "MOD of a scaled and an integer operand." -catches = "result scale from one side" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t003_dec_int"] +id = "sf.functions.decompress-string.t004" +form = "sf.functions.decompress-string" +kind = "null_argument" +target = "method" +note = "NULL method." +catches = "NULL read as a default method" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t004_null_method"] required = true [[obligation]] -id = "sf.functions.mod.t004" -form = "sf.functions.mod" +id = "sf.functions.decompress-string.t005" +form = "sf.functions.decompress-string" +kind = "error_parity" +target = "error" +note = "Bytes that are not a valid Snappy block." +catches = "NULL or partial output instead of an error" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t005_corrupt"] +required = true + +[[obligation]] +id = "sf.functions.decompress-string.t006" +form = "sf.functions.decompress-string" +kind = "error_parity" +target = "error" +note = "An unknown method." +catches = "a fallback codec instead of an error" +reading = "the page: is BINARY or a string; is SNAPPY, ZLIB, ZSTD or BZ2 with an optional level, case-insensitive; an unknown method fails; the level is ignored; returns VARCHAR" +cases = ["cases/translation/functions/decompress-string.toml#t006_unknown_method"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t001" +form = "sf.functions.degrees" kind = "type_combination" target = "FLOAT" -note = "MOD of FLOAT values." -catches = "floor-based float modulo" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t004_float"] +note = "DEGREES of FLOAT values." +catches = "RADIANS computed (the reverse conversion)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t001_float"] required = true [[obligation]] -id = "sf.functions.mod.t005" -form = "sf.functions.mod" +id = "sf.functions.degrees.t002" +form = "sf.functions.degrees" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "DEGREES of NUMBER(12,4), converted to FLOAT." +catches = "RADIANS computed (the reverse conversion); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t002_fixed"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t003" +form = "sf.functions.degrees" kind = "null_argument" -target = "expr1" +target = "x" +note = "DEGREES of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t003_null"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t004" +form = "sf.functions.degrees" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large radian values." +catches = "x*180 computed before dividing by pi, overflowing where the result is finite" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t004_large"] +required = true + +[[obligation]] +id = "sf.functions.degrees.t005" +form = "sf.functions.degrees" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "A result past the double range: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "Infinity versus an error" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/degrees.toml#t005_overflow"] +required = true + +[[obligation]] +id = "sf.functions.div0.t001" +form = "sf.functions.div0" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "DIV0 of integers, as / divides." +catches = "integer division (3 instead of 3.500000)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.div0.t002" +form = "sf.functions.div0" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "DIV0 of NUMBER(10,2) values." +catches = "quotient scale differing from /" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.div0.t003" +form = "sf.functions.div0" +kind = "type_combination" +target = "FLOAT" +note = "DIV0 of FLOAT values." +catches = "infinity or NaN for a zero divisor instead of 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.div0.t004" +form = "sf.functions.div0" +kind = "null_argument" +target = "dividend" note = "NULL dividend." -catches = "NULL treated as 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t005_null_a"] +catches = "NULL turned into 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t004_null_a"] required = true [[obligation]] -id = "sf.functions.mod.t006" -form = "sf.functions.mod" +id = "sf.functions.div0.t005" +form = "sf.functions.div0" kind = "null_argument" -target = "expr2" +target = "divisor" note = "NULL divisor." -catches = "NULL treated as 0" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t006_null_b"] +catches = "a NULL divisor returning 0 (that is DIV0NULL)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t005_null_b"] required = true [[obligation]] -id = "sf.functions.mod.t007" -form = "sf.functions.mod" +id = "sf.functions.div0.t006" +form = "sf.functions.div0" kind = "boundary" subkind = "negative" target = "negative" -note = "Argument order with negatives." -catches = "arguments swapped" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t007_order"] +note = "Negative dividends over zero." +catches = "the sign of the dividend kept on the zero result (-0 instead of 0)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0 returns 0 when the divisor is 0" +cases = ["cases/translation/functions/div0.toml#t006_neg_zero"] required = true [[obligation]] -id = "sf.functions.mod.t008" -form = "sf.functions.mod" +id = "sf.functions.div0null.t001" +form = "sf.functions.div0null" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "DIV0NULL of integers, as / divides." +catches = "integer division (3 instead of 3.500000)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t002" +form = "sf.functions.div0null" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "DIV0NULL of NUMBER(10,2) values." +catches = "quotient scale differing from /" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t003" +form = "sf.functions.div0null" +kind = "type_combination" +target = "FLOAT" +note = "DIV0NULL of FLOAT values." +catches = "infinity or NaN for a zero divisor instead of 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t004" +form = "sf.functions.div0null" +kind = "null_argument" +target = "dividend" +note = "NULL dividend." +catches = "NULL turned into 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t004_null_a"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t005" +form = "sf.functions.div0null" +kind = "null_argument" +target = "divisor" +note = "NULL divisor." +catches = "a NULL divisor returning NULL (that is DIV0)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t005_null_b"] +required = true + +[[obligation]] +id = "sf.functions.div0null.t006" +form = "sf.functions.div0null" kind = "boundary" -subkind = "magnitude" -target = "magnitude" -note = "Remainders of 38-digit integers." -catches = "64-bit modulo" -reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" -cases = ["cases/translation/functions/mod.toml#t008_max"] +subkind = "negative" +target = "negative" +note = "Negative dividends over zero." +catches = "a sign-keeping or FLOAT path giving -0 or -1e-10 where the quotient underflows the scale" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; DIV0NULL returns 0 when the divisor is 0 or NULL" +cases = ["cases/translation/functions/div0null.toml#t006_neg_zero"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t001" +form = "sf.functions.editdistance" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Levenshtein distances that differ from Hamming and LCS distances." +catches = "Hamming or LCS distance, a byte-based distance (2 for caf\u00e9/cafe), or transpositions counted as 1" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t002" +form = "sf.functions.editdistance" +kind = "type_combination" +target = "VARCHAR, VARCHAR, NUMBER" +note = "A maximum below and above the distance." +catches = "the maximum ignored (3 instead of 2) or applied when above the distance" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t002_max_distance"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t003" +form = "sf.functions.editdistance" +kind = "null_argument" +target = "string_expr1" +note = "NULL first string." +catches = "NULL read as an empty string (the other length)" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t003_null_string_expr1"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t004" +form = "sf.functions.editdistance" +kind = "null_argument" +target = "string_expr2" +note = "NULL second string." +catches = "NULL read as an empty string" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t004_null_string_expr2"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t005" +form = "sf.functions.editdistance" +kind = "null_argument" +target = "max_distance" +note = "NULL maximum." +catches = "NULL read as no maximum (the full distance instead of NULL)" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t005_null_max_distance"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t006" +form = "sf.functions.editdistance" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative maximum, which the page says gives 0." +catches = "the negative maximum returned or ignored" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t006_negative_max"] +required = true + +[[obligation]] +id = "sf.functions.editdistance.t007" +form = "sf.functions.editdistance" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty string against a set one." +catches = "NULL or 0 instead of the other length" +reading = "the page: two strings and an optional integer ; a negative maximum gives 0; the result is LEAST(distance, max_distance)" +cases = ["cases/translation/functions/editdistance.toml#t007_empty"] +required = true + +[[obligation]] +id = "sf.functions.endswith.t001" +form = "sf.functions.endswith" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "ENDSWITH with the arguments in both orders and in another case." +catches = "arguments swapped (the page's own Returns text reads them reversed) or a case-insensitive match" +reading = "the page: ENDSWITH(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/endswith.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.endswith.t002" +form = "sf.functions.endswith" +kind = "null_argument" +target = "expr1" +note = "NULL first argument." +catches = "NULL read as an empty string (FALSE instead of NULL)" +reading = "the page: ENDSWITH(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/endswith.toml#t002_null_expr1"] +required = true + +[[obligation]] +id = "sf.functions.endswith.t003" +form = "sf.functions.endswith" +kind = "null_argument" +target = "expr2" +note = "NULL second argument." +catches = "NULL read as an empty string (TRUE instead of NULL)" +reading = "the page: ENDSWITH(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/endswith.toml#t003_null_expr2"] +required = true + +[[obligation]] +id = "sf.functions.endswith.t004" +form = "sf.functions.endswith" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty search string, and an empty subject." +catches = "FALSE for the empty search string" +reading = "the page: ENDSWITH(, ) tests against ; NULL if either is NULL" +cases = ["cases/translation/functions/endswith.toml#t004_empty"] +required = true + +[[obligation]] +id = "sf.functions.exp.t001" +form = "sf.functions.exp" +kind = "type_combination" +target = "FLOAT" +note = "EXP of FLOAT values." +catches = "LN computed, or e^x read as 10^x" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t001_float"] +required = true + +[[obligation]] +id = "sf.functions.exp.t002" +form = "sf.functions.exp" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "EXP of NUMBER(12,4), converted to FLOAT." +catches = "LN computed, or e^x read as 10^x; or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t002_fixed"] +required = true + +[[obligation]] +id = "sf.functions.exp.t003" +form = "sf.functions.exp" +kind = "type_combination" +target = "DECFLOAT" +note = "EXP of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t003_decfloat"] +required = true + +[[obligation]] +id = "sf.functions.exp.t004" +form = "sf.functions.exp" +kind = "null_argument" +target = "x" +note = "EXP of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t004_null"] +required = true + +[[obligation]] +id = "sf.functions.exp.t005" +form = "sf.functions.exp" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Past the double range." +catches = "infinity versus error" +reading = "the page: \"The data type must be FLOAT or DECFLOAT.\" DECFLOAT returns DECFLOAT, otherwise FLOAT" +cases = ["cases/translation/functions/exp.toml#t005_overflow_inf"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t001" +form = "sf.functions.factorial" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "FACTORIAL of integers." +catches = "gamma(x) computed (fractional), or x! off by one (n-1)!" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t002" +form = "sf.functions.factorial" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "FACTORIAL of an integral NUMBER(10,2)." +catches = "scale carried into the result" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t002_dec_int"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t003" +form = "sf.functions.factorial" +kind = "null_argument" +target = "integer_expr" +note = "FACTORIAL of NULL." +catches = "NULL turned into 1" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t003_null"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t004" +form = "sf.functions.factorial" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large factorials that still fit 38 digits." +catches = "64-bit overflow (21! does not fit a BIGINT)" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t004_large"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t005" +form = "sf.functions.factorial" +kind = "error_parity" +target = "error" +note = "A negative argument." +catches = "a gamma value or 1 instead of the error" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t005_negative"] +required = true + +[[obligation]] +id = "sf.functions.factorial.t006" +form = "sf.functions.factorial" +kind = "error_parity" +target = "error" +note = "A result past 38 digits." +catches = "FLOAT or wrapping instead of the error" +reading = "the page names only ; the examples use 0 to 10" +cases = ["cases/translation/functions/factorial.toml#t006_too_big"] +required = true + +[[obligation]] +id = "sf.functions.floor.t001" +form = "sf.functions.floor" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "FLOOR of integers with a negative scale." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a negative scale ignored" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.floor.t002" +form = "sf.functions.floor" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "FLOOR of NUMBER(12,4) with and without a scale." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.floor.t003" +form = "sf.functions.floor" +kind = "type_combination" +target = "FLOAT" +note = "FLOOR of FLOAT returns FLOAT." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3); or a NUMBER returned" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.floor.t004" +form = "sf.functions.floor" +kind = "type_combination" +target = "DECFLOAT" +note = "FLOOR of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT: the fraction of a 20-digit value lost, or 0.29 at scale 2 off by one ulp" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t004_decfloat"] +required = true + +[[obligation]] +id = "sf.functions.floor.t005" +form = "sf.functions.floor" +kind = "null_argument" +target = "input_expr" +note = "FLOOR of NULL." +catches = "NULL turned into 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t005_null_x"] +required = true + +[[obligation]] +id = "sf.functions.floor.t006" +form = "sf.functions.floor" +kind = "null_argument" +target = "scale_expr" +note = "FLOOR with a NULL scale." +catches = "the NULL scale read as 0" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t006_null_scale"] +required = true + +[[obligation]] +id = "sf.functions.floor.t007" +form = "sf.functions.floor" +kind = "boundary" +subkind = "rounding_scale" +target = "rounding_scale" +note = "Scales past the input scale and far negative." +catches = "scale capped or wrapping" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t007_scale_past"] +required = true + +[[obligation]] +id = "sf.functions.floor.t008" +form = "sf.functions.floor" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative values at the rounding step." +catches = "CEIL or truncation used (FLOOR(-2.1) is -3)" +reading = "the page: input_expr \"must be one of the numeric data types, such as DECFLOAT, FLOAT, or NUMBER\"; scale_expr an integer, default 0, negative allowed; the result keeps the input type" +cases = ["cases/translation/functions/floor.toml#t008_negatives"] +required = true + +[[obligation]] +id = "sf.functions.hash.t001" +form = "sf.functions.hash" +kind = "type_combination" +target = "NUMBER" +note = "HASH of integers, compared with Snowflake's own values." +catches = "another hash function or seed" +reading = "the page: HASH takes expressions of any type and returns NUMBER(19,0); it never returns NULL; equal NUMBERs hash equally across precision and scale, and so do FLOATs exactly convertible to NUMBER(38,0)" +cases = ["cases/translation/functions/hash.toml#t001_number"] +required = true + +[[obligation]] +id = "sf.functions.hash.t002" +form = "sf.functions.hash" +kind = "type_combination" +target = "VARCHAR" +note = "HASH of strings, including multi-byte." +catches = "another hash or a Latin-1 encoding" +reading = "the page: HASH takes expressions of any type and returns NUMBER(19,0); it never returns NULL; equal NUMBERs hash equally across precision and scale, and so do FLOATs exactly convertible to NUMBER(38,0)" +cases = ["cases/translation/functions/hash.toml#t002_varchar"] +required = true + +[[obligation]] +id = "sf.functions.hash.t003" +form = "sf.functions.hash" +kind = "type_combination" +target = "NUMBER(10,2), FLOAT" +note = "HASH of 10 as NUMBER(38,0), NUMBER(10,2) and FLOAT, which the page says agree." +catches = "the scale or the FLOAT bits hashed (FALSE)" +reading = "the page: HASH takes expressions of any type and returns NUMBER(19,0); it never returns NULL; equal NUMBERs hash equally across precision and scale, and so do FLOATs exactly convertible to NUMBER(38,0)" +cases = ["cases/translation/functions/hash.toml#t003_scale_stable"] +required = true + +[[obligation]] +id = "sf.functions.hash.t004" +form = "sf.functions.hash" +kind = "type_combination" +target = "VARCHAR, NUMBER" +note = "HASH of two arguments in both orders." +catches = "arguments combined order-independently" +reading = "the page: HASH takes expressions of any type and returns NUMBER(19,0); it never returns NULL; equal NUMBERs hash equally across precision and scale, and so do FLOATs exactly convertible to NUMBER(38,0)" +cases = ["cases/translation/functions/hash.toml#t004_multi"] +required = true + +[[obligation]] +id = "sf.functions.hash.t005" +form = "sf.functions.hash" +kind = "null_argument" +target = "expr" +note = "NULL input, which the page says still hashes." +catches = "NULL propagated instead of a hash value" +reading = "the page: HASH takes expressions of any type and returns NUMBER(19,0); it never returns NULL; equal NUMBERs hash equally across precision and scale, and so do FLOATs exactly convertible to NUMBER(38,0)" +cases = ["cases/translation/functions/hash.toml#t005_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg.t001" +form = "sf.functions.hash-agg.hash-agg" +kind = "type_combination" +target = "NUMBER" +note = "HASH_AGG over varied rows with a duplicate." +catches = "another per-row hash or combining function" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL" +cases = ["cases/translation/functions/hash-agg.hash-agg.toml#t001_number"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg.t002" +form = "sf.functions.hash-agg.hash-agg" +kind = "type_combination" +target = "DISTINCT NUMBER" +note = "HASH_AGG(DISTINCT) against the plain aggregate on rows with duplicates." +catches = "DISTINCT ignored (the two values equal)" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL" +cases = ["cases/translation/functions/hash-agg.hash-agg.toml#t002_distinct"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg.t003" +form = "sf.functions.hash-agg.hash-agg" +kind = "type_combination" +target = "VARCHAR, NUMBER" +note = "Two columns holding the same values in different row orders, and both column orders." +catches = "a row-order-sensitive fold (r1 FALSE) or a column-order-insensitive one (r2 TRUE)" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL" +cases = ["cases/translation/functions/hash-agg.hash-agg.toml#t003_row_order"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg.t004" +form = "sf.functions.hash-agg.hash-agg" +kind = "null_argument" +target = "expr" +note = "NULL rows, which the page says are not ignored." +catches = "NULL rows skipped (equal to the aggregate without them) or a NULL result" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL" +cases = ["cases/translation/functions/hash-agg.hash-agg.toml#t004_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg.t005" +form = "sf.functions.hash-agg.hash-agg" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "HASH_AGG over no rows." +catches = "NULL for an empty input (the page: never NULL)" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL" +cases = ["cases/translation/functions/hash-agg.hash-agg.toml#t005_no_rows"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg-over.t001" +form = "sf.functions.hash-agg.hash-agg-over" +kind = "type_combination" +target = "NUMBER OVER (PARTITION BY)" +note = "HASH_AGG per partition with partitions of equal multisets." +catches = "the whole input hashed instead of the partition, or partitions with the same rows hashed differently" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL; as a window function partitioned by " +cases = ["cases/translation/functions/hash-agg.hash-agg-over.toml#t001_partition"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg-over.t002" +form = "sf.functions.hash-agg.hash-agg-over" +kind = "type_combination" +target = "NUMBER OVER ()" +note = "An empty OVER clause." +catches = "a running (ordered) hash instead of the whole-input value" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL; as a window function partitioned by " +cases = ["cases/translation/functions/hash-agg.hash-agg-over.toml#t002_no_partition"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg-over.t003" +form = "sf.functions.hash-agg.hash-agg-over" +kind = "null_argument" +target = "expr" +note = "NULL values inside a partition." +catches = "NULLs skipped within the window" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL; as a window function partitioned by " +cases = ["cases/translation/functions/hash-agg.hash-agg-over.toml#t003_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg-over.syntax.t001" +form = "sf.functions.hash-agg.hash-agg-over.syntax" +kind = "type_combination" +target = "* OVER (PARTITION BY)" +note = "HASH_AGG(*) per partition." +catches = "the star expanded to a different column set or order (the ordinal column included or dropped)" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL; HASH_AGG(*) hashes every column of the row" +cases = ["cases/translation/functions/hash-agg.hash-agg-over.syntax.toml#t001_star"] +required = true + +[[obligation]] +id = "sf.functions.hash-agg.hash-agg-over.syntax.t002" +form = "sf.functions.hash-agg.hash-agg-over.syntax" +kind = "null_argument" +target = "expr3" +note = "NULL partition key." +catches = "NULL-keyed rows dropped or merged with another partition" +reading = "the page: HASH_AGG hashes rows with HASH, does not ignore NULLs, is order-insensitive across rows but sensitive to column order, and never returns NULL; HASH_AGG(*) hashes every column of the row" +cases = ["cases/translation/functions/hash-agg.hash-agg-over.syntax.toml#t002_null_expr3"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-binary.t001" +form = "sf.functions.hex-decode-binary" +kind = "type_combination" +target = "VARCHAR -> BINARY" +note = "HEX_DECODE_BINARY of upper- and lower-case hex digits." +catches = "lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns BINARY" +cases = ["cases/translation/functions/hex-decode-binary.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-binary.t002" +form = "sf.functions.hex-decode-binary" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL decoded as empty" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns BINARY" +cases = ["cases/translation/functions/hex-decode-binary.toml#t002_null_input"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-binary.t003" +form = "sf.functions.hex-decode-binary" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL or an error for empty input" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns BINARY" +cases = ["cases/translation/functions/hex-decode-binary.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-binary.t004" +form = "sf.functions.hex-decode-binary" +kind = "error_parity" +target = "error" +note = "A non-hex digit." +catches = "NULL (the TRY_ behaviour) or a partial decode where Snowflake raises an error" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns BINARY" +cases = ["cases/translation/functions/hex-decode-binary.toml#t004_invalid"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-binary.t005" +form = "sf.functions.hex-decode-binary" +kind = "error_parity" +target = "error" +note = "An odd number of hex digits." +catches = "the last nibble dropped or zero-padded instead of an error" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns BINARY" +cases = ["cases/translation/functions/hex-decode-binary.toml#t005_odd_length"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-string.t001" +form = "sf.functions.hex-decode-string" +kind = "type_combination" +target = "VARCHAR -> VARCHAR" +note = "HEX_DECODE_STRING of upper- and lower-case hex digits." +catches = "lower-case digits rejected, nibble order swapped, or UTF-8 read as Latin-1" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns VARCHAR" +cases = ["cases/translation/functions/hex-decode-string.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-string.t002" +form = "sf.functions.hex-decode-string" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL decoded as empty" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns VARCHAR" +cases = ["cases/translation/functions/hex-decode-string.toml#t002_null_input"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-string.t003" +form = "sf.functions.hex-decode-string" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL or an error for empty input" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns VARCHAR" +cases = ["cases/translation/functions/hex-decode-string.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-string.t004" +form = "sf.functions.hex-decode-string" +kind = "error_parity" +target = "error" +note = "A non-hex digit." +catches = "NULL (the TRY_ behaviour) or a partial decode where Snowflake raises an error" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns VARCHAR" +cases = ["cases/translation/functions/hex-decode-string.toml#t004_invalid"] +required = true + +[[obligation]] +id = "sf.functions.hex-decode-string.t005" +form = "sf.functions.hex-decode-string" +kind = "error_parity" +target = "error" +note = "An odd number of hex digits." +catches = "the last nibble dropped or zero-padded instead of an error" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); returns VARCHAR" +cases = ["cases/translation/functions/hex-decode-string.toml#t005_odd_length"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t001" +form = "sf.functions.hex-encode" +kind = "type_combination" +target = "VARCHAR" +note = "HEX_ENCODE of multi-byte text." +catches = "Latin-1 bytes (E9 for \u00e9) or lower-case digits by default" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t002" +form = "sf.functions.hex-encode" +kind = "type_combination" +target = "BINARY" +note = "HEX_ENCODE of bytes including 00 and FF." +catches = "the leading zero byte dropped or sign-extended" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t003" +form = "sf.functions.hex-encode" +kind = "type_combination" +target = "VARCHAR, case" +note = "The case argument 0 and 1." +catches = "the case argument ignored or inverted" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t003_case"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t004" +form = "sf.functions.hex-encode" +kind = "null_argument" +target = "input" +note = "NULL input next to a set one." +catches = "NULL encoded as an empty string" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t004_null_input"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t005" +form = "sf.functions.hex-encode" +kind = "null_argument" +target = "case" +note = "NULL case next to a set one." +catches = "NULL read as the default (upper case) instead of NULL" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t005_null_case"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t006" +form = "sf.functions.hex-encode" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for empty input" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t006_empty"] +required = true + +[[obligation]] +id = "sf.functions.hex-encode.t007" +form = "sf.functions.hex-encode" +kind = "error_parity" +target = "error" +note = "A case value other than 0 or 1." +catches = "any non-zero value read as upper case instead of an error" +reading = "the page: is a string of hexadecimal digits (decode) or a binary or string (encode); 1 (default) gives upper case and 0 lower case; \"All other values are illegal and result in an error\"" +cases = ["cases/translation/functions/hex-encode.toml#t007_bad_case"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t001" +form = "sf.functions.ilike-any" +kind = "type_combination" +target = "VARCHAR, (VARCHAR, VARCHAR)" +note = "ILIKE ANY where one, both or neither pattern matches." +catches = "ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t001_patterns"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t002" +form = "sf.functions.ilike-any" +kind = "type_combination" +target = "VARCHAR" +note = "Patterns in another case." +catches = "case sensitivity reversed (FALSE for ABC)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t003" +form = "sf.functions.ilike-any" +kind = "type_combination" +target = "ILIKE ANY ... ESCAPE" +note = "An escape character before %." +catches = "the escape ignored (50 matches 5!%)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t003_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t004" +form = "sf.functions.ilike-any" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t004_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t005" +form = "sf.functions.ilike-any" +kind = "null_argument" +target = "pattern" +note = "One NULL pattern beside a matching and a non-matching one." +catches = "a NULL pattern making the result NULL even when the other pattern matches, or NULL matching NULL" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t005_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.ilike-any.t006" +form = "sf.functions.ilike-any" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty subject against an empty and a % pattern." +catches = "the empty pattern matching a non-empty subject" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/ilike-any.toml#t006_empty"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t001" +form = "sf.functions.ilike.ilike" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The _ and % wildcards, anchored at both ends." +catches = "an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t001_wildcards"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t002" +form = "sf.functions.ilike.ilike" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Letters in another case, including non-ASCII." +catches = "case-sensitive matching (FALSE for ABC against abc), or ASCII-only folding (\u00c9 against \u00e9)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t003" +form = "sf.functions.ilike.ilike" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A newline matched by _ and %, which the page documents." +catches = "wildcards stopping at a newline" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t003_newline"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t004" +form = "sf.functions.ilike.ilike" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A backslash in the pattern with no ESCAPE clause." +catches = "backslash used as a default escape (a\\b would then match only ab)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t004_no_default_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t005" +form = "sf.functions.ilike.ilike" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t005_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t006" +form = "sf.functions.ilike.ilike" +kind = "null_argument" +target = "pattern" +note = "NULL pattern, and NULL against NULL." +catches = "NULL matching NULL (TRUE) or NULL read as an empty pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t006_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t007" +form = "sf.functions.ilike.ilike" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty subject and pattern." +catches = "the empty pattern matching every string" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t007_empty"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t008" +form = "sf.functions.ilike.ilike" +kind = "type_combination" +target = "VARCHAR, VARCHAR, VARCHAR" +note = "An escape character before % and _." +catches = "the escape ignored" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t008_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.t009" +form = "sf.functions.ilike.ilike" +kind = "null_argument" +target = "escape" +note = "NULL escape." +catches = "NULL read as no escape" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.toml#t009_null_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t001" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The _ and % wildcards, anchored at both ends." +catches = "an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t001_wildcards"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t002" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Letters in another case, including non-ASCII." +catches = "case-sensitive matching (FALSE for ABC against abc), or ASCII-only folding (\u00c9 against \u00e9)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t003" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A newline matched by _ and %, which the page documents." +catches = "wildcards stopping at a newline" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t003_newline"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t004" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A backslash in the pattern with no ESCAPE clause." +catches = "backslash used as a default escape (a\\b would then match only ab)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t004_no_default_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t005" +form = "sf.functions.ilike.ilike.syntax" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t005_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t006" +form = "sf.functions.ilike.ilike.syntax" +kind = "null_argument" +target = "pattern" +note = "NULL pattern, and NULL against NULL." +catches = "NULL matching NULL (TRUE) or NULL read as an empty pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t006_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t007" +form = "sf.functions.ilike.ilike.syntax" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty subject and pattern." +catches = "the empty pattern matching every string" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t007_empty"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t008" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "VARCHAR ILIKE VARCHAR ESCAPE" +note = "ESCAPE before % and _." +catches = "the escape ignored" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t008_escape"] +required = true + +[[obligation]] +id = "sf.functions.ilike.ilike.syntax.t009" +form = "sf.functions.ilike.ilike.syntax" +kind = "type_combination" +target = "NOT ILIKE" +note = "NOT ILIKE with matching, non-matching and NULL rows." +catches = "NOT applied as case-sensitive or dropped" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/ilike.ilike.syntax.toml#t009_not"] +required = true + +[[obligation]] +id = "sf.functions.initcap.t001" +form = "sf.functions.initcap" +kind = "type_combination" +target = "VARCHAR" +note = "INITCAP with mixed case and default delimiters." +catches = "the rest of each word not lower-cased, or a delimiter set without _ @ - (Ab_cd instead of Ab_Cd)" +reading = "the page: default delimiters are whitespace and ! ? @ \" ^ # $ & ~ _ , . : ; + - * % / | \\ [ ] ( ) { } < >; given delimiters replace them; an empty delimiter string makes one word; the rest of each word is lower-cased" +cases = ["cases/translation/functions/initcap.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.initcap.t002" +form = "sf.functions.initcap" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Given delimiters that replace the defaults." +catches = "given delimiters added to the defaults instead of replacing them" +reading = "the page: default delimiters are whitespace and ! ? @ \" ^ # $ & ~ _ , . : ; + - * % / | \\ [ ] ( ) { } < >; given delimiters replace them; an empty delimiter string makes one word; the rest of each word is lower-cased" +cases = ["cases/translation/functions/initcap.toml#t002_delimiters"] +required = true + +[[obligation]] +id = "sf.functions.initcap.t003" +form = "sf.functions.initcap" +kind = "null_argument" +target = "expr" +note = "NULL string." +catches = "NULL read as empty" +reading = "the page: default delimiters are whitespace and ! ? @ \" ^ # $ & ~ _ , . : ; + - * % / | \\ [ ] ( ) { } < >; given delimiters replace them; an empty delimiter string makes one word; the rest of each word is lower-cased" +cases = ["cases/translation/functions/initcap.toml#t003_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.initcap.t004" +form = "sf.functions.initcap" +kind = "null_argument" +target = "delimiters" +note = "NULL delimiters." +catches = "NULL read as the defaults" +reading = "the page: default delimiters are whitespace and ! ? @ \" ^ # $ & ~ _ , . : ; + - * % / | \\ [ ] ( ) { } < >; given delimiters replace them; an empty delimiter string makes one word; the rest of each word is lower-cased" +cases = ["cases/translation/functions/initcap.toml#t004_null_delimiters"] +required = true + +[[obligation]] +id = "sf.functions.initcap.t005" +form = "sf.functions.initcap" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty delimiter string, which makes one word." +catches = "the empty string read as the defaults" +reading = "the page: default delimiters are whitespace and ! ? @ \" ^ # $ & ~ _ , . : ; + - * % / | \\ [ ] ( ) { } < >; given delimiters replace them; an empty delimiter string makes one word; the rest of each word is lower-cased" +cases = ["cases/translation/functions/initcap.toml#t005_empty_delimiters"] +required = true + +[[obligation]] +id = "sf.functions.insert.t001" +form = "sf.functions.insert" +kind = "type_combination" +target = "VARCHAR, NUMBER, NUMBER, VARCHAR" +note = "Replace characters after a multi-byte character." +catches = "a 0-based position, len ignored (pure insert), or byte offsets" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.insert.t002" +form = "sf.functions.insert" +kind = "type_combination" +target = "VARCHAR, length+1, 0, VARCHAR" +note = "An append at length+1 with len 0." +catches = "an error, or insertion before the last character" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t002_append"] +required = true + +[[obligation]] +id = "sf.functions.insert.t003" +form = "sf.functions.insert" +kind = "type_combination" +target = "BINARY, NUMBER, NUMBER, BINARY" +note = "INSERT on binary." +catches = "hex text positions instead of bytes" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t003_binary"] +required = true + +[[obligation]] +id = "sf.functions.insert.t004" +form = "sf.functions.insert" +kind = "null_argument" +target = "base_expr" +note = "NULL base." +catches = "NULL read as empty" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t004_null_base_expr"] +required = true + +[[obligation]] +id = "sf.functions.insert.t005" +form = "sf.functions.insert" +kind = "null_argument" +target = "pos" +note = "NULL position." +catches = "NULL read as 1" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t005_null_pos"] +required = true + +[[obligation]] +id = "sf.functions.insert.t006" +form = "sf.functions.insert" +kind = "null_argument" +target = "len" +note = "NULL length." +catches = "NULL read as 0" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t006_null_len"] +required = true + +[[obligation]] +id = "sf.functions.insert.t007" +form = "sf.functions.insert" +kind = "null_argument" +target = "insert_expr" +note = "NULL insert string." +catches = "NULL read as '' (a delete)" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t007_null_insert_expr"] +required = true + +[[obligation]] +id = "sf.functions.insert.t008" +form = "sf.functions.insert" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty insert string, which deletes." +catches = "nothing deleted" +reading = "the page: is 1-based, from 1 to length+1; from 0; base and insert both strings or both binary; NULL if any argument is NULL" +cases = ["cases/translation/functions/insert.toml#t008_delete"] +required = true + +[[obligation]] +id = "sf.functions.jarowinkler-similarity.t001" +form = "sf.functions.jarowinkler-similarity" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The page's example pairs." +catches = "plain Jaro without the prefix bonus, a 0-1 FLOAT instead of 0-100, or a byte-based comparison (\u015bwi\u0119ta)" +reading = "the page's examples: an integer 0-100, 0 for two empty strings, NULL for NULL" +cases = ["cases/translation/functions/jarowinkler-similarity.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.jarowinkler-similarity.t002" +form = "sf.functions.jarowinkler-similarity" +kind = "null_argument" +target = "string_expr1" +note = "NULL first string." +catches = "NULL read as empty (0)" +reading = "the page's examples: an integer 0-100, 0 for two empty strings, NULL for NULL" +cases = ["cases/translation/functions/jarowinkler-similarity.toml#t002_null_string_expr1"] +required = true + +[[obligation]] +id = "sf.functions.jarowinkler-similarity.t003" +form = "sf.functions.jarowinkler-similarity" +kind = "null_argument" +target = "string_expr2" +note = "NULL second string." +catches = "NULL read as empty (0)" +reading = "the page's examples: an integer 0-100, 0 for two empty strings, NULL for NULL" +cases = ["cases/translation/functions/jarowinkler-similarity.toml#t003_null_string_expr2"] +required = true + +[[obligation]] +id = "sf.functions.jarowinkler-similarity.t004" +form = "sf.functions.jarowinkler-similarity" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Two empty strings, which the example shows as 0." +catches = "100 for two identical empty strings" +reading = "the page's examples: an integer 0-100, 0 for two empty strings, NULL for NULL" +cases = ["cases/translation/functions/jarowinkler-similarity.toml#t004_empty"] +required = true + +[[obligation]] +id = "sf.functions.left.t001" +form = "sf.functions.left" +kind = "type_combination" +target = "VARCHAR, NUMBER" +note = "LEFT of multi-byte text." +catches = "a byte count (cutting \u00e9 in half) or the other end of the string" +reading = "the page: is VARCHAR or BINARY; counts UTF-8 characters or bytes; a negative length gives an empty string; NULL gives NULL" +cases = ["cases/translation/functions/left.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.left.t002" +form = "sf.functions.left" +kind = "type_combination" +target = "BINARY, NUMBER" +note = "LEFT of binary." +catches = "hex characters counted instead of bytes" +reading = "the page: is VARCHAR or BINARY; counts UTF-8 characters or bytes; a negative length gives an empty string; NULL gives NULL" +cases = ["cases/translation/functions/left.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.left.t003" +form = "sf.functions.left" +kind = "null_argument" +target = "string_expr" +note = "NULL string." +catches = "NULL read as empty" +reading = "the page: is VARCHAR or BINARY; counts UTF-8 characters or bytes; a negative length gives an empty string; NULL gives NULL" +cases = ["cases/translation/functions/left.toml#t003_null_string_expr"] +required = true + +[[obligation]] +id = "sf.functions.left.t004" +form = "sf.functions.left" +kind = "null_argument" +target = "length_expr" +note = "NULL length." +catches = "NULL read as 0 (an empty string instead of NULL)" +reading = "the page: is VARCHAR or BINARY; counts UTF-8 characters or bytes; a negative length gives an empty string; NULL gives NULL" +cases = ["cases/translation/functions/left.toml#t004_null_length_expr"] +required = true + +[[obligation]] +id = "sf.functions.left.t005" +form = "sf.functions.left" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative length, which the page says gives an empty string." +catches = "an error, or Postgres semantics (all but the last characters)" +reading = "the page: is VARCHAR or BINARY; counts UTF-8 characters or bytes; a negative length gives an empty string; NULL gives NULL" +cases = ["cases/translation/functions/left.toml#t005_negative"] +required = true + +[[obligation]] +id = "sf.functions.length.len.t001" +form = "sf.functions.length.len" +kind = "type_combination" +target = "VARCHAR" +note = "LEN of multi-byte text and trailing blanks." +catches = "a byte count (6 for h\u00e9llo) or trailing blanks trimmed" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.len.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.length.len.t002" +form = "sf.functions.length.len" +kind = "type_combination" +target = "BINARY" +note = "LEN of binary values." +catches = "the hex text length (6 instead of 3)" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.len.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.length.len.t003" +form = "sf.functions.length.len" +kind = "null_argument" +target = "expression" +note = "NULL next to a set value." +catches = "NULL read as an empty string (0)" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.len.toml#t003_null_expression"] +required = true + +[[obligation]] +id = "sf.functions.length.len.t004" +form = "sf.functions.length.len" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for an empty string" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.len.toml#t004_empty"] +required = true + +[[obligation]] +id = "sf.functions.length.length.t001" +form = "sf.functions.length.length" +kind = "type_combination" +target = "VARCHAR" +note = "LENGTH of multi-byte text and trailing blanks." +catches = "a byte count (6 for h\u00e9llo) or trailing blanks trimmed" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.length.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.length.length.t002" +form = "sf.functions.length.length" +kind = "type_combination" +target = "BINARY" +note = "LENGTH of binary values." +catches = "the hex text length (6 instead of 3)" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.length.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.length.length.t003" +form = "sf.functions.length.length" +kind = "null_argument" +target = "expression" +note = "NULL next to a set value." +catches = "NULL read as an empty string (0)" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.length.toml#t003_null_expression"] +required = true + +[[obligation]] +id = "sf.functions.length.length.t004" +form = "sf.functions.length.length" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for an empty string" +reading = "the page: is a string or binary; returns NUMBER(18,0)" +cases = ["cases/translation/functions/length.length.toml#t004_empty"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t001" +form = "sf.functions.like-all" +kind = "type_combination" +target = "VARCHAR, (VARCHAR, VARCHAR)" +note = "LIKE ALL where one, both or neither pattern matches." +catches = "ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t001_patterns"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t002" +form = "sf.functions.like-all" +kind = "type_combination" +target = "VARCHAR" +note = "Patterns in another case." +catches = "case sensitivity reversed (TRUE for ABC against a%)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t003" +form = "sf.functions.like-all" +kind = "type_combination" +target = "LIKE ALL ... ESCAPE" +note = "An escape character before %." +catches = "the escape ignored (50 matches 5!%)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t003_escape"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t004" +form = "sf.functions.like-all" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t004_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t005" +form = "sf.functions.like-all" +kind = "null_argument" +target = "pattern" +note = "One NULL pattern beside a matching and a non-matching one." +catches = "NULL patterns ignored (TRUE) where three-valued ALL gives NULL" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t005_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.like-all.t006" +form = "sf.functions.like-all" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty subject against an empty and a % pattern." +catches = "the empty pattern matching a non-empty subject" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-all.toml#t006_empty"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t001" +form = "sf.functions.like-any" +kind = "type_combination" +target = "VARCHAR, (VARCHAR, VARCHAR)" +note = "LIKE ANY where one, both or neither pattern matches." +catches = "ANY and ALL confused (one-of-two matching gives TRUE for ANY, FALSE for ALL) or an unanchored match" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t001_patterns"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t002" +form = "sf.functions.like-any" +kind = "type_combination" +target = "VARCHAR" +note = "Patterns in another case." +catches = "case sensitivity reversed (TRUE for ABC against a%)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t003" +form = "sf.functions.like-any" +kind = "type_combination" +target = "LIKE ANY ... ESCAPE" +note = "An escape character before %." +catches = "the escape ignored (50 matches 5!%)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t003_escape"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t004" +form = "sf.functions.like-any" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t004_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t005" +form = "sf.functions.like-any" +kind = "null_argument" +target = "pattern" +note = "One NULL pattern beside a matching and a non-matching one." +catches = "a NULL pattern making the result NULL even when the other pattern matches, or NULL matching NULL" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t005_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.like-any.t006" +form = "sf.functions.like-any" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty subject against an empty and a % pattern." +catches = "the empty pattern matching a non-empty subject" +reading = "the page: compared with each pattern; at least one pattern; optional ESCAPE; NULL does not match NULL; matching covers the entire string" +cases = ["cases/translation/functions/like-any.toml#t006_empty"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t001" +form = "sf.functions.like.like" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The _ and % wildcards, anchored at both ends." +catches = "an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t001_wildcards"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t002" +form = "sf.functions.like.like" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Letters in another case, including non-ASCII." +catches = "case-insensitive matching for LIKE or case-sensitive for ILIKE" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t003" +form = "sf.functions.like.like" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A newline matched by _ and %, which the page documents." +catches = "wildcards stopping at a newline" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t003_newline"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t004" +form = "sf.functions.like.like" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A backslash in the pattern with no ESCAPE clause." +catches = "backslash used as a default escape (a\\b would then match only ab)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t004_no_default_escape"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t005" +form = "sf.functions.like.like" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t005_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t006" +form = "sf.functions.like.like" +kind = "null_argument" +target = "pattern" +note = "NULL pattern, and NULL against NULL." +catches = "NULL matching NULL (TRUE) or NULL read as an empty pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t006_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t007" +form = "sf.functions.like.like" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty subject and pattern." +catches = "the empty pattern matching every string" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t007_empty"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t008" +form = "sf.functions.like.like" +kind = "type_combination" +target = "VARCHAR, VARCHAR, VARCHAR" +note = "An escape character before % and _." +catches = "the escape ignored (5! % read as a wildcard) or the escape kept as a literal" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t008_escape"] +required = true + +[[obligation]] +id = "sf.functions.like.like.t009" +form = "sf.functions.like.like" +kind = "null_argument" +target = "escape" +note = "NULL escape." +catches = "NULL read as no escape (a value instead of NULL)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.toml#t009_null_escape"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t001" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The _ and % wildcards, anchored at both ends." +catches = "an unanchored match (TRUE for abc against b), _ matching zero characters, or a regex reading of the pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t001_wildcards"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t002" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Letters in another case, including non-ASCII." +catches = "case-insensitive matching for LIKE or case-sensitive for ILIKE" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t003" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A newline matched by _ and %, which the page documents." +catches = "wildcards stopping at a newline" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t003_newline"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t004" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A backslash in the pattern with no ESCAPE clause." +catches = "backslash used as a default escape (a\\b would then match only ab)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t004_no_default_escape"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t005" +form = "sf.functions.like.like.syntax" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against %)" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t005_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t006" +form = "sf.functions.like.like.syntax" +kind = "null_argument" +target = "pattern" +note = "NULL pattern, and NULL against NULL." +catches = "NULL matching NULL (TRUE) or NULL read as an empty pattern" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t006_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t007" +form = "sf.functions.like.like.syntax" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty subject and pattern." +catches = "the empty pattern matching every string" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t007_empty"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t008" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "VARCHAR LIKE VARCHAR ESCAPE" +note = "ESCAPE before % and _." +catches = "the escape ignored" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t008_escape"] +required = true + +[[obligation]] +id = "sf.functions.like.like.syntax.t009" +form = "sf.functions.like.like.syntax" +kind = "type_combination" +target = "NOT LIKE" +note = "NOT LIKE with matching, non-matching and NULL rows." +catches = "NOT applied after NULL handling (TRUE for a NULL subject) or dropped" +reading = "the page: SQL wildcards _ and %, matching the entire string, wildcards matching newlines, no default escape character; NULL if any argument is NULL" +cases = ["cases/translation/functions/like.like.syntax.toml#t009_not"] +required = true + +[[obligation]] +id = "sf.functions.ln.t001" +form = "sf.functions.ln" +kind = "type_combination" +target = "FLOAT" +note = "LN of FLOAT values." +catches = "LOG10 or LOG2 computed" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t001_float"] +required = true + +[[obligation]] +id = "sf.functions.ln.t002" +form = "sf.functions.ln" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "LN of NUMBER(12,4), converted to FLOAT." +catches = "LOG10 or LOG2 computed; or the result left fixed-point" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t002_fixed"] +required = true + +[[obligation]] +id = "sf.functions.ln.t003" +form = "sf.functions.ln" +kind = "type_combination" +target = "DECFLOAT" +note = "LN of DECFLOAT, which returns DECFLOAT." +catches = "a DECFLOAT argument computed and returned as FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t003_decfloat"] +required = true + +[[obligation]] +id = "sf.functions.ln.t004" +form = "sf.functions.ln" +kind = "null_argument" +target = "x" +note = "LN of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t004_null"] +required = true + +[[obligation]] +id = "sf.functions.ln.t005" +form = "sf.functions.ln" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Very small positive values." +catches = "underflow to -infinity" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t005_tiny"] +required = true + +[[obligation]] +id = "sf.functions.ln.t006" +form = "sf.functions.ln" +kind = "error_parity" +target = "error" +note = "LN of 0 or a negative value is an error." +catches = "NULL, NaN or -infinity instead of the error" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; the numeric category gives NUMBER and FLOAT arguments" +cases = ["cases/translation/functions/ln.toml#t006_zero"] +required = true + +[[obligation]] +id = "sf.functions.log.t001" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "LOG of integers." +catches = "arguments swapped (LOG(2, 8) is 3, LOG(8, 2) is 1/3) or LN used" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.log.t002" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "LOG of fixed-point values." +catches = "base rounded to an integer" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.log.t003" +form = "sf.functions.log" +kind = "type_combination" +target = "FLOAT" +note = "LOG of FLOAT values." +catches = "arguments swapped (LOG(10, 0.001) is -3, LOG(0.001, 10) is -1/3) or a non-FLOAT result type" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.log.t004" +form = "sf.functions.log" +kind = "type_combination" +target = "NUMBER(38,0), FLOAT" +note = "LOG with an integer base and a FLOAT value." +catches = "type promotion differing" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t004_mixed"] +required = true + +[[obligation]] +id = "sf.functions.log.t005" +form = "sf.functions.log" +kind = "null_argument" +target = "base" +note = "NULL base." +catches = "NULL turned into e or 10" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t005_null_base"] +required = true + +[[obligation]] +id = "sf.functions.log.t006" +form = "sf.functions.log" +kind = "null_argument" +target = "expr" +note = "NULL value." +catches = "NULL turned into 1" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t006_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.log.t007" +form = "sf.functions.log" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "A base between 0 and 1 gives negative logs of values above 1." +catches = "the sign lost" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t007_base_below_one"] +required = true + +[[obligation]] +id = "sf.functions.log.t008" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A base of 1 is an error." +catches = "infinity or NULL instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t008_base_one"] +required = true + +[[obligation]] +id = "sf.functions.log.t009" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A negative base is an error." +catches = "NaN instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t009_base_neg"] +required = true + +[[obligation]] +id = "sf.functions.log.t010" +form = "sf.functions.log" +kind = "error_parity" +target = "error" +note = "A value of 0 is an error." +catches = "-infinity instead of the error" +reading = "the page: base and expr \"can be of any numeric data type (INTEGER, fixed-point, or floating point)\"; \"If base is 1 or less than or equal to 0, an error is returned. If expr is less than or equal to 0, an error is returned.\"" +cases = ["cases/translation/functions/log.toml#t010_expr_zero"] +required = true + +[[obligation]] +id = "sf.functions.lower.t001" +form = "sf.functions.lower" +kind = "type_combination" +target = "VARCHAR" +note = "LOWER of non-ASCII capitals." +catches = "ASCII-only lowering (\u00c0 \u00c9 \u03a9 kept)" +reading = "the page: returns VARCHAR" +cases = ["cases/translation/functions/lower.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.lower.t002" +form = "sf.functions.lower" +kind = "null_argument" +target = "expr" +note = "NULL string." +catches = "NULL read as empty" +reading = "the page: returns VARCHAR" +cases = ["cases/translation/functions/lower.toml#t002_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.lower.t003" +form = "sf.functions.lower" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for the empty string" +reading = "the page: returns VARCHAR" +cases = ["cases/translation/functions/lower.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t001" +form = "sf.functions.lpad" +kind = "type_combination" +target = "VARCHAR, NUMBER, VARCHAR" +note = "LPAD with a multi-character pad that must be cut." +catches = "the pad cut from the wrong end, or the length counted in bytes (\u00e9)" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t002" +form = "sf.functions.lpad" +kind = "type_combination" +target = "VARCHAR, NUMBER, VARCHAR" +note = "A base longer than the length." +catches = "the base kept whole, or truncated from the right end for LPAD" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t002_truncate"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t003" +form = "sf.functions.lpad" +kind = "type_combination" +target = "VARCHAR, NUMBER" +note = "No pad argument." +catches = "no padding, or a pad other than a blank" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t003_default_pad"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t004" +form = "sf.functions.lpad" +kind = "type_combination" +target = "BINARY, NUMBER, BINARY" +note = "LPAD of binary." +catches = "hex text padded instead of bytes" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t004_binary"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t005" +form = "sf.functions.lpad" +kind = "null_argument" +target = "base" +note = "NULL base." +catches = "NULL read as empty (pad only)" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t005_null_base"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t006" +form = "sf.functions.lpad" +kind = "null_argument" +target = "length_expr" +note = "NULL length." +catches = "NULL read as 0" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t006_null_length_expr"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t007" +form = "sf.functions.lpad" +kind = "null_argument" +target = "pad" +note = "NULL pad." +catches = "NULL read as the default blank" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t007_null_pad"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t008" +form = "sf.functions.lpad" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty pad, which adds nothing but still truncates." +catches = "an infinite loop or error, or no truncation" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t008_empty_pad"] +required = true + +[[obligation]] +id = "sf.functions.lpad.t009" +form = "sf.functions.lpad" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative length: records what Snowflake returns." +catches = "an error or the untouched base where Snowflake returns its value" +reading = "the page: and are both VARCHAR or both BINARY; a longer base is truncated to ; the pad repeats and is truncated; an empty pad adds nothing; the default pad is a blank for strings" +cases = ["cases/translation/functions/lpad.toml#t009_negative_length"] +required = true + +[[obligation]] +id = "sf.functions.ltrim.t001" +form = "sf.functions.ltrim" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "LTRIM with a character set given out of order." +catches = "the set read as a substring, or characters removed from the right as well" +reading = "the page: is a set removed in any order, default a single blank; other whitespace is not removed unless listed; NULL if either argument is NULL" +cases = ["cases/translation/functions/ltrim.toml#t001_characters"] +required = true + +[[obligation]] +id = "sf.functions.ltrim.t002" +form = "sf.functions.ltrim" +kind = "type_combination" +target = "VARCHAR" +note = "LTRIM with the default blank, beside tabs and newlines." +catches = "all whitespace removed (the tab and newline kept by Snowflake)" +reading = "the page: is a set removed in any order, default a single blank; other whitespace is not removed unless listed; NULL if either argument is NULL" +cases = ["cases/translation/functions/ltrim.toml#t002_default"] +required = true + +[[obligation]] +id = "sf.functions.ltrim.t003" +form = "sf.functions.ltrim" +kind = "null_argument" +target = "expr" +note = "NULL string." +catches = "NULL read as empty" +reading = "the page: is a set removed in any order, default a single blank; other whitespace is not removed unless listed; NULL if either argument is NULL" +cases = ["cases/translation/functions/ltrim.toml#t003_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.ltrim.t004" +form = "sf.functions.ltrim" +kind = "null_argument" +target = "characters" +note = "NULL character set." +catches = "NULL read as the default blank" +reading = "the page: is a set removed in any order, default a single blank; other whitespace is not removed unless listed; NULL if either argument is NULL" +cases = ["cases/translation/functions/ltrim.toml#t004_null_characters"] +required = true + +[[obligation]] +id = "sf.functions.ltrim.t005" +form = "sf.functions.ltrim" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty character set, and a string made only of trimmed characters." +catches = "an empty set read as the default blank, or NULL for a fully trimmed string" +reading = "the page: is a set removed in any order, default a single blank; other whitespace is not removed unless listed; NULL if either argument is NULL" +cases = ["cases/translation/functions/ltrim.toml#t005_empty"] +required = true + +[[obligation]] +id = "sf.functions.md5-binary.t001" +form = "sf.functions.md5-binary" +kind = "type_combination" +target = "VARCHAR" +note = "MD5_BINARY of ASCII and multi-byte text." +catches = "another digest, upper-case hex, or Latin-1 bytes for \u00e9" +reading = "the page: is a string; returns a 16-byte BINARY" +cases = ["cases/translation/functions/md5-binary.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.md5-binary.t002" +form = "sf.functions.md5-binary" +kind = "null_argument" +target = "msg" +note = "NULL message next to a set one." +catches = "NULL hashed as an empty string" +reading = "the page: is a string; returns a 16-byte BINARY" +cases = ["cases/translation/functions/md5-binary.toml#t002_null_msg"] +required = true + +[[obligation]] +id = "sf.functions.md5-binary.t003" +form = "sf.functions.md5-binary" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string, which has a defined digest." +catches = "NULL for the empty string" +reading = "the page: is a string; returns a 16-byte BINARY" +cases = ["cases/translation/functions/md5-binary.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-lower64.t001" +form = "sf.functions.md5-number-lower64" +kind = "type_combination" +target = "VARCHAR" +note = "MD5_NUMBER_LOWER64 of ASCII and multi-byte text." +catches = "another digest, upper-case hex, or Latin-1 bytes for \u00e9" +reading = "the page: is a string; returns the lower 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-lower64.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-lower64.t002" +form = "sf.functions.md5-number-lower64" +kind = "null_argument" +target = "msg" +note = "NULL message next to a set one." +catches = "NULL hashed as an empty string" +reading = "the page: is a string; returns the lower 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-lower64.toml#t002_null_msg"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-lower64.t003" +form = "sf.functions.md5-number-lower64" +kind = "boundary" +subkind = "overflow" +target = "overflow" +note = "Messages whose 64-bit half has the top bit set (the empty string and 'Snowflake')." +catches = "a signed 64-bit result (negative) or the other half of the digest" +reading = "the page: is a string; returns the lower 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-lower64.toml#t003_high_bit"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-upper64.t001" +form = "sf.functions.md5-number-upper64" +kind = "type_combination" +target = "VARCHAR" +note = "MD5_NUMBER_UPPER64 of ASCII and multi-byte text." +catches = "another digest, upper-case hex, or Latin-1 bytes for \u00e9" +reading = "the page: is a string; returns the upper 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-upper64.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-upper64.t002" +form = "sf.functions.md5-number-upper64" +kind = "null_argument" +target = "msg" +note = "NULL message next to a set one." +catches = "NULL hashed as an empty string" +reading = "the page: is a string; returns the upper 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-upper64.toml#t002_null_msg"] +required = true + +[[obligation]] +id = "sf.functions.md5-number-upper64.t003" +form = "sf.functions.md5-number-upper64" +kind = "boundary" +subkind = "overflow" +target = "overflow" +note = "Messages whose 64-bit half has the top bit set (the empty string and 'Snowflake')." +catches = "a signed 64-bit result (negative) or the other half of the digest" +reading = "the page: is a string; returns the upper 64 bits as an unsigned integer" +cases = ["cases/translation/functions/md5-number-upper64.toml#t003_high_bit"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5.t001" +form = "sf.functions.md5.md5" +kind = "type_combination" +target = "VARCHAR" +note = "MD5 of ASCII and multi-byte text." +catches = "another digest, upper-case hex, or Latin-1 bytes for \u00e9" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5.t002" +form = "sf.functions.md5.md5" +kind = "null_argument" +target = "msg" +note = "NULL message next to a set one." +catches = "NULL hashed as an empty string" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5.toml#t002_null_msg"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5.t003" +form = "sf.functions.md5.md5" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string, which has a defined digest." +catches = "NULL for the empty string" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5-hex.t001" +form = "sf.functions.md5.md5-hex" +kind = "type_combination" +target = "VARCHAR" +note = "MD5_HEX of ASCII and multi-byte text." +catches = "another digest, upper-case hex, or Latin-1 bytes for \u00e9" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5-hex.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5-hex.t002" +form = "sf.functions.md5.md5-hex" +kind = "null_argument" +target = "msg" +note = "NULL message next to a set one." +catches = "NULL hashed as an empty string" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5-hex.toml#t002_null_msg"] +required = true + +[[obligation]] +id = "sf.functions.md5.md5-hex.t003" +form = "sf.functions.md5.md5-hex" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string, which has a defined digest." +catches = "NULL for the empty string" +reading = "the page: is a string; returns a 32-character hex string" +cases = ["cases/translation/functions/md5.md5-hex.toml#t003_empty"] +required = true + +[[obligation]] +id = "sf.functions.mod.t001" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "MOD of integers with negatives." +catches = "floor-based modulo (MOD(-7, 3) is -1, not 2)" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.mod.t002" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "MOD of NUMBER(10,2) values." +catches = "fractional remainder dropped" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.mod.t003" +form = "sf.functions.mod" +kind = "type_combination" +target = "NUMBER(10,2), NUMBER(38,0)" +note = "MOD of a scaled and an integer operand." +catches = "result scale from one side" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t003_dec_int"] +required = true + +[[obligation]] +id = "sf.functions.mod.t004" +form = "sf.functions.mod" +kind = "type_combination" +target = "FLOAT" +note = "MOD of FLOAT values." +catches = "floor-based float modulo" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t004_float"] +required = true + +[[obligation]] +id = "sf.functions.mod.t005" +form = "sf.functions.mod" +kind = "null_argument" +target = "expr1" +note = "NULL dividend." +catches = "NULL treated as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t005_null_a"] +required = true + +[[obligation]] +id = "sf.functions.mod.t006" +form = "sf.functions.mod" +kind = "null_argument" +target = "expr2" +note = "NULL divisor." +catches = "NULL treated as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t006_null_b"] +required = true + +[[obligation]] +id = "sf.functions.mod.t007" +form = "sf.functions.mod" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Argument order with negatives." +catches = "arguments swapped" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t007_order"] +required = true + +[[obligation]] +id = "sf.functions.mod.t008" +form = "sf.functions.mod" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Remainders of 38-digit integers." +catches = "64-bit modulo" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t008_max"] +required = true + +[[obligation]] +id = "sf.functions.mod.t009" +form = "sf.functions.mod" +kind = "error_parity" +target = "error" +note = "A zero divisor: records what Snowflake returns there; re-kinded to boundary after capture if Snowflake returns a value instead of an error." +catches = "a bare % giving NULL for MOD(x, 0); with it, mod.t006 catches NULL read as 0" +reading = "the page: both arguments numeric expressions; the numeric category gives integer NUMBER, NUMBER with a scale and FLOAT; \"the remainder from truncation-based division (rounding toward zero)\"" +cases = ["cases/translation/functions/mod.toml#t009_zero_divisor"] +required = true + +[[obligation]] +id = "sf.functions.octet-length.t001" +form = "sf.functions.octet-length" +kind = "type_combination" +target = "VARCHAR" +note = "OCTET_LENGTH of multi-byte text." +catches = "the character count instead of UTF-8 bytes (5 characters, 6 bytes)" +reading = "the page: is a string or binary; the length in bytes" +cases = ["cases/translation/functions/octet-length.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.octet-length.t002" +form = "sf.functions.octet-length" +kind = "type_combination" +target = "BINARY" +note = "OCTET_LENGTH of binary." +catches = "the hex text length instead of the byte length" +reading = "the page: is a string or binary; the length in bytes" +cases = ["cases/translation/functions/octet-length.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.octet-length.t003" +form = "sf.functions.octet-length" +kind = "null_argument" +target = "string_or_binary" +note = "NULL next to a set value." +catches = "NULL read as 0" +reading = "the page: is a string or binary; the length in bytes" +cases = ["cases/translation/functions/octet-length.toml#t003_null_string_or_binary"] +required = true + +[[obligation]] +id = "sf.functions.octet-length.t004" +form = "sf.functions.octet-length" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "The empty string." +catches = "NULL for an empty string" +reading = "the page: is a string or binary; the length in bytes" +cases = ["cases/translation/functions/octet-length.toml#t004_empty"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t001" +form = "sf.functions.parse-ip" +kind = "type_combination" +target = "VARCHAR, 'INET'" +note = "IPv4, IPv4 with a prefix, and IPv6." +catches = "missing ipv4_range_start/end for a prefix, a signed ip_fields value, or IPv6 fields out of order" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t001_inet"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t002" +form = "sf.functions.parse-ip" +kind = "type_combination" +target = "VARCHAR, 'cidr'" +note = "CIDR in lower case." +catches = "the type name compared case-sensitively" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t002_cidr"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t003" +form = "sf.functions.parse-ip" +kind = "type_combination" +target = "VARCHAR, 'INET', 1" +note = "An unparseable address with permissive 1." +catches = "an error or NULL instead of an object with an error field" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t003_permissive"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t004" +form = "sf.functions.parse-ip" +kind = "null_argument" +target = "expr" +note = "NULL address." +catches = "an error object instead of NULL" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t004_null_expr"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t005" +form = "sf.functions.parse-ip" +kind = "null_argument" +target = "permissive" +note = "NULL permissive flag." +catches = "NULL read as 0" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t005_null_permissive"] +required = true + +[[obligation]] +id = "sf.functions.parse-ip.t006" +form = "sf.functions.parse-ip" +kind = "error_parity" +target = "error" +note = "An unparseable address with the default permissive 0." +catches = "an error object or NULL instead of an error" +reading = "the page: is INET or CIDR, case-insensitive; 0 fails on a parse error and 1 returns an object with an error field; returns OBJECT" +cases = ["cases/translation/functions/parse-ip.toml#t006_strict"] +required = true + +[[obligation]] +id = "sf.functions.parse-url.t001" +form = "sf.functions.parse-url" +kind = "type_combination" +target = "VARCHAR" +note = "A URL with every part, and one with none of the optional parts." +catches = "parameters not split into an object, a port as a number, or missing keys set to empty strings instead of NULL" +reading = "the page: returns an OBJECT with fragment, host, parameters, path, port, query and scheme; NULL for NULL; as for PARSE_IP" +cases = ["cases/translation/functions/parse-url.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.parse-url.t002" +form = "sf.functions.parse-url" +kind = "type_combination" +target = "VARCHAR, 1" +note = "An unparseable URL with permissive 1." +catches = "an error instead of an error object" +reading = "the page: returns an OBJECT with fragment, host, parameters, path, port, query and scheme; NULL for NULL; as for PARSE_IP" +cases = ["cases/translation/functions/parse-url.toml#t002_permissive"] +required = true + +[[obligation]] +id = "sf.functions.parse-url.t003" +form = "sf.functions.parse-url" +kind = "null_argument" +target = "string" +note = "NULL URL." +catches = "an error object instead of NULL" +reading = "the page: returns an OBJECT with fragment, host, parameters, path, port, query and scheme; NULL for NULL; as for PARSE_IP" +cases = ["cases/translation/functions/parse-url.toml#t003_null_string"] +required = true + +[[obligation]] +id = "sf.functions.parse-url.t004" +form = "sf.functions.parse-url" +kind = "null_argument" +target = "permissive" +note = "NULL permissive flag." +catches = "NULL read as 0" +reading = "the page: returns an OBJECT with fragment, host, parameters, path, port, query and scheme; NULL for NULL; as for PARSE_IP" +cases = ["cases/translation/functions/parse-url.toml#t004_null_permissive"] +required = true + +[[obligation]] +id = "sf.functions.parse-url.t005" +form = "sf.functions.parse-url" +kind = "error_parity" +target = "error" +note = "An unparseable URL with permissive 0." +catches = "an error object or NULL instead of an error" +reading = "the page: returns an OBJECT with fragment, host, parameters, path, port, query and scheme; NULL for NULL; as for PARSE_IP" +cases = ["cases/translation/functions/parse-url.toml#t005_strict"] +required = true + +[[obligation]] +id = "sf.functions.pi.t001" +form = "sf.functions.pi" +kind = "type_combination" +target = "()" +note = "PI() in arithmetic, at full double precision." +catches = "a rounded constant (3.14159) or a NUMBER" +reading = "the page: PI() takes no argument and returns FLOAT" +cases = ["cases/translation/functions/pi.toml#t001_value"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t001" +form = "sf.functions.position.position" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Search for a substring after multi-byte characters." +catches = "arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t002" +form = "sf.functions.position.position" +kind = "type_combination" +target = "VARCHAR, VARCHAR, NUMBER" +note = "A start position past the first match." +catches = "the start position ignored (2 instead of 4) or the result counted from the start position" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t002_start_pos"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t003" +form = "sf.functions.position.position" +kind = "type_combination" +target = "BINARY, BINARY" +note = "Search in binary values." +catches = "a hex-text offset (3 instead of 2)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t003_binary"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t004" +form = "sf.functions.position.position" +kind = "null_argument" +target = "expr1" +note = "NULL needle next to a set one." +catches = "NULL read as an empty needle (1 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t004_null_expr1"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t005" +form = "sf.functions.position.position" +kind = "null_argument" +target = "expr2" +note = "NULL haystack next to a set one." +catches = "NULL read as an empty string (0 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t005_null_expr2"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t006" +form = "sf.functions.position.position" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty needle, which the page says gives 1." +catches = "NULL or 0 for an empty needle" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t006_empty_needle"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t007" +form = "sf.functions.position.position" +kind = "null_argument" +target = "start_pos" +note = "NULL start next to a set one." +catches = "NULL read as the default 1" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t007_null_start_pos"] +required = true + +[[obligation]] +id = "sf.functions.position.position.t008" +form = "sf.functions.position.position" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "A start position past the end of the string." +catches = "an error or the last match instead of 0" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position.toml#t008_start_past_end"] +required = true + +[[obligation]] +id = "sf.functions.position.position-in.t001" +form = "sf.functions.position.position-in" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Search for a substring after multi-byte characters." +catches = "arguments swapped (searching the needle in the haystack), a 0-based result, or a byte offset (5 instead of 3 for \u00e9\u00e9l)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position-in.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.position.position-in.t002" +form = "sf.functions.position.position-in" +kind = "type_combination" +target = "BINARY, BINARY" +note = "Search in binary values." +catches = "a hex-text offset (3 instead of 2)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position-in.toml#t002_binary"] +required = true + +[[obligation]] +id = "sf.functions.position.position-in.t003" +form = "sf.functions.position.position-in" +kind = "null_argument" +target = "expr1" +note = "NULL needle next to a set one." +catches = "NULL read as an empty needle (1 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position-in.toml#t003_null_expr1"] +required = true + +[[obligation]] +id = "sf.functions.position.position-in.t004" +form = "sf.functions.position.position-in" +kind = "null_argument" +target = "expr2" +note = "NULL haystack next to a set one." +catches = "NULL read as an empty string (0 instead of NULL)" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position-in.toml#t004_null_expr2"] +required = true + +[[obligation]] +id = "sf.functions.position.position-in.t005" +form = "sf.functions.position.position-in" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "An empty needle, which the page says gives 1." +catches = "NULL or 0 for an empty needle" +reading = "the page: and are both strings or both binary; is 1-based; not found or start past the end gives 0; an empty gives 1; NULL gives NULL" +cases = ["cases/translation/functions/position.position-in.toml#t005_empty_needle"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t001" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "POW of integers returns FLOAT." +catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t002" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "POW with fractional exponents." +catches = "the exponent rounded" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t003" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "FLOAT" +note = "POW of FLOAT values." +catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t004" +form = "sf.functions.pow.pow" +kind = "type_combination" +target = "DECFLOAT" +note = "POW of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t004_decfloat"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t005" +form = "sf.functions.pow.pow" +kind = "null_argument" +target = "x" +note = "NULL base." +catches = "NULL^0 returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t005_null_x"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t006" +form = "sf.functions.pow.pow" +kind = "null_argument" +target = "y" +note = "NULL exponent." +catches = "1^NULL returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t006_null_y"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t007" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Powers past the double range, and 0^0." +catches = "infinity versus error; 0^0 as 0" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t007_overflow_inf"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t008" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative bases with integer exponents." +catches = "the sign lost for odd exponents" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t008_neg_base"] +required = true + +[[obligation]] +id = "sf.functions.pow.pow.t009" +form = "sf.functions.pow.pow" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, NULL or an error differing from Snowflake" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.pow.toml#t009_neg_frac"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t001" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "NUMBER(38,0)" +note = "POWER of integers returns FLOAT." +catches = "arguments swapped (2^3 is 8, 3^2 is 9) or an integer result" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t001_int"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t002" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "NUMBER(10,2)" +note = "POWER with fractional exponents." +catches = "the exponent rounded" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t002_dec"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t003" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "FLOAT" +note = "POWER of FLOAT values." +catches = "arguments swapped (2.5^1.5 is 3.95, 1.5^2.5 is 2.76) or a non-FLOAT result type" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t003_float"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t004" +form = "sf.functions.pow.power" +kind = "type_combination" +target = "DECFLOAT" +note = "POWER of DECFLOAT returns DECFLOAT." +catches = "computed in FLOAT" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t004_decfloat"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t005" +form = "sf.functions.pow.power" +kind = "null_argument" +target = "x" +note = "NULL base." +catches = "NULL^0 returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t005_null_x"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t006" +form = "sf.functions.pow.power" +kind = "null_argument" +target = "y" +note = "NULL exponent." +catches = "1^NULL returning 1" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t006_null_y"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t007" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Powers past the double range, and 0^0." +catches = "infinity versus error; 0^0 as 0" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t007_overflow_inf"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t008" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "Negative bases with integer exponents." +catches = "the sign lost for odd exponents" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t008_neg_base"] +required = true + +[[obligation]] +id = "sf.functions.pow.power.t009" +form = "sf.functions.pow.power" +kind = "boundary" +subkind = "negative" +target = "negative" +note = "A negative base with a fractional exponent: records what Snowflake returns there (NaN, Infinity, NULL or an error); re-kinded to error_parity after capture if Snowflake raises an error." +catches = "NaN, NULL or an error differing from Snowflake" +reading = "the page: DECFLOAT returns DECFLOAT, otherwise FLOAT; x and y numeric (the numeric category gives NUMBER and FLOAT)" +cases = ["cases/translation/functions/pow.power.toml#t009_neg_frac"] +required = true + +[[obligation]] +id = "sf.functions.radians.t001" +form = "sf.functions.radians" +kind = "type_combination" +target = "FLOAT" +note = "RADIANS of FLOAT values." +catches = "DEGREES computed (the reverse conversion)" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t001_float"] +required = true + +[[obligation]] +id = "sf.functions.radians.t002" +form = "sf.functions.radians" +kind = "type_combination" +target = "NUMBER(12,4)" +note = "RADIANS of NUMBER(12,4), converted to FLOAT." +catches = "DEGREES computed (the reverse conversion); or the result left fixed-point" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t002_fixed"] +required = true + +[[obligation]] +id = "sf.functions.radians.t003" +form = "sf.functions.radians" +kind = "null_argument" +target = "x" +note = "RADIANS of NULL." +catches = "NULL turned into 0 or NaN" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t003_null"] +required = true + +[[obligation]] +id = "sf.functions.radians.t004" +form = "sf.functions.radians" +kind = "boundary" +subkind = "magnitude" +target = "magnitude" +note = "Large degree values." +catches = "x*pi computed before dividing by 180, overflowing where the result is finite" +reading = "the page: \"The data type must be FLOAT.\" Returns FLOAT; a fixed-point argument reaches it by implicit conversion" +cases = ["cases/translation/functions/radians.toml#t004_large"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t001" +form = "sf.functions.regexp" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Patterns that match inside but not across the whole subject." +catches = "an unanchored search (TRUE for abc against b) or a LIKE reading of the pattern" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t001_anchored"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t002" +form = "sf.functions.regexp" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Case-sensitive by default." +catches = "case-insensitive matching (TRUE for ABC against abc)" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t002_case"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t003" +form = "sf.functions.regexp" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "A dot against a newline without the s parameter." +catches = "dot matching newline by default" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t003_dot_newline"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t004" +form = "sf.functions.regexp" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as an empty string (TRUE against .*)" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t004_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t005" +form = "sf.functions.regexp" +kind = "null_argument" +target = "pattern" +note = "NULL pattern." +catches = "NULL read as an empty pattern" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t005_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t006" +form = "sf.functions.regexp" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "Empty subject and pattern (the page: '' becomes ^$)." +catches = "the empty pattern matching everything" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t006_empty"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t007" +form = "sf.functions.regexp" +kind = "error_parity" +target = "error" +note = "An unbalanced bracket." +catches = "FALSE or NULL instead of an error" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t007_bad_pattern"] +required = true + +[[obligation]] +id = "sf.functions.regexp.t008" +form = "sf.functions.regexp" +kind = "type_combination" +target = "NOT" +note = "The NOT form with matching, non-matching and NULL rows." +catches = "NOT dropped, or TRUE for a NULL subject" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; the page: \"The function implicitly anchors a pattern at both ends\"" +cases = ["cases/translation/functions/regexp.toml#t008_not"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t001" +form = "sf.functions.regexp-count" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "Non-overlapping matches, unanchored." +catches = "overlapping matches counted (3 for aa in aaaa) or an anchored match" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t001_varchar"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t002" +form = "sf.functions.regexp-count" +kind = "type_combination" +target = "VARCHAR, VARCHAR, NUMBER" +note = "A start position in characters past a multi-byte character." +catches = "the position ignored, read 0-based, or as a byte offset" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t002_position"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t003" +form = "sf.functions.regexp-count" +kind = "type_combination" +target = "VARCHAR, VARCHAR, NUMBER, VARCHAR" +note = "The i parameter." +catches = "parameters ignored" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t003_parameters"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t004" +form = "sf.functions.regexp-count" +kind = "null_argument" +target = "subject" +note = "NULL subject." +catches = "NULL read as empty (0)" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t004_null_subject"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t005" +form = "sf.functions.regexp-count" +kind = "null_argument" +target = "pattern" +note = "NULL pattern." +catches = "NULL read as an empty pattern" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t005_null_pattern"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t006" +form = "sf.functions.regexp-count" +kind = "null_argument" +target = "position" +note = "NULL position." +catches = "NULL read as 1" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t006_null_position"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t007" +form = "sf.functions.regexp-count" +kind = "null_argument" +target = "parameters" +note = "NULL parameters." +catches = "NULL read as c" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t007_null_parameters"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t008" +form = "sf.functions.regexp-count" +kind = "boundary" +subkind = "empty_string" +target = "empty_string" +note = "A pattern that can match the empty string." +catches = "empty matches not counted, or counted twice at each position" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t008_empty_match"] +required = true + +[[obligation]] +id = "sf.functions.regexp-count.t009" +form = "sf.functions.regexp-count" +kind = "error_parity" +target = "error" +note = "Position 0, outside the documented positive range: records what Snowflake returns there; it moves to boundary if Snowflake returns a value instead of an error." +catches = "position 0 read as 1" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; is a positive integer" +cases = ["cases/translation/functions/regexp-count.toml#t009_position_zero"] +required = true + +[[obligation]] +id = "sf.functions.regexp-instr.t001" +form = "sf.functions.regexp-instr" +kind = "type_combination" +target = "VARCHAR, VARCHAR" +note = "The first match after multi-byte characters." +catches = "a 0-based or byte offset, or 0 for a match" +reading = "the pages: POSIX extended regular expressions; parameters c, i, m, e, s (default c); positions are 1-based; NULL if any argument is NULL; skips occurrence-1 matches;