From 148695ca8fde655efe56a0f009c375d9eafb606b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:02:05 +0000 Subject: [PATCH 01/10] cases: D267 statistics and month-arithmetic edges 23 cases appended to existing files, for receipts before converting: - STDDEV, STDDEV_SAMP, STDDEV_POP, VARIANCE, VAR_SAMP, VAR_POP, VARIANCE_POP, CORR, COVAR_SAMP, COVAR_POP: values near 1e9 with a small spread, and NUMBER(10,2) inputs with SYSTEM$TYPEOF. - ADD_MONTHS: negative counts from month ends, day 31 into shorter months, across a year. - MONTHS_BETWEEN: month ends and equal days with times of day, and a long negative span. --- cases/translation/functions/add-months.toml | 15 +++++++++++ cases/translation/functions/corr.corr.toml | 27 +++++++++++++++++++ .../functions/covar-pop.covar-pop.toml | 27 +++++++++++++++++++ .../functions/covar-samp.covar-samp.toml | 27 +++++++++++++++++++ .../translation/functions/months-between.toml | 26 ++++++++++++++++++ .../functions/stddev-pop.stddev-pop.toml | 27 +++++++++++++++++++ .../functions/stddev.stddev-samp.toml | 27 +++++++++++++++++++ .../translation/functions/stddev.stddev.toml | 27 +++++++++++++++++++ .../functions/var-pop.var-pop.toml | 27 +++++++++++++++++++ .../functions/var-samp.var-samp.toml | 27 +++++++++++++++++++ .../functions/variance-pop.variance-pop.toml | 27 +++++++++++++++++++ .../functions/variance.variance.toml | 27 +++++++++++++++++++ 12 files changed, 311 insertions(+) diff --git a/cases/translation/functions/add-months.toml b/cases/translation/functions/add-months.toml index dc11ff354..1aa78b9ee 100644 --- a/cases/translation/functions/add-months.toml +++ b/cases/translation/functions/add-months.toml @@ -86,3 +86,18 @@ rows = [ query = "SELECT ADD_MONTHS('2024-01-15'::DATE, n) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t008_negative_and_day_31" +description = "Negative counts from month ends and day 31 into shorter months, across a year. Catches: the month end lost going backwards, or day 31 overflowing into the next month" +columns = [{ name = "d", type = "DATE" }] +rows = [ + ["'2024-03-31'"], + ["'2024-01-31'"], + ["'2024-05-31'"], + ["'2023-01-29'"], + ["'2024-12-15'"], +] +query = "SELECT ADD_MONTHS(d, -1) AS m1, ADD_MONTHS(d, 1) AS p1, ADD_MONTHS(d, -13) AS m13 FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/corr.corr.toml b/cases/translation/functions/corr.corr.toml index 48f105726..ca0d9bd4b 100644 --- a/cases/translation/functions/corr.corr.toml +++ b/cases/translation/functions/corr.corr.toml @@ -83,3 +83,30 @@ rows = [ query = "SELECT CORR(y, x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Pairs near 1e9 with small spreads. Catches: a sum-of-products formula losing the digits of the spread" +columns = [{ name = "y", type = "NUMBER(38,0)" }, { name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000002", "1000000001"], + ["1000000004", "1000000002"], + ["1000000005", "1000000003"], + ["1000000009", "1000000004"], +] +query = "SELECT CORR(y, x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "NUMBER(10,2) columns. Catches: the result type or scale of decimal inputs" +columns = [{ name = "y", type = "NUMBER(10,2)" }, { name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25", "2.50"], + ["2.50", "2.75"], + ["4.10", "3.00"], +] +query = "SELECT CORR(y, x) AS r, SYSTEM$TYPEOF(CORR(y, x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/covar-pop.covar-pop.toml b/cases/translation/functions/covar-pop.covar-pop.toml index 5beba504c..fae7e7d1b 100644 --- a/cases/translation/functions/covar-pop.covar-pop.toml +++ b/cases/translation/functions/covar-pop.covar-pop.toml @@ -70,3 +70,30 @@ rows = [ query = "SELECT COVAR_POP(y, x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t006_large_mean" +description = "Pairs near 1e9 with small spreads. Catches: a sum-of-products formula losing the digits of the spread" +columns = [{ name = "y", type = "NUMBER(38,0)" }, { name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000002", "1000000001"], + ["1000000004", "1000000002"], + ["1000000005", "1000000003"], + ["1000000009", "1000000004"], +] +query = "SELECT COVAR_POP(y, x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007_decimal" +description = "NUMBER(10,2) columns. Catches: the result type or scale of decimal inputs" +columns = [{ name = "y", type = "NUMBER(10,2)" }, { name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25", "2.50"], + ["2.50", "2.75"], + ["4.10", "3.00"], +] +query = "SELECT COVAR_POP(y, x) AS r, SYSTEM$TYPEOF(COVAR_POP(y, x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/covar-samp.covar-samp.toml b/cases/translation/functions/covar-samp.covar-samp.toml index eedac2bc3..5810e7bf1 100644 --- a/cases/translation/functions/covar-samp.covar-samp.toml +++ b/cases/translation/functions/covar-samp.covar-samp.toml @@ -70,3 +70,30 @@ rows = [ query = "SELECT COVAR_SAMP(y, x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t006_large_mean" +description = "Pairs near 1e9 with small spreads. Catches: a sum-of-products formula losing the digits of the spread" +columns = [{ name = "y", type = "NUMBER(38,0)" }, { name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000002", "1000000001"], + ["1000000004", "1000000002"], + ["1000000005", "1000000003"], + ["1000000009", "1000000004"], +] +query = "SELECT COVAR_SAMP(y, x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t007_decimal" +description = "NUMBER(10,2) columns. Catches: the result type or scale of decimal inputs" +columns = [{ name = "y", type = "NUMBER(10,2)" }, { name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25", "2.50"], + ["2.50", "2.75"], + ["4.10", "3.00"], +] +query = "SELECT COVAR_SAMP(y, x) AS r, SYSTEM$TYPEOF(COVAR_SAMP(y, x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/months-between.toml b/cases/translation/functions/months-between.toml index 353a6499c..a878b8a4c 100644 --- a/cases/translation/functions/months-between.toml +++ b/cases/translation/functions/months-between.toml @@ -84,3 +84,29 @@ rows = [ query = "SELECT MONTHS_BETWEEN(a, b) AS r FROM input ORDER BY ordinal" order = "ordered" tags = ["boundary"] + +[[case]] +id = "t008_month_ends_with_time" +description = "Timestamps on month ends and on equal days, with times of day. Catches: the time counted when both are month ends or share a day, or the fraction taken over the month length instead of 31 days" +columns = [{ name = "a", type = "TIMESTAMP_NTZ" }, { name = "b", type = "TIMESTAMP_NTZ" }] +rows = [ + ["'2019-03-31 23:00:00'", "'2019-02-28 01:00:00'"], + ["'2024-04-30 00:00:00'", "'2024-03-30 12:00:00'"], + ["'2024-01-31 06:00:00'", "'2024-02-29 18:00:00'"], + ["'2024-01-10 06:00:00'", "'2024-02-20 18:00:00'"], +] +query = "SELECT MONTHS_BETWEEN(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t009_long_negative" +description = "A span of years, negative. Catches: the whole months or the fraction miscounted far apart" +columns = [{ name = "a", type = "DATE" }, { name = "b", type = "DATE" }] +rows = [ + ["'2000-01-15'", "'2024-07-20'"], + ["'2024-07-20'", "'1999-12-31'"], +] +query = "SELECT MONTHS_BETWEEN(a, b) AS r FROM input ORDER BY ordinal" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/stddev-pop.stddev-pop.toml b/cases/translation/functions/stddev-pop.stddev-pop.toml index 1d15690fb..3983d2652 100644 --- a/cases/translation/functions/stddev-pop.stddev-pop.toml +++ b/cases/translation/functions/stddev-pop.stddev-pop.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT STDDEV_POP(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT STDDEV_POP(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT STDDEV_POP(x) AS r, SYSTEM$TYPEOF(STDDEV_POP(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/stddev.stddev-samp.toml b/cases/translation/functions/stddev.stddev-samp.toml index 4929e5cc1..def96fe5a 100644 --- a/cases/translation/functions/stddev.stddev-samp.toml +++ b/cases/translation/functions/stddev.stddev-samp.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT STDDEV_SAMP(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT STDDEV_SAMP(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT STDDEV_SAMP(x) AS r, SYSTEM$TYPEOF(STDDEV_SAMP(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/stddev.stddev.toml b/cases/translation/functions/stddev.stddev.toml index 615f253fd..14b530681 100644 --- a/cases/translation/functions/stddev.stddev.toml +++ b/cases/translation/functions/stddev.stddev.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT STDDEV(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT STDDEV(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT STDDEV(x) AS r, SYSTEM$TYPEOF(STDDEV(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/var-pop.var-pop.toml b/cases/translation/functions/var-pop.var-pop.toml index 8b3225b03..21989dd73 100644 --- a/cases/translation/functions/var-pop.var-pop.toml +++ b/cases/translation/functions/var-pop.var-pop.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT VAR_POP(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT VAR_POP(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT VAR_POP(x) AS r, SYSTEM$TYPEOF(VAR_POP(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/var-samp.var-samp.toml b/cases/translation/functions/var-samp.var-samp.toml index a128def19..7998b651c 100644 --- a/cases/translation/functions/var-samp.var-samp.toml +++ b/cases/translation/functions/var-samp.var-samp.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT VAR_SAMP(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT VAR_SAMP(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT VAR_SAMP(x) AS r, SYSTEM$TYPEOF(VAR_SAMP(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/variance-pop.variance-pop.toml b/cases/translation/functions/variance-pop.variance-pop.toml index f0bfc6920..bf1987463 100644 --- a/cases/translation/functions/variance-pop.variance-pop.toml +++ b/cases/translation/functions/variance-pop.variance-pop.toml @@ -104,3 +104,30 @@ rows = [ query = "SELECT VARIANCE_POP(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT VARIANCE_POP(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT VARIANCE_POP(x) AS r, SYSTEM$TYPEOF(VARIANCE_POP(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] diff --git a/cases/translation/functions/variance.variance.toml b/cases/translation/functions/variance.variance.toml index b6a3f87be..a97e18d45 100644 --- a/cases/translation/functions/variance.variance.toml +++ b/cases/translation/functions/variance.variance.toml @@ -105,3 +105,30 @@ rows = [ query = "SELECT VARIANCE(x) AS r FROM input WHERE ordinal < 0" order = "ordered" tags = ["empty-input"] + +[[case]] +id = "t007_large_mean" +description = "Values near 1e9 with a spread of 1. Catches: a sum-of-squares formula losing the digits of the spread" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT VARIANCE(x) AS r FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t008_decimal" +description = "A NUMBER(10,2) column. Catches: the result type or scale of a decimal input" +columns = [{ name = "x", type = "NUMBER(10,2)" }] +rows = [ + ["1.25"], + ["2.50"], + ["4.10"], +] +query = "SELECT VARIANCE(x) AS r, SYSTEM$TYPEOF(VARIANCE(x)) AS t FROM input" +order = "ordered" +tags = ["type-combination"] From e510d1a670f0b8c699a0dcbacbd430bccebb2573 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:18:41 +0000 Subject: [PATCH 02/10] evidence: D267 statistics and month-arithmetic edges Recorded from wip/d267-edges 148695ca (23 cases, utc-v1, no failures). --- ...5c58276b6a341822ad6b82d27cb34e606958.arrow | Bin 0 -> 1690 bytes ...85c58276b6a341822ad6b82d27cb34e606958.json | 70 ++++++++++++++++ ...6468260d9502464935759ce5f11b3daa0620.arrow | Bin 0 -> 1034 bytes ...a6468260d9502464935759ce5f11b3daa0620.json | 61 ++++++++++++++ ...695a058bf92dd79586043bcb675d524d5e18.arrow | Bin 0 -> 978 bytes ...7695a058bf92dd79586043bcb675d524d5e18.json | 61 ++++++++++++++ ...4f45e60471601cbae8c552ebaeccb5d49770.arrow | Bin 0 -> 986 bytes ...54f45e60471601cbae8c552ebaeccb5d49770.json | 61 ++++++++++++++ ...dedf722467bdc706043dba1855a3f9671f7b.arrow | Bin 0 -> 986 bytes ...ddedf722467bdc706043dba1855a3f9671f7b.json | 61 ++++++++++++++ ...ee882c13e5235d0c7171cded8bfcc8f6039a.arrow | Bin 0 -> 986 bytes ...bee882c13e5235d0c7171cded8bfcc8f6039a.json | 61 ++++++++++++++ ...a301b7d4c3fd0e58c927ac270314973573d6.arrow | Bin 0 -> 1706 bytes ...ba301b7d4c3fd0e58c927ac270314973573d6.json | 70 ++++++++++++++++ ...4d78c52cd88d120c50aae51850fb611e8475.arrow | Bin 0 -> 978 bytes ...94d78c52cd88d120c50aae51850fb611e8475.json | 61 ++++++++++++++ ...4557f240ef1e7365ef46c70efce35eacbb71.arrow | Bin 0 -> 978 bytes ...84557f240ef1e7365ef46c70efce35eacbb71.json | 61 ++++++++++++++ ...c5b94e6dd92799fa7b6feb41289331ea8614.arrow | Bin 0 -> 1002 bytes ...1c5b94e6dd92799fa7b6feb41289331ea8614.json | 61 ++++++++++++++ ...db4765ffc346b545b0bd74b3b52cad4ba2db.arrow | Bin 0 -> 1706 bytes ...5db4765ffc346b545b0bd74b3b52cad4ba2db.json | 70 ++++++++++++++++ ...bd8c2c4254527891b50003f63937345e1005.arrow | Bin 0 -> 1706 bytes ...9bd8c2c4254527891b50003f63937345e1005.json | 70 ++++++++++++++++ ...b57a881ddf42e72d2d5e3210bd75e197a616.arrow | Bin 0 -> 1706 bytes ...3b57a881ddf42e72d2d5e3210bd75e197a616.json | 70 ++++++++++++++++ ...716169fb6bcdfc59c72305ebbb14abe32c68.arrow | Bin 0 -> 978 bytes ...8716169fb6bcdfc59c72305ebbb14abe32c68.json | 61 ++++++++++++++ ...269e0e7fcb93dcaa980ff84c2cccc11c1001.arrow | Bin 0 -> 1690 bytes ...8269e0e7fcb93dcaa980ff84c2cccc11c1001.json | 70 ++++++++++++++++ ...6d384af66531ac6ccb683589db095bb47b3a.arrow | Bin 0 -> 1690 bytes ...96d384af66531ac6ccb683589db095bb47b3a.json | 70 ++++++++++++++++ ...043d777a96fb247ef8576011b3297d7aeba6.arrow | Bin 0 -> 1690 bytes ...0043d777a96fb247ef8576011b3297d7aeba6.json | 70 ++++++++++++++++ ...a0610acc939afde4f242aef1dd55ea0d0ad0.arrow | Bin 0 -> 986 bytes ...1a0610acc939afde4f242aef1dd55ea0d0ad0.json | 61 ++++++++++++++ ...4219b55bc9fd95f8b6078aeff47ee7a5b22f.arrow | Bin 0 -> 2322 bytes ...d4219b55bc9fd95f8b6078aeff47ee7a5b22f.json | 79 ++++++++++++++++++ ...4d0579413af28bffd402ce595a3012e80990.arrow | Bin 0 -> 1690 bytes ...34d0579413af28bffd402ce595a3012e80990.json | 70 ++++++++++++++++ ...e7be5608738abea4b72c0e46321b961f64b2.arrow | Bin 0 -> 1690 bytes ...5e7be5608738abea4b72c0e46321b961f64b2.json | 70 ++++++++++++++++ ...cd2e46e4bcf57bdd1a65fcc393629940962e.arrow | Bin 0 -> 986 bytes ...8cd2e46e4bcf57bdd1a65fcc393629940962e.json | 61 ++++++++++++++ ...b31687cb682aded35fd77e7547ff977cea97.arrow | Bin 0 -> 978 bytes ...bb31687cb682aded35fd77e7547ff977cea97.json | 61 ++++++++++++++ 46 files changed, 1511 insertions(+) create mode 100644 evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.arrow create mode 100644 evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.json create mode 100644 evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.arrow create mode 100644 evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.json create mode 100644 evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.arrow create mode 100644 evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.json create mode 100644 evidence/results/32abfb26d0c8d0c1c6cfdf62cd154f45e60471601cbae8c552ebaeccb5d49770.arrow create mode 100644 evidence/results/32abfb26d0c8d0c1c6cfdf62cd154f45e60471601cbae8c552ebaeccb5d49770.json create mode 100644 evidence/results/3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b.arrow create mode 100644 evidence/results/3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b.json create mode 100644 evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.arrow create mode 100644 evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.json create mode 100644 evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.arrow create mode 100644 evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.json create mode 100644 evidence/results/502b3426fb59b9896c7f33fc08294d78c52cd88d120c50aae51850fb611e8475.arrow create mode 100644 evidence/results/502b3426fb59b9896c7f33fc08294d78c52cd88d120c50aae51850fb611e8475.json create mode 100644 evidence/results/5ede4a0cc5e10697a9295b31e2984557f240ef1e7365ef46c70efce35eacbb71.arrow create mode 100644 evidence/results/5ede4a0cc5e10697a9295b31e2984557f240ef1e7365ef46c70efce35eacbb71.json create mode 100644 evidence/results/74a21c66ba08e085d86acc7fed71c5b94e6dd92799fa7b6feb41289331ea8614.arrow create mode 100644 evidence/results/74a21c66ba08e085d86acc7fed71c5b94e6dd92799fa7b6feb41289331ea8614.json create mode 100644 evidence/results/76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db.arrow create mode 100644 evidence/results/76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db.json create mode 100644 evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.arrow create mode 100644 evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.json create mode 100644 evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.arrow create mode 100644 evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.json create mode 100644 evidence/results/80a8eab37ed290d6751d1a9ee288716169fb6bcdfc59c72305ebbb14abe32c68.arrow create mode 100644 evidence/results/80a8eab37ed290d6751d1a9ee288716169fb6bcdfc59c72305ebbb14abe32c68.json create mode 100644 evidence/results/8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001.arrow create mode 100644 evidence/results/8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001.json create mode 100644 evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.arrow create mode 100644 evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.json create mode 100644 evidence/results/c2ac733b842269806829394b1810043d777a96fb247ef8576011b3297d7aeba6.arrow create mode 100644 evidence/results/c2ac733b842269806829394b1810043d777a96fb247ef8576011b3297d7aeba6.json create mode 100644 evidence/results/d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0.arrow create mode 100644 evidence/results/d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0.json create mode 100644 evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.arrow create mode 100644 evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.json create mode 100644 evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.arrow create mode 100644 evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.json create mode 100644 evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.arrow create mode 100644 evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.json create mode 100644 evidence/results/ef1805677b87c659ef657c0c48f8cd2e46e4bcf57bdd1a65fcc393629940962e.arrow create mode 100644 evidence/results/ef1805677b87c659ef657c0c48f8cd2e46e4bcf57bdd1a65fcc393629940962e.json create mode 100644 evidence/results/f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97.arrow create mode 100644 evidence/results/f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97.json diff --git a/evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.arrow b/evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.arrow new file mode 100644 index 0000000000000000000000000000000000000000..8d11afb8eaee1f2969be9dc78bbe769a12495b05 GIT binary patch literal 1690 zcmeHIu}%U(5FO;e6VHSgVk{^qEGlvo zu(YtCG||s6mY(nJadAB$c2-RCc4l_wc6Z*+f?X)&PSPSS5>nLtU`+{Lo+_Fa^sS~jFLzKDvmrxz4fsq>>>~ao`C^y4RCH9Fo5Kj z>oT9VpT7Y6s@^O$iv4y4_MVo*^K5VO^dcAiUd5?2tG$}YWgsV=@p8(wQpeA^(Q>%g z1`q+dqL@8pIkE^eTGcw1Ql~ahHRC>K z(J{_KVXl*$z*zzFk^_AoP6qw<(fvpF?Q@qgetYQT?Bdx$?s(tHp2KIo zjB$MnRg(=19^WUsZVTg@vb?pQxG7}lDM^hq)$Bcc?{r#k&ffFy-@yNP&#$-)Tm2_} E19Af5H2?qr literal 0 HcmV?d00001 diff --git a/evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.json b/evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.json new file mode 100644 index 000000000..08d8d087e --- /dev/null +++ b/evidence/results/099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "694f720c7620559391a91df48bfeee2ae2e2010e89778fda6c53f772a405975d", + "case_hash": "099308c1bd4e67acacdcd0c066c85c58276b6a341822ad6b82d27cb34e606958", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "a1bcdbfffc4621116af9a3ee892e1277176c00dfc6be7d21ac94fc88f60bd27b", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.arrow b/evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.arrow new file mode 100644 index 0000000000000000000000000000000000000000..21a649899eaaff0aaabf4f6f67c6ee43ba550dc9 GIT binary patch literal 1034 zcmeHH!AiqG5S?mFV+~kJ5Wyel!Gran9z3K{1)(5>f?i{YZ6Ktf?ZJW{(4&YS(6eW+ zf=55WfAHcT)c1CivJ|{|^ugPi$;{5q%r0pM!PRw3WC7e)MCwA+#1&gM#DNA>EZ73e z!DD9UQz5uojet>iE0qlYMpanKj596ux9QroF zyCe(K-ZYP)ca4}e*sUX0E^<4a#N9aSPX;2n5yLzOAY-CII4WbFS7ML`y^D;aFy%R~ zhM(sg9_w22Q5+@XWSHUGCt}zu>*d~y%bW8K_>R?84$Z;%O&Xn@ZF7J7W>RqZZ;9vK0SsPv7TXzr8u;-d6vBA9mxK ANB{r; literal 0 HcmV?d00001 diff --git a/evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.json b/evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.json new file mode 100644 index 000000000..a602b0141 --- /dev/null +++ b/evidence/results/0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "9a747da48e1ff19933594429191238e321d830d319644c6f4c93dce045e8e1f6", + "case_hash": "0a497e88e5913300bf33c6efde4a6468260d9502464935759ce5f11b3daa0620", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": false, + "precision": 27, + "scale": 6, + "type_code": 0, + "type_name": "FIXED" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "59ddd17718e3facf69be4fbf2c9d5de585ef51ecc7d15c53cee715f644fbf605", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 4, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.arrow b/evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.arrow new file mode 100644 index 0000000000000000000000000000000000000000..5b1db6aa8e9d6f9d7fa0595c2639dbc588911610 GIT binary patch literal 978 zcmeH`ze>YU6vj{5G{%~tND&7|M+b`z4uW)WD=4AhTuW>Nr7dj-3qC+cN5^iygb(2= z_yD>%x!2!!lVT}4x_RKcIp^N*{Jl5zIKH^5nJti8&#YpSvPBkH$wD;5^RW#pWcBlv zZLD2jw#_%hW5e+(R%J)x6|1a!5apT0&irER06c>^xC2da0DN#sJ>pAO9-VE1o3!8R zHK)S_eX_31ewvxha#Va{oHTcaiC=?6{;-niZBDbMT5c>()~M5<*t zO4{i-9rV#dI@e^>r+QZ1LVR9t@Ubn>8+1}a=G6yq12+S+f}8&gj(jDN?Qo_Wh}=xA z=(|}fekNt?ET?)t1)7&;wG`F*n|#N~d{+JB^Y&8vcz^snqxFSs#g3>>RYzEQ59;uk fb>C@OoLL^|UdPC4p8xlwoBo^kGiUB~_b-0|bLn`Q literal 0 HcmV?d00001 diff --git a/evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.json b/evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.json new file mode 100644 index 000000000..ad443e887 --- /dev/null +++ b/evidence/results/2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "f2971000f2850ad20a752c62bf528cd8fb055ddebec0a796b7be4797a69360ab", + "case_hash": "2dac89cd88bbc44396c19dd809e7695a058bf92dd79586043bcb675d524d5e18", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "9464296322c817d4faec4a7af874e57550c2706cefa177c8a0182d1f9e5d8e1b", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/32abfb26d0c8d0c1c6cfdf62cd154f45e60471601cbae8c552ebaeccb5d49770.arrow b/evidence/results/32abfb26d0c8d0c1c6cfdf62cd154f45e60471601cbae8c552ebaeccb5d49770.arrow new file mode 100644 index 0000000000000000000000000000000000000000..bf5b426f00dc2fb6ef4f8ffc1c32624da2fa131b GIT binary patch literal 986 zcmeHGK}y3w6n)W{M$MwsPPPo z?9$uR_a+HO!KFJNyqWnk|NZ&@XVMRX+q)jHC7vvx3E==99PGj6im+_jpyhJ3%aPl( zIwa8I>{8J%eogq?5v+#K(Wj%7MeITs(00goa!o#yBl3i_$u9Gezt*}(XZz%1l7;DL zna5lYYd$-)d*{j=cvvptVVsQ@6CkhoD9$My@mq2>}9INa6!fT3YfTK7EmJc=EH=X{z@2CI37 zUhHvh@u}XYRgv|w>8Kc+z3*`@E6#A7EjMy+;@9yb_^!7QO=cB^&#BQ&A_bg=D&ucvy#|OiRlZ(Zl=}<+^qc! z)oG{7sh;nEVw#l~)#}CjRiDJ~(iQV@zstWXj9@EUIby0*^)Z&-g*v@qJ$G7p*Sfms XO=0Ax?*DhBckx&6Xv5sQ?w|e!)!cIs literal 0 HcmV?d00001 diff --git a/evidence/results/3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b.json b/evidence/results/3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b.json new file mode 100644 index 000000000..ef40a44ad --- /dev/null +++ b/evidence/results/3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "4d1567221ce2cce4d9a7c55af07b1fb7da4ab6b0d58b497f5feb765b0d844cd5", + "case_hash": "3f25ef5a06b3655830e93b4ab99ddedf722467bdc706043dba1855a3f9671f7b", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "3c0739c0ba8a6c79db0efd6751ad81e65876c84ce8a3a450af0ab6d2cb22a7fd", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.arrow b/evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.arrow new file mode 100644 index 0000000000000000000000000000000000000000..21823bec2a7120b3438e0dbbea674f3f55fa3503 GIT binary patch literal 986 zcmeHGK}y3w6n)W{Mx8~GAoT*$g$s)yF5DDU5DG#l=r)Gf216R!E-ZL}E?szl9>PO- z1aDK{n5{ilutaNu8<28p`!Ie8nj>O`SB4`N21-^3#TNhFN;7z|hjWP}OV+ ex^`8@TUG&IbWAkwll}jefWLT4EB3u_|LISnvwJWA literal 0 HcmV?d00001 diff --git a/evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.json b/evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.json new file mode 100644 index 000000000..f7ca16a7c --- /dev/null +++ b/evidence/results/42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "892ec1e4e758450c3746009c108e9b497e8bef866660d12544c24207257928fa", + "case_hash": "42175beaf6e79d91cc3c9c0b0e3bee882c13e5235d0c7171cded8bfcc8f6039a", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 6, + "type_code": 0, + "type_name": "FIXED" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "7ff0bf4af9164a38f67054747a6f1c372d924b5359adb64a80e3e97db5346032", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.arrow b/evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.arrow new file mode 100644 index 0000000000000000000000000000000000000000..580a76ea0e9faf1f09578a8d74cc18610926b987 GIT binary patch literal 1706 zcmeHI%}PQ+6h7wlC%X$N37;S=TzC=8ppBA2pcQ5mM39;31yNBqLW>@v2WZg)wCDj^ zwruHxxGnp>882S!R=ZXnICIXK`R@RIA1UEiph4&x^bvXos2A()#-J&(JBP7$G^W<9_8iQW3&Xuq z&;YbwtG2k$y=$HOq_ZNdrQ565?7rP;!+!#XXJx(AO5vQxkrimG)3A|BRc+wZTz5DN ze&fs+VxE`>XJyR)##sXQ_!nnI7lvoCJUHvRFb*H&)y>(13#0OYG&g52bL(E5jev1D z7kdCtPoLJX5Db}!_+HtCXj4fjPw(~0A|UVDs6L;%$}HZve!HZ}t6vvy{D0s0 K2p5r4|EMo)=ja*$ literal 0 HcmV?d00001 diff --git a/evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.json b/evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.json new file mode 100644 index 000000000..b9c020a3a --- /dev/null +++ b/evidence/results/456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "ab294485001c4f806ee452218e5d770eb25b300ed0907fb8d6038002402a10fa", + "case_hash": "456e76029907c2304740f8faf8aba301b7d4c3fd0e58c927ac270314973573d6", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 10, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "b033b93a96346eae4bc6b508029f8f5b3aebb3746276aef753b8a98ec6f18471", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/502b3426fb59b9896c7f33fc08294d78c52cd88d120c50aae51850fb611e8475.arrow b/evidence/results/502b3426fb59b9896c7f33fc08294d78c52cd88d120c50aae51850fb611e8475.arrow new file mode 100644 index 0000000000000000000000000000000000000000..9aa623efb03dab47f93ad850f737b548590a5e83 GIT binary patch literal 978 zcmeH`ze>YU6vj{5G{%~tND&7|M+Zd*2bT&=fIro0&@4bnW>6c&!-~+6|3+RJ0;DdYW5nsCU=xiT6W%*#zU(QnW zVuy3hp6Y#E7HKcdN5$CeZHIGSafai;e9L+NfzyJw7!D@N^I1D@U|*6*wan(}Fk58P z96h9SO-6mHXVonvw{ee;9e~Mnlo7J3K7bpz8JIQP{8w<~D~argGu=SsW@=5}&06tG zX=9f;)$k>a;S>stWX~W2Bzv R|NZEu|K|OynS1R1YU6vj{6G{%~tND&7|M+Zd*2SGZx6_ikLjwQB%(w4TM1s|ZJn}SP6AHs+5 z0o;57!M*;zn-ojY(ai(j%{lje=kL9#N74D^f!Q3nEtpkJQnAo{D_eku6g+Ih3Rt~- zWgBZ7m~HY6@K|%as@2$$6iPML9f6DJDsrPSbIYy)WeOuAG-=FRyMd&Y`z}Y&=X+N>A;9Au>ol_5pH0!SUrBnl&g& zcB?O-G^eueXs!bt<3Mv{5n$><2RvBFMyaKvdL?(4vRv0b29`EqVZ# zEnE5^ZEO0zdmS8Xs$DA&oO{l>_nULi{qDI;zFa=JutXfl^O8tRC{ZybBteP5g9Hr3 zf+&K~n2sWV%V|I)0V}cw%k{9r#w3mzCBZmM3^=ap{74DE0u4c5p^wlzDCbg81N#2q zIK)FY&dXbyauu*Dsn!}dJ-Wux%sg=)&dQkojk6@~@h{Fw9t_W7`Eb_rU|c@PtCzC}4@TtyX>uW!n6f$TTvPR-EE}pnd7oOMh;=UJXhX>t z&*%#4_(_PRxK>BAnDtTgn-E#op&H4NcOOp9#~Z`WSoendL=DVwe123YmXn!W%1Uov zo)xU@m0IKZ>98IXEQG+2iHPr&U5GZ7r1JD$V-^8<(?<3A)KzBj#`W7JO@95lc;o;3 M#wWOly!uCd0VJ^K0{{R3 literal 0 HcmV?d00001 diff --git a/evidence/results/76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db.json b/evidence/results/76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db.json new file mode 100644 index 000000000..a00c8a0e3 --- /dev/null +++ b/evidence/results/76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "4ae33509162de3efdf29e33afa349b750e84228ba762e71b88b501e6153da508", + "case_hash": "76b7bdaee975a7500d5b430aa695db4765ffc346b545b0bd74b3b52cad4ba2db", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 10, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "5331dd85b4922133ca51a61b57064dd5375e63e34d185e6b6bfe20ed342d930d", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.arrow b/evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.arrow new file mode 100644 index 0000000000000000000000000000000000000000..a5183d9e8159748af50bd960735f7b7ca260ac33 GIT binary patch literal 1706 zcmeHI%}N4M6h7AYlg&cP!Y2p=7Y>3Mv{5n$><2RvBFMyaKvdL?(4vRv0b29`EqVZ# zEnE5^ZEO0zdmS8Xs$DA&oO{l>_nULi{qDI;zFa=JutXfl^O8tRC{ZybBteP5g9Hr3 zf+&K~n2sWV%V|I)0V}cw%k{9r#w3mzCBZmM3^=ap{74DE0u4c5p^wlzDCbg81N#2q zIK)FY&dXbyauu*Dsn!}dJ-Wux%sg=)&dQkojk6@~@h{Fw9t_W7`Eb_rU|c@PtCzC}4@TtyX>uW!n6f$TTvPR-EE}pnd7oOMh;=UJXhX>t z&*%#4_(_PRxK>BAnDtTgn-E#op&H4NcOOp9#~Z`WSoendL=DVwe123YmXn!W%1Uov zo)xU@m0IKZ>98IXEQG+2iHPr&U5GZ7r1JD$V-^8<(?<3A)KzBj#`W7JO@95lc;o;3 M#wWOly!uCd0VJ^K0{{R3 literal 0 HcmV?d00001 diff --git a/evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.json b/evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.json new file mode 100644 index 000000000..9d90fab6d --- /dev/null +++ b/evidence/results/7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "4ae33509162de3efdf29e33afa349b750e84228ba762e71b88b501e6153da508", + "case_hash": "7d8894d25792c18162d1633addd9bd8c2c4254527891b50003f63937345e1005", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 10, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "32e063a26372c22d7a8d7771e809b70955ad17a456af996379ad0c1afb19db7a", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.arrow b/evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.arrow new file mode 100644 index 0000000000000000000000000000000000000000..580a76ea0e9faf1f09578a8d74cc18610926b987 GIT binary patch literal 1706 zcmeHI%}PQ+6h7wlC%X$N37;S=TzC=8ppBA2pcQ5mM39;31yNBqLW>@v2WZg)wCDj^ zwruHxxGnp>882S!R=ZXnICIXK`R@RIA1UEiph4&x^bvXos2A()#-J&(JBP7$G^W<9_8iQW3&Xuq z&;YbwtG2k$y=$HOq_ZNdrQ565?7rP;!+!#XXJx(AO5vQxkrimG)3A|BRc+wZTz5DN ze&fs+VxE`>XJyR)##sXQ_!nnI7lvoCJUHvRFb*H&)y>(13#0OYG&g52bL(E5jev1D z7kdCtPoLJX5Db}!_+HtCXj4fjPw(~0A|UVDs6L;%$}HZve!HZ}t6vvy{D0s0 K2p5r4|EMo)=ja*$ literal 0 HcmV?d00001 diff --git a/evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.json b/evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.json new file mode 100644 index 000000000..06d934df1 --- /dev/null +++ b/evidence/results/7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "ab294485001c4f806ee452218e5d770eb25b300ed0907fb8d6038002402a10fa", + "case_hash": "7e2aed0b33775d9d70c7a4c89813b57a881ddf42e72d2d5e3210bd75e197a616", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 10, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "688b49d44a216dc7210bbee4f23c525252b977dac71408ec6051131a78a9017f", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/80a8eab37ed290d6751d1a9ee288716169fb6bcdfc59c72305ebbb14abe32c68.arrow b/evidence/results/80a8eab37ed290d6751d1a9ee288716169fb6bcdfc59c72305ebbb14abe32c68.arrow new file mode 100644 index 0000000000000000000000000000000000000000..9aa623efb03dab47f93ad850f737b548590a5e83 GIT binary patch literal 978 zcmeH`ze>YU6vj{5G{%~tND&7|M+Zd*2bT&=fIro0&@4bnW>6c&!-~+6|3+RJ0;DdYW5nsCU=xiT6W%*#zU(QnW zVuy3hp6Y#E7HKcdN5$CeZHIGSafai;e9L+NfzyJw7!D@N^I1D@U|*6*wan(}Fk58P z96h9SO-6mHXVonvw{ee;9e~Mnlo7J3K7bpz8JIQP{8w<~D~argGu=SsW@=5}&06tG zX=9f;)$k>a;S>stWX~W2Bzv R|NZEu|K|OynS1R1-0BPV27y|bI=e7Y2$bb1h z^J$0qE3j|9cCFdjg&%z239aIYO8 z1>DtZE$;IaiF2RQfe6pi?YVXD&g-;ckC4M!d0xUPU$7im1zMekho#i1HB`-b$XR-d zvqX~XuCYxi+3Ih zYnUfICneN@_v452T$8dEC^eFU=sui``@<3C{g5$!dumlo=kla-c5Ice;Im%FxW0j^ z$%X-s?~`4(hjB|;!P>9f6fzE!6egN#@t%WsIy{;D5a54_t<+{*%4|_|W4D literal 0 HcmV?d00001 diff --git a/evidence/results/8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001.json b/evidence/results/8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001.json new file mode 100644 index 000000000..759076d38 --- /dev/null +++ b/evidence/results/8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "702fdfb570fee35a0176af672a6fbfeb9a3204f39678d43399363f758d56372c", + "case_hash": "8eac6a94a6457905c7fe6feda9d8269e0e7fcb93dcaa980ff84c2cccc11c1001", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "df617ed1a85da230c9cbfae929a906a6649b3f636653fb5075d97cc583dcd770", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.arrow b/evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.arrow new file mode 100644 index 0000000000000000000000000000000000000000..bfdd9bb407a3b4b210ebccd3237bfdfedb77ccd1 GIT binary patch literal 1690 zcmeHIu}%U(5FO;e6VHSgVk{^qw6hS=#K!0uu_A$7&S+vR5Kp*-009~jKEO|~q_p${ zEG#W7C@uU03;#fTZ;y-T0kN}UlD9LnGq=0*b{0&_DxPOWMv%t^k)%)(Vn|$KqQinj zH24Bf$Eb}*5u{5gBC-xGvI@=kqRLH53NuP#DX1j!81>f2lCW(c4!i&Zpbv0v3($b{ z*J#9i+J62L>|3`{uG_tq1N$hD!}ILz@bn_ry{=Pm8r5!1FW&u+Z+_8EcAP4L~Y%ya0@LP%@YlyLP4b_Jd)l`3GRFp#kzPBgqsRQJT zckT;om?bSGbWBm5CP&Dn!lj7NNA%6v* z^)klw4OC4w40wE>?7Cfy8_M$5e&(i-p{FD>)>QNN?7h=jy*Yo+zkdV&<2}FOGEDWK F^bG}+;S>M> literal 0 HcmV?d00001 diff --git a/evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.json b/evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.json new file mode 100644 index 000000000..e0bd49e52 --- /dev/null +++ b/evidence/results/b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "b20a26365c7db223f0fe5d5a385cafcb0d8f27522bf340dcc034c24dd0aa17a5", + "case_hash": "b8d3642d16138d583ba8d4870a496d384af66531ac6ccb683589db095bb47b3a", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "5d627750226a7e35597705ae02662e8f5949f1de1f7a38f79fd92d9a627579f9", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/c2ac733b842269806829394b1810043d777a96fb247ef8576011b3297d7aeba6.arrow b/evidence/results/c2ac733b842269806829394b1810043d777a96fb247ef8576011b3297d7aeba6.arrow new file mode 100644 index 0000000000000000000000000000000000000000..ead4d8c0e258baf95cfb46b45ec0c863e5d13ebc GIT binary patch literal 1690 zcmeHIu}%U(5FO;e6VHSgVk{^qEGzf^;c{MAo52R-qYRRJn0UU`9zK0To9cqwe}x5_SoQ0?$AXxB)n~31~p_ zYcyg$Z7+Wb_O)FvJEd;Zf_>o2;dyqqd3uq{ZpX@7^=hXkauvu)XWX1ht=#f*Zhbl2 zYYT_~?Mm6jZL8$^>V3fgJKdUj3XsPRa3e_U4Pv;!H=k zu%p-;e(ua%Hjk+Z?pVDJkOOugwivO0_$|edHN@DthU!BJYpOp}D#{@S-`$h-H~{(L zoqNI>W(m(p@wDsx_})C%q^!6~jpV?;4=02E@crYn|1jKVjNcyT3)#~7QQ_n;pSyt1 zdKu&T2C60-20Xq`cHIuf4Q07&KXFsYz*UkOYpVHscHil&-kiVZ-@k$X@t$9C8Cmt8 F^bN^$PO- z1aDK{n5{ilutaNu8<28p`!Ie8nj>O`SB4`N21-^3#TNhFN;7z|hjWP}OV+ ex^`8@TUG&IbWAkwll}jefWLT4EB3u_|LISnvwJWA literal 0 HcmV?d00001 diff --git a/evidence/results/d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0.json b/evidence/results/d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0.json new file mode 100644 index 000000000..8f147df3b --- /dev/null +++ b/evidence/results/d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "892ec1e4e758450c3746009c108e9b497e8bef866660d12544c24207257928fa", + "case_hash": "d331d8654ea29993a3bcb618a411a0610acc939afde4f242aef1dd55ea0d0ad0", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": 38, + "scale": 6, + "type_code": 0, + "type_name": "FIXED" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "5145c18ed9302f9806c48ea619333041d38bf77ff31023d68214f4ebfea61bfe", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.arrow b/evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.arrow new file mode 100644 index 0000000000000000000000000000000000000000..b21681196ae6598cb1d95bdba890e38f853f05ca GIT binary patch literal 2322 zcmeHJJugH-6g^mL@rsQsQTPE05kjF**aQ(QBAaNfwb*2{gl!}`5`RG=M4|8-M4{YL zDdGnxl?puP%zG2#<@qQTdy_je_uexf_q>_An_XUBTp0#@O1%Y0L(%~cNwgxx5v3_X z8?;hv6JZMwj`e1sze!%;`px$R%dqk26FOKgz~+WWxGGSmUQiFHdFlXF^Iu+lUwDl< z-Nf42Z5OapwbFd4x>?%-CL)-j5L4VL>{`rv45pR1y<(xF zeU6+sQ^d#^ptJ1kmWt)Q@^+QuD`J$Z&MTeF=BL=9lUmu{EORMy>T}}ce|Z1oClldE zdo1xg{KQW$=exO!yC^^75lkFEs}W2bKSwbbm!C@~{vAJcV*bL9vQCmgo zbLY~?&|}2htG*cCav^Vbgk4ah_iBlE`#0Wd5lkF!ClO2>Z#OX*m$yeJ{vB^AX0BJJ z_w!f&QielL2!-4DPaJE$#6u4edeNTY6)g$!49u9Qnhx5*zIspMP@L)PR)X(it*^b^ zM{BxbZ5tP7u2|c~6;oYt_m%HAdpgGqsE5?kIbe1{vjSVOTk1Xi$Dv>2GivudFgOc5 zvX_WA^~&8Ndr8AsI^c|7g=GN!R9mC&by&)C&^detjz-OF)J*%E(x{n$-n;6H`&;}U NYUVTFan}47`T*irUXuU- literal 0 HcmV?d00001 diff --git a/evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.json b/evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.json new file mode 100644 index 000000000..bda1c3707 --- /dev/null +++ b/evidence/results/e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f.json @@ -0,0 +1,79 @@ +{ + "arrow_sha256": "2b407682e24e59c63a688cb7d949de48efdbfa7f7fdf51145c3226af5d236d7a", + "case_hash": "e41e10d93e83728568d14ed4c37d4219b55bc9fd95f8b6078aeff47ee7a5b22f", + "columns": [ + { + "internal_size": null, + "name": "M1", + "nullable": false, + "precision": null, + "scale": null, + "type_code": 3, + "type_name": "DATE" + }, + { + "internal_size": null, + "name": "P1", + "nullable": false, + "precision": null, + "scale": null, + "type_code": 3, + "type_name": "DATE" + }, + { + "internal_size": null, + "name": "M13", + "nullable": false, + "precision": null, + "scale": null, + "type_code": 3, + "type_name": "DATE" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "8402e2b4098ed8e8d4b723f1f081cb8f04a2299e9016ca07f37221c3c523e88f", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 5, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.arrow b/evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.arrow new file mode 100644 index 0000000000000000000000000000000000000000..ace484fd6e10c9e11821c9531888fe6fd952884f GIT binary patch literal 1690 zcmeHIJx>Bb5FO;e6VHSgVk{^q?6eTk#7@tM)g_S28BL4@;_;DOfB+4NKfs@0Nona1 zu&}f+hSI{{BEGlB#q)sJSux4mnc11!-FZ6;rfrul@*-o%(~?M9C@C={DGAYGL1G$w zfv02CCZh<_r5qF4gcezcW_(%YrX_tjjS4v++1fgx}QaBc_Cfb7?J z%zWBG{tE0`vsLk&LC1xC9LnK&4)%C@k()u^wcJ*{-w?TtqAd(CzW_8W3oE6+hWtAN+8H?fpDwT7x04>(Is zaTbeno$L(GY|N9h5ikUzoRx8pe{t52|8_jp~N-SpE(ufkb>{;$$C71eDTf$ zVGZ+y=cEMM_kR3fo@-Lpe5FQm5Z;HAQGdHec|T%|-=0||)44h=ou636Yxt~}F|Kc* zYO-O#FTD)ifozCmc#e4q!8~7jZ`2&|>s{f>Kbi?3} literal 0 HcmV?d00001 diff --git a/evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.json b/evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.json new file mode 100644 index 000000000..bf9e1a088 --- /dev/null +++ b/evidence/results/e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "91e6e5db366e69765de8df0fc0a9ace935d70ea84fa2613a4615714bbe924213", + "case_hash": "e7e52854877fb675bb57c7ec96c34d0579413af28bffd402ce595a3012e80990", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "a744992c1ac0f61ce2d29cdbc7207b6fa18739289978054fc93be594d3f87ad8", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.arrow b/evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.arrow new file mode 100644 index 0000000000000000000000000000000000000000..8d11afb8eaee1f2969be9dc78bbe769a12495b05 GIT binary patch literal 1690 zcmeHIu}%U(5FO;e6VHSgVk{^qEGlvo zu(YtCG||s6mY(nJadAB$c2-RCc4l_wc6Z*+f?X)&PSPSS5>nLtU`+{Lo+_Fa^sS~jFLzKDvmrxz4fsq>>>~ao`C^y4RCH9Fo5Kj z>oT9VpT7Y6s@^O$iv4y4_MVo*^K5VO^dcAiUd5?2tG$}YWgsV=@p8(wQpeA^(Q>%g z1`q+dqL@8pIkE^eTGcw1Ql~ahHRC>K z(J{_KVXl*$z*zzFk^_AoP6qw<(fvpF?Q@qgetYQT?Bdx$?s(tHp2KIo zjB$MnRg(=19^WUsZVTg@vb?pQxG7}lDM^hq)$Bcc?{r#k&ffFy-@yNP&#$-)Tm2_} E19Af5H2?qr literal 0 HcmV?d00001 diff --git a/evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.json b/evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.json new file mode 100644 index 000000000..6afda005c --- /dev/null +++ b/evidence/results/e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2.json @@ -0,0 +1,70 @@ +{ + "arrow_sha256": "694f720c7620559391a91df48bfeee2ae2e2010e89778fda6c53f772a405975d", + "case_hash": "e910211cf32c35936f8ff38258f5e7be5608738abea4b72c0e46321b961f64b2", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": 134217728, + "name": "T", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "3eab6c0a2f5b2272a3922d84c2f6a20ed868fbdb328caf7818135151473e356b", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/ef1805677b87c659ef657c0c48f8cd2e46e4bcf57bdd1a65fcc393629940962e.arrow b/evidence/results/ef1805677b87c659ef657c0c48f8cd2e46e4bcf57bdd1a65fcc393629940962e.arrow new file mode 100644 index 0000000000000000000000000000000000000000..bf5b426f00dc2fb6ef4f8ffc1c32624da2fa131b GIT binary patch literal 986 zcmeHGK}y3w6n)W{M$MwsPPPo z?9$uR_a+HO!KFJNyqWnk|NZ&@XVMRX+q)jHC7vvx3E==99PGj6im+_jpyhJ3%aPl( zIwa8I>{8J%eogq?5v+#K(Wj%7MeITs(00goa!o#yBl3i_$u9Gezt*}(XZz%1l7;DL zna5lYYd$-)d*{j=cvvptVVsQ@6CkhoD9$MYU6vj{5G{%~tND&7|M+b`z4uW)WD=4AhTuW>Nr7dj-3qC+cN5^iygb(2= z_yD>%x!2!!lVT}4x_RKcIp^N*{Jl5zIKH^5nJti8&#YpSvPBkH$wD;5^RW#pWcBlv zZLD2jw#_%hW5e+(R%J)x6|1a!5apT0&irER06c>^xC2da0DN#sJ>pAO9-VE1o3!8R zHK)S_eX_31ewvxha#Va{oHTcaiC=?6{;-niZBDbMT5c>()~M5<*t zO4{i-9rV#dI@e^>r+QZ1LVR9t@Ubn>8+1}a=G6yq12+S+f}8&gj(jDN?Qo_Wh}=xA z=(|}fekNt?ET?)t1)7&;wG`F*n|#N~d{+JB^Y&8vcz^snqxFSs#g3>>RYzEQ59;uk fb>C@OoLL^|UdPC4p8xlwoBo^kGiUB~_b-0|bLn`Q literal 0 HcmV?d00001 diff --git a/evidence/results/f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97.json b/evidence/results/f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97.json new file mode 100644 index 000000000..e93c2dab2 --- /dev/null +++ b/evidence/results/f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97.json @@ -0,0 +1,61 @@ +{ + "arrow_sha256": "f2971000f2850ad20a752c62bf528cd8fb055ddebec0a796b7be4797a69360ab", + "case_hash": "f4c120779a454f18f99d54580a4bb31687cb682aded35fd77e7547ff977cea97", + "columns": [ + { + "internal_size": null, + "name": "R", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "3c3c1b20b43e58d2d9774c447ac32f4aff2b2bde9ed8a36b1fc48d75c470ecf7", + "recorded_at": "2026-11-06T13:12:10.222971+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} From 3bcc29d4b5488667de46e47b21117d826f5e39a1 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:29:14 +0000 Subject: [PATCH 03/10] NVL2: convert three arguments as a CASE on the first being NULL NVL2(a, b, c) of exactly three arguments, a not semi-structured, converts to CASE WHEN a IS NOT NULL THEN b ELSE c END, its branches unified without reading text, as the recorded NUMBER, FLOAT, text and untyped NULL cases show. Text beside another type, a VARIANT first argument and any other arity refuse. --- .../sqlquack-rules/src/catalogue/constants.rs | 8 ++- .../conversion/_helpers/expressions/calls.rs | 5 +- .../_helpers/expressions/conditionals.rs | 30 ++++++++++- .../src/conversion/constants.rs | 2 + .../src/analysis/tests/test_coverage_batch.rs | 52 +++++++++++++++++++ 5 files changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/sqlquack-rules/src/catalogue/constants.rs b/crates/sqlquack-rules/src/catalogue/constants.rs index 956582135..55517fb08 100644 --- a/crates/sqlquack-rules/src/catalogue/constants.rs +++ b/crates/sqlquack-rules/src/catalogue/constants.rs @@ -35,7 +35,7 @@ use crate::conversion::constants::{ LAST_DAY_RULE, LATERAL_SUBQUERY, LEAST_GREATEST, LIKE, LIMIT, LISTAGG_RULE, LOGARITHM, LOGIC, MEDIAN_RULE, MIN_MAX, MIN_MAX_FLOAT, MODE_RULE, NAMED_AGGREGATES, NEGATE, NOT_IN_SUBQUERY, NULL_ARITHMETIC, NULL_BRANCHES, NULL_IF_ZERO_RULE, NULL_SAFE_COMPARISON, NULLIF, - NUMBER_LITERAL, NUMBER_TEXT_FORMAT, NUMERIC_FUNCTION, OBJECT_CONSTRUCT_RULE, + NUMBER_LITERAL, NUMBER_TEXT_FORMAT, NUMERIC_FUNCTION, NVL2_RULE, OBJECT_CONSTRUCT_RULE, OBJECT_DELETE_RULE, OBJECT_INSERT_RULE, OBJECT_KEYS_RULE, OBJECT_PICK_RULE, ORDER_BY, PARENTHESES, PARSE_JSON_RULE, PARSE_JSON_VARIANT, PARSE_URL_RULE, PERCENTILE_RULE, PIVOT_RULE, POSITIONAL_COLUMN, POWER_RULE, PROJECT_FUNCTION_CALL, PROJECT_TABLE_FUNCTION, QUALIFY, @@ -399,6 +399,12 @@ pub const RULES: &[RuleSpec] = &[ conditions: "A BOOLEAN condition; branches unify.", failures: &[], }, + RuleSpec { + id: NVL2_RULE, + constructs: &["fn:NVL2"], + conditions: "Exactly three arguments, the first not semi-structured: the second where the first is not NULL, else the third; branches unify as recorded for NUMBER, FLOAT, text and untyped NULL branches, and text beside another type refuses.", + failures: &[], + }, RuleSpec { id: NULLIF, constructs: &["fn:NULLIF"], diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs index ea2be5126..661a3437a 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs @@ -27,7 +27,7 @@ use crate::conversion::_helpers::expressions::casts::{ }; use crate::conversion::_helpers::expressions::conditionals::unified; use crate::conversion::_helpers::expressions::conditionals::{ - decode, div0, null_if_zero, zero_if_null, + decode, div0, null_if_zero, nvl2, zero_if_null, }; use crate::conversion::_helpers::expressions::hashing::hash_call; use crate::conversion::_helpers::expressions::lambdas::{lambda_call, reduce_call}; @@ -327,6 +327,9 @@ pub(crate) fn convert_function(payload: &Value, site: Site<'_>) -> Result { zoned_text_call(&name, value, &construct, site) } + ("NVL2", [tested, when_set, otherwise]) => { + nvl2((tested, when_set, otherwise), &construct, site) + } ("LEAST" | "GREATEST", parts) if !parts.is_empty() => { least_greatest(&name, parts, &construct, site) } diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/conditionals.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/conditionals.rs index f5e28e313..331b67481 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/conditionals.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/conditionals.rs @@ -23,7 +23,7 @@ use crate::conversion::_helpers::support::tree::{ }; use crate::conversion::constants::{ CASE, CASE_SUBJECT, COALESCE, DECODE_RULE, DIV0_RULE, IFF, NULL_BRANCHES, NULL_IF_ZERO_RULE, - SEMI_STRUCTURED_HOLDING, TEXT_BRANCHES, ZERO_IF_NULL_RULE, + NVL2_RULE, SEMI_STRUCTURED_HOLDING, TEXT_BRANCHES, ZERO_IF_NULL_RULE, }; /// The branches cast to their unified type, and that type; text beside other types refuses. @@ -238,6 +238,34 @@ pub(crate) fn iff(payload: &Value, site: Site<'_>) -> Result { }) } +/// NVL2: the second argument where the first is not NULL, else the third; text beside other types refuses. +pub(crate) fn nvl2( + (tested, when_set, otherwise): (&Value, &Value, &Value), + construct: &Construct, + site: Site<'_>, +) -> Result { + let _rule = site.context.log.rule(NVL2_RULE); + let tested = convert_expression(tested, site)?; + if tested.data_type == SnowflakeType::SemiStructured { + return Err(outside_conditions( + construct.clone(), + "NVL2 of a semi-structured value, whose JSON null no recording shows".to_owned(), + )); + } + let branches = [ + convert_expression(when_set, site)?, + convert_expression(otherwise, site)?, + ]; + let (cast_branches, target) = unified(&branches, construct, site.context)?; + Ok(Emitted::new( + format!( + "(CASE WHEN ({}) IS NOT NULL THEN {} ELSE {} END)", + tested.sql, cast_branches[0], cast_branches[1] + ), + target, + )) +} + /// Searched and simple CASE. pub(crate) fn case_expression(payload: &Value, site: Site<'_>) -> Result { let construct = node_construct("case"); diff --git a/crates/sqlquack-rules/src/conversion/constants.rs b/crates/sqlquack-rules/src/conversion/constants.rs index 006eb3e0f..2f7fb6e95 100644 --- a/crates/sqlquack-rules/src/conversion/constants.rs +++ b/crates/sqlquack-rules/src/conversion/constants.rs @@ -605,6 +605,8 @@ pub const CASE: &str = "rule.case"; pub const COALESCE: &str = "rule.coalesce"; /// IFF. pub const IFF: &str = "rule.iff"; +/// NVL2 of a non-semi-structured value and two branches that unify without reading text. +pub const NVL2_RULE: &str = "rule.nvl2"; /// NULLIF. pub const NULLIF: &str = "rule.nullif"; /// Casts between NUMBER and FLOAT. diff --git a/crates/sqlquack/src/analysis/tests/test_coverage_batch.rs b/crates/sqlquack/src/analysis/tests/test_coverage_batch.rs index bf4bad852..a9d57bfee 100644 --- a/crates/sqlquack/src/analysis/tests/test_coverage_batch.rs +++ b/crates/sqlquack/src/analysis/tests/test_coverage_batch.rs @@ -1386,3 +1386,55 @@ fn given_urls_when_converting_parse_url_then_parts_and_parameters_are_read_as_re ); } } + +#[test] +fn given_nvl2_when_converting_then_the_branch_follows_whether_the_first_is_null_or_it_refuses() { + let test_cases = [ + FirstValueTestCase { + description: "the second argument where the first is not NULL", + sql: "SELECT NVL2(1, 'set', 'unset') AS v", + expected_outcome: Ok("set"), + }, + FirstValueTestCase { + description: "the third argument where the first is NULL", + sql: "SELECT NVL2(NULL, 'set', 'unset') AS v", + expected_outcome: Ok("unset"), + }, + FirstValueTestCase { + description: "a NUMBER and a FLOAT branch unify to FLOAT", + sql: "SELECT NVL2(NULL, 1, 2.5::FLOAT) AS v", + expected_outcome: Ok("2.5"), + }, + FirstValueTestCase { + description: "text beside a NUMBER, whose reading no recording shows", + sql: "SELECT NVL2(1, '7', 8) AS v", + expected_outcome: Err("no proven common type"), + }, + FirstValueTestCase { + description: "a semi-structured first argument", + sql: "SELECT NVL2(PARSE_JSON('null'), 1, 2) AS v", + expected_outcome: Err("semi-structured"), + }, + FirstValueTestCase { + description: "four arguments, which Snowflake rejects", + sql: "SELECT NVL2(1, 2, 3, 4) AS v", + expected_outcome: Err("NVL2"), + }, + FirstValueTestCase { + description: "two arguments, which Snowflake rejects", + sql: "SELECT NVL2(1, 2) AS v", + expected_outcome: Err("NVL2"), + }, + ]; + for test_case in test_cases { + assert_eq!( + shop_first_outcome(test_case.sql, test_case.expected_outcome), + test_case + .expected_outcome + .map(str::to_owned) + .map_err(str::to_owned), + "{}", + test_case.description + ); + } +} From b204059311613ee1c3a162d079380b963a41b2fb Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:38:46 +0000 Subject: [PATCH 04/10] cases: VARIANCE rounding tie, result scales and FLOAT cancellation The receipts show VARIANCE of a NUMBER is a rounded NUMBER and STDDEV the square root of that rounded value; these cases pin the rounding at an exact tie, the scale rule beyond scales 0 and 2, and how a FLOAT variance near 1e9 cancels. --- .../functions/variance.variance.toml | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/cases/translation/functions/variance.variance.toml b/cases/translation/functions/variance.variance.toml index a97e18d45..cac26b93d 100644 --- a/cases/translation/functions/variance.variance.toml +++ b/cases/translation/functions/variance.variance.toml @@ -132,3 +132,168 @@ rows = [ query = "SELECT VARIANCE(x) AS r, SYSTEM$TYPEOF(VARIANCE(x)) AS t FROM input" order = "ordered" tags = ["type-combination"] + +[[case]] +id = "t009_rounding_tie" +description = "One 1 and 127 zeros: the sample variance is exactly 1/128 = 0.0078125, a tie at six decimals, with STDDEV beside it. Catches: half-to-even (0.007812) instead of half-up (0.007813), and STDDEV taken from the unrounded variance" +columns = [{ name = "x", type = "NUMBER(38,0)" }] +rows = [ + ["1"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], + ["0"], +] +query = "SELECT VARIANCE(x) AS v, VAR_POP(x) AS p, STDDEV(x) AS s FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t010_scales" +description = "VARIANCE and VAR_POP of NUMBER columns of scales 1, 3, 5 and 12, with their types. Catches: the result scale rule (6 for scale 0 and 10 for scale 2 are recorded) misread" +columns = [{ name = "a", type = "NUMBER(38,1)" }, { name = "b", type = "NUMBER(38,3)" }, { name = "c", type = "NUMBER(38,5)" }, { name = "d", type = "NUMBER(38,12)" }] +rows = [ + ["1.5", "1.125", "1.00001", "1.000000000001"], + ["2.0", "2.500", "2.00003", "2.000000000005"], + ["4.5", "4.875", "4.00007", "4.000000000007"], +] +query = "SELECT VARIANCE(a) AS va, SYSTEM$TYPEOF(VARIANCE(a)) AS ta, VARIANCE(b) AS vb, SYSTEM$TYPEOF(VARIANCE(b)) AS tb, VARIANCE(c) AS vc, SYSTEM$TYPEOF(VARIANCE(c)) AS tc, VARIANCE(d) AS vd, SYSTEM$TYPEOF(VARIANCE(d)) AS td, VAR_POP(b) AS pb, SYSTEM$TYPEOF(VAR_POP(b)) AS tpb FROM input" +order = "ordered" +tags = ["type-combination"] + +[[case]] +id = "t011_float_large_mean" +description = "A FLOAT column near 1e9 with a spread of 1, and a negative one. Catches: a stable algorithm where Snowflake cancels, or the reverse" +columns = [{ name = "x", type = "FLOAT" }] +rows = [ + ["1000000001"], + ["1000000002"], + ["1000000003"], + ["1000000004"], +] +query = "SELECT VARIANCE(x) AS v, VAR_POP(x) AS p, STDDEV(x) AS s, VARIANCE(-x) AS n FROM input" +order = "ordered" +tags = ["type-combination"] From a586f831b08505a3ec243c95cb19f759ef664d39 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 13:55:38 +0000 Subject: [PATCH 05/10] evidence: D267 variance rounding ties, scales and a large-mean FLOAT Recorded from fix/d267-functions b2040593 (3 cases, utc-v1, no failures). --- ...644a083325d6aec4bd3045c3c04e331339da.arrow | Bin 0 -> 2306 bytes ...1644a083325d6aec4bd3045c3c04e331339da.json | 79 ++++++++++ ...26b498466b88727498b18c798646499b7039.arrow | Bin 0 -> 2906 bytes ...a26b498466b88727498b18c798646499b7039.json | 88 +++++++++++ ...05e2f4de3e6d91402a1268aedca4cc2d62f4.arrow | Bin 0 -> 7034 bytes ...805e2f4de3e6d91402a1268aedca4cc2d62f4.json | 142 ++++++++++++++++++ 6 files changed, 309 insertions(+) create mode 100644 evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.arrow create mode 100644 evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.json create mode 100644 evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.arrow create mode 100644 evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.json create mode 100644 evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.arrow create mode 100644 evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.json diff --git a/evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.arrow b/evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.arrow new file mode 100644 index 0000000000000000000000000000000000000000..7eb2fa392b1b64fdf5e2dbf163db16a3fa197cd7 GIT binary patch literal 2306 zcmeH}zb}J97{|ZWs^X1^G-48ih=id+#3oJrh@?WQ!CFnxq^X83O|1R{gVAgjv(X|E zlb8$!qtSrZ_xrxR^t`;-EWPA;@1A?UclW%X-rd`DHoLMh0k|wTQJ@zpU2qUbI}&WM zL}K(oFTrQ5@dPMGtqn*;)C-)wxh}=gB-=^$N$tSfBEXV z(yJ}KVx6?@QYBx`)vHCe9|M{h_9@4$daal#R(5K;T$g#+^Q0P@!ft-wXvV+Mv{QFb z$d|QF#)|V5G0BCLOLf0kC>@mcDr!@scU8W!?0h=I3f;8w-cE^AHL{MAMcjLLpbfY0 z9;c2lPOBXM6Q?IDw=hl)*Dwr}|HSlHPAN;HdA{S63urhE_eGOa&5DO{I-$mJ(%6Ni z&G|XjK~4$ukfXoLMi>_ZpbrBcy$){=_9Dq1l}ISRbw+>UH_E+k*o5)RSeh_?RZA1b z?>L|d@;kTUVf^l>`5Qmy3qKBZpU6YwRUGt_jZU`X@kBI6zQCv#)uW5PKab9p&Zt=F z{Y>$H7vp`drByMt?=^kXKV$-p+V}~pU-yf>u0p^0M}yV`Y%T`;vA5;>>(h%_agkE_ zIHaQ!>d@0Yr_(&e=djoE*L+YiEYE&r@oL?Rt$Ps&p6k}V*!+&CcxH5$*x%p(z87D3 Jp3~lcpAXIGSHb`Q literal 0 HcmV?d00001 diff --git a/evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.json b/evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.json new file mode 100644 index 000000000..44d099b44 --- /dev/null +++ b/evidence/results/6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da.json @@ -0,0 +1,79 @@ +{ + "arrow_sha256": "16e82fbeb06392fad1ea6fda6b9d1946200f53c9fb5fa6147b3ff95f68398ab6", + "case_hash": "6ef46e4d8df1537126d4962d6461644a083325d6aec4bd3045c3c04e331339da", + "columns": [ + { + "internal_size": null, + "name": "V", + "nullable": true, + "precision": 38, + "scale": 6, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "P", + "nullable": true, + "precision": 38, + "scale": 6, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "S", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "d83cafa25756a6e76aa011c40b6d8515416f29507494e9adce3cd5352511231d", + "recorded_at": "2026-11-06T13:53:43.897300+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.arrow b/evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.arrow new file mode 100644 index 0000000000000000000000000000000000000000..2f0188bf997600e0e65b85f022942ea8280b4fc7 GIT binary patch literal 2906 zcmeH}y-EW?6os!zT%&FgBuEO26c!d1iWU|Y2DA$*F=%UyQ3D~0rV+%-B99;@^{h655cs;z++F zQG)d)P#4sacqZCUmPDFyk$g;KqA#dlyn7xx#M6kmXk(9HOthlB40qrU@N4)9ya=C! zbG)ZF&!e7Y>KPk^?^NrhTJiXxg7SMvGlw?jxpmyACk8v zEt-Dlj>@GP=h;?qe4@-?BIa^%SSeSJs{3`6o6zuEnJ>Mtl*{8K!|>YvZWTlMS)W2D z5|g`+zxk}8-(P%MDyJwuW0*sKyN%C6NMqx(ZP92xXDS}W=N6i7_*_9pKF^;&eA1Tn zv)X}A2L1K7+t$x~NMqx(8PeGJoLMxQ&y9*l@p*uz8$MhW4ptb~ModN|gPrBbfWI$O zl12+H*mq+VZtIA$?<4#g9^QAW*#DilH9{I2xATz3#_iss(cE5CJc?TqU(S<==S#Qz zNQ?|f5L0*=aAIGYE2D_<#B+V75>J3J&JhNE8$vyplV{B(oV+l^w=tI}GcofvI#V}q zj;YPmO`JJtGj$VZ%$%KCr}Jccr@HQIE0Z7FnV$=aj!!NXutNs3Xdj20Tv{x^vh9hp uel2+P?v>uXqW6FAUip0%Php#AKD~QIgiLr2>;H5A;a*AMhsyc?slNbqUchSr literal 0 HcmV?d00001 diff --git a/evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.json b/evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.json new file mode 100644 index 000000000..63641ccd7 --- /dev/null +++ b/evidence/results/76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039.json @@ -0,0 +1,88 @@ +{ + "arrow_sha256": "0d37df618c4099348a4ef009fe33e0ea5c47a14037864aee82fa8bf80ec090d4", + "case_hash": "76356a69488e7d2ff157faaec04a26b498466b88727498b18c798646499b7039", + "columns": [ + { + "internal_size": null, + "name": "V", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": null, + "name": "P", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": null, + "name": "S", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + }, + { + "internal_size": null, + "name": "N", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 1, + "type_name": "REAL" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "a69256a8d928db3f7de91f984112dbbc38c12c7f927758a71320166c1a624eaf", + "recorded_at": "2026-11-06T13:53:43.897300+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.arrow b/evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.arrow new file mode 100644 index 0000000000000000000000000000000000000000..572a9ef585ea7f1ccbbfedc33504bffd6e816745 GIT binary patch literal 7034 zcmeHMJ!lj`6n@dm8THhtho}gqP>CRN7e!C7NMcSDqlUQt;2(+c)If+ww1|)*f`z3; z>_nniSXfC43yTOAmKGL4Qdn9B@!#nBz1e--yJ2=$JCPljnSJ}deKYgzH#;+T9lgCr zPZo)Mq?x5e&BSR5#qufrfK)CppO@BT(5LjhA&^gLbTyiEe>OfdjibBmHrXIv?~m^RY1nD z=-NR45VzB({2XIPiHQ4B9jOcqUK_kRjO$y#@L0LOoW1fX9u9$G8oGLU5TQ)3+rRn# zTfcBje_GthqU>FeXl0JvU>P-^I-&Fv1P< zy8qy*au;fwn^(YX!F|?V8+bzH)DQQ=Lx4E_E zySRA=jBpdDpC<4k`WXRFvHIB;uOAPwWd20yXH;RL^mEU`xb!ovFv6qO&pYLh(og1l zjiWgIO#k%#(}tTE{UmTxMO#VSOejp0ex50ejN8`FtirhTGjI7_`f0&<|3p6rfD`>Z z1y7=%W^_)kU)&F`GhROvh?T_AGlk)puJ6yRg>mVJ=Ih%kaL18l{c2HuZpXz@7cjz6 zoPIJ`-PnKM@Dpe#@8IV|0zXr#PyFsZWP5#|Q5f!b(eVwMSC}XbwVzh+w9r615sQMhm-`ZdG+(jEnA;jC+jCYhu40ucYTA>b2t&NUFkWzHkbIE;vw6={roTIaNh8DnE%{<1O4Th A=l}o! literal 0 HcmV?d00001 diff --git a/evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.json b/evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.json new file mode 100644 index 000000000..9dca9a315 --- /dev/null +++ b/evidence/results/b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4.json @@ -0,0 +1,142 @@ +{ + "arrow_sha256": "107dfda79354e1996828f9f8100f62b072fc3ba391e37c42c5ac5e720c9e06af", + "case_hash": "b75a59630d3ee20acdd54da6671805e2f4de3e6d91402a1268aedca4cc2d62f4", + "columns": [ + { + "internal_size": null, + "name": "VA", + "nullable": true, + "precision": 38, + "scale": 8, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TA", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + }, + { + "internal_size": null, + "name": "VB", + "nullable": true, + "precision": 38, + "scale": 12, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TB", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + }, + { + "internal_size": null, + "name": "VC", + "nullable": true, + "precision": 38, + "scale": 12, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TC", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + }, + { + "internal_size": null, + "name": "VD", + "nullable": true, + "precision": 38, + "scale": 12, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TD", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + }, + { + "internal_size": null, + "name": "PB", + "nullable": true, + "precision": 38, + "scale": 12, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TPB", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "625d1464c0114801d7e453966a10fa5229aeefc14d779122ccddd5243b73ae78", + "recorded_at": "2026-11-06T13:53:43.897300+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 1, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} From 45fb9ed9786d738811329c9f1d7b7849988efb9d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 15:22:34 +0000 Subject: [PATCH 06/10] refactor: move the format-element table into a text_formats domain conversion/constants.rs reached 2,002 lines with main merged in. The TO_CHAR format elements, month-name adjacency, bare FF and separators now live in text_formats with strftime_pattern and clock_format, which read them. No behaviour change. --- .../conversion/_helpers/expressions/casts.rs | 9 +- .../expressions/temporal_functions.rs | 103 ++---------------- .../src/conversion/constants.rs | 31 ------ crates/sqlquack-rules/src/lib.rs | 1 + .../src/text_formats/_helpers/adjacency.rs | 60 ++++++++++ .../src/text_formats/_helpers/mod.rs | 1 + .../src/text_formats/constants.rs | 33 ++++++ .../src/text_formats/main/clock_format.rs | 18 +++ .../src/text_formats/main/mod.rs | 2 + .../src/text_formats/main/strftime_pattern.rs | 47 ++++++++ crates/sqlquack-rules/src/text_formats/mod.rs | 5 + 11 files changed, 181 insertions(+), 129 deletions(-) create mode 100644 crates/sqlquack-rules/src/text_formats/_helpers/adjacency.rs create mode 100644 crates/sqlquack-rules/src/text_formats/_helpers/mod.rs create mode 100644 crates/sqlquack-rules/src/text_formats/constants.rs create mode 100644 crates/sqlquack-rules/src/text_formats/main/clock_format.rs create mode 100644 crates/sqlquack-rules/src/text_formats/main/mod.rs create mode 100644 crates/sqlquack-rules/src/text_formats/main/strftime_pattern.rs create mode 100644 crates/sqlquack-rules/src/text_formats/mod.rs diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/casts.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/casts.rs index d55811335..a9fed0877 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/casts.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/casts.rs @@ -62,11 +62,12 @@ use crate::conversion::constants::{ CAST_BOOLEAN, CAST_NUMERIC, CAST_PARSED_TEXT, CAST_SAME_TYPE, CAST_TEMPORAL, CAST_TEXT_LITERAL, CAST_TEXT_TIME, CAST_TEXT_TIMESTAMP, CAST_TO_TEXT, CAST_UTC_ZONED, FLOAT_SCALE_FACET, FLOAT_TO_DOUBLE, KEEP_NULL_CONSTRUCTOR, KIND_CAST_TARGETS, MICROSECOND_DIGITS, - NTZ_OUTPUT_PATTERN, OBJECT_CONSTRUCTOR, OBJECT_TYPE_NAME, TARGET_FACET, TEXT_TYPE_PAIR, - TEXT_TYPE_SPELLINGS, TIME_CAST, TIMESTAMP_TIME_RULE, TO_BOOLEAN_RULE, TO_DOUBLE_PAIR, - TO_NUMBER_PAIR, TO_VARCHAR_PAIR, TO_VARIANT_RULE, VARIANT_CAST, VARIANT_KIND_CAST, - VARIANT_NAME, VARIANT_TEXT_FUNCTION, VARIANT_TO_VARCHAR, + OBJECT_CONSTRUCTOR, OBJECT_TYPE_NAME, TARGET_FACET, TEXT_TYPE_PAIR, TEXT_TYPE_SPELLINGS, + TIME_CAST, TIMESTAMP_TIME_RULE, TO_BOOLEAN_RULE, TO_DOUBLE_PAIR, TO_NUMBER_PAIR, + TO_VARCHAR_PAIR, TO_VARIANT_RULE, VARIANT_CAST, VARIANT_KIND_CAST, VARIANT_NAME, + VARIANT_TEXT_FUNCTION, VARIANT_TO_VARCHAR, }; +use crate::text_formats::constants::NTZ_OUTPUT_PATTERN; use sqlquack_policy::policy::constants::{ DATE_TEXT_SPELLING, SESSION_TIMEZONE_UTC, TIMESTAMP_RANGE, TIMESTAMP_TEXT_ROUNDING, TIMESTAMP_TZ_OFFSET, diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs index 37b8073a9..c7bbb05e2 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs @@ -58,16 +58,16 @@ use crate::conversion::_helpers::support::tree::{ field, node_construct, payload_object, require_understood, }; use crate::conversion::constants::{ - BARE_FRACTION, BARE_FRACTION_PATTERNS, CLOCK_FORMAT_FIELDS, CURRENT_TIME_RULE, DATE_ARITHMETIC, - DATE_FORMAT_RULE, DATE_NAME, DATE_PART_RULE, DATE_PARTS, DAY_NAME_RULE, DECIMAL_EPOCH_RULE, - FRACTIONAL_DATE_AMOUNT, FROM_PARTS, LAST_DAY_RULE, MAX_FRACTION_DIGITS_U8, MICROSECOND_DIGITS, - MONTH_NAME_FIELDS, MONTH_NAME_FOLLOWERS, NULL_BRANCHES, NUMBER_TEXT_FORMAT, OFFSET_FREE_UNITS, - RECORDED_TIME_ZONES, SCALE_DIGITS, TEMPORAL_TEXT_FIELDS, TEMPORAL_TEXT_FORMAT, - TEMPORAL_TEXT_SEPARATORS, TEXT_TO_TIME_RULE, TEXT_TO_TIMESTAMP_RULE, TIME_ARITHMETIC, - TIME_ZONE_CONVERSION, TIMESTAMP_FORMAT_RULE, TIMESTAMP_NTZ_NAME, TIMESTAMP_TIME_RULE, - TO_DATE_TEMPORAL, TO_TIMESTAMP_TEMPORAL, UTC_OFFSET_ARITHMETIC, UTC_ZONE, ZONE_COLUMNS, - ZONED_PARTS, + CURRENT_TIME_RULE, DATE_ARITHMETIC, DATE_FORMAT_RULE, DATE_NAME, DATE_PART_RULE, DATE_PARTS, + DAY_NAME_RULE, DECIMAL_EPOCH_RULE, FRACTIONAL_DATE_AMOUNT, FROM_PARTS, LAST_DAY_RULE, + MAX_FRACTION_DIGITS_U8, MICROSECOND_DIGITS, NULL_BRANCHES, NUMBER_TEXT_FORMAT, + OFFSET_FREE_UNITS, RECORDED_TIME_ZONES, SCALE_DIGITS, TEMPORAL_TEXT_FORMAT, TEXT_TO_TIME_RULE, + TEXT_TO_TIMESTAMP_RULE, TIME_ARITHMETIC, TIME_ZONE_CONVERSION, TIMESTAMP_FORMAT_RULE, + TIMESTAMP_NTZ_NAME, TIMESTAMP_TIME_RULE, TO_DATE_TEMPORAL, TO_TIMESTAMP_TEMPORAL, + UTC_OFFSET_ARITHMETIC, UTC_ZONE, ZONE_COLUMNS, ZONED_PARTS, }; +use crate::text_formats::main::clock_format::clock_format; +use crate::text_formats::main::strftime_pattern::strftime_pattern; use sqlquack_duckdb::raises::main::raised::raised; use sqlquack_policy::policy::constants::{ CURRENT_TIME, DATE_TEXT_SPELLING, SESSION_TIMEZONE_UTC, TIME_ZONE_SPELLING, TIMESTAMP_RANGE, @@ -952,21 +952,6 @@ pub(crate) fn temporal_text( )) } -/// Whether a format names only clock fields, HH24, MI, SS and up to six fraction digits. -fn clock_format(written: &str) -> bool { - let mut rest = written; - while !rest.is_empty() { - let Some(field) = CLOCK_FORMAT_FIELDS - .iter() - .find(|field| rest.starts_with(**field)) - else { - return false; - }; - rest = &rest[field.len()..]; - } - true -} - /// TO_CHAR of a NUMBER with a format `FM9...90.0...0` its whole digits always hold. fn number_text( (sql, precision, scale): (&str, u8, u8), @@ -1019,76 +1004,6 @@ fn fixed_number_format(written: &str) -> Option<(u8, u8)> { Some((whole, fraction)) } -/// The strftime pattern of a format of known fields between separators, or None. -fn strftime_pattern(format: &str, precision: Option) -> Option { - let with_time = precision.is_some(); - let upper = format.to_ascii_uppercase(); - let mut rest = upper.as_str(); - let mut pattern = String::new(); - let mut last_letter: Option = None; - let mut previous: Option<&str> = None; - while !rest.is_empty() { - let written = &format[format.len() - rest.len()..]; - if let Some(after) = rest.strip_prefix(BARE_FRACTION) - && !after.starts_with(|next: char| next.is_ascii_digit()) - { - if !adjacency_recorded(previous, BARE_FRACTION) { - return None; - } - let (_, digits) = BARE_FRACTION_PATTERNS - .iter() - .find(|(digits, _)| Some(*digits) == precision)?; - pattern.push_str(digits); - rest = after; - last_letter = None; - previous = Some(BARE_FRACTION); - continue; - } - let field = TEMPORAL_TEXT_FIELDS - .iter() - .find(|(token, _, time)| rest.starts_with(token) && (with_time || !time)); - match field { - Some((token, replacement, _)) => { - if last_letter.is_some_and(|letter| token.starts_with(letter)) - || !adjacency_recorded(previous, token) - || (MONTH_NAME_FIELDS.contains(token) && !written.starts_with(token)) - { - return None; - } - pattern.push_str(replacement); - rest = &rest[token.len()..]; - last_letter = token.chars().last().filter(char::is_ascii_alphabetic); - previous = Some(token); - } - None => { - last_letter = None; - previous = None; - let separator = rest.chars().next()?; - if !TEMPORAL_TEXT_SEPARATORS.contains(separator) { - return None; - } - pattern.push(separator); - rest = &rest[separator.len_utf8()..]; - } - } - } - Some(pattern) -} - -/// Whether `next` may follow the field `previous` with no separator between, as recordings show. -fn adjacency_recorded(previous: Option<&str>, next: &str) -> bool { - match previous { - None => true, - Some(previous) => match MONTH_NAME_FOLLOWERS - .iter() - .find(|(name, _)| *name == previous) - { - Some((_, followers)) => followers.contains(&next), - None => !MONTH_NAME_FIELDS.contains(&next), - }, - } -} - /// The value of up to two decimal digits. fn small_integer(digits: &str) -> Option { if digits.is_empty() || digits.len() > SCALE_DIGITS { diff --git a/crates/sqlquack-rules/src/conversion/constants.rs b/crates/sqlquack-rules/src/conversion/constants.rs index 25934cc97..4f937c834 100644 --- a/crates/sqlquack-rules/src/conversion/constants.rs +++ b/crates/sqlquack-rules/src/conversion/constants.rs @@ -881,33 +881,6 @@ pub const TO_NUMBER_PAIR: &str = "to_number_to_cast"; pub const TO_VARCHAR_PAIR: &str = "to_varchar_to_cast"; /// TO_VARCHAR and TO_CHAR of a date or timestamp with a literal format. pub const TEMPORAL_TEXT_FORMAT: &str = "rule.temporal_text_format"; -/// Format fields: the Snowflake token, its strftime spelling, and whether it is a time field. -pub const TEMPORAL_TEXT_FIELDS: &[(&str, &str, bool)] = &[ - ("YYYY", "%Y", false), - ("MMMM", "%B", false), - ("MON", "%b", false), - ("MM", "%m", false), - ("DD", "%d", false), - ("HH24", "%H", true), - ("MI", "%M", true), - ("SS", "%S", true), - ("FF9", "%n", true), - ("FF6", "%f", true), - ("FF3", "%g", true), -]; -/// Fields writing an English month name, recorded only as written in upper case. -pub(crate) const MONTH_NAME_FIELDS: &[&str] = &["MMMM", "MON"]; -/// The fields recorded directly after each month name: none after MON, whose MONMM writes the full name. -pub(crate) const MONTH_NAME_FOLLOWERS: &[(&str, &[&str])] = &[("MON", &[]), ("MMMM", &["DD"])]; -/// The format element writing as many fraction digits as the timestamp's precision. -pub(crate) const BARE_FRACTION: &str = "FF"; -/// What FF writes for each precision recorded: none for 0, then milli-, micro- and nanoseconds. -pub(crate) const BARE_FRACTION_PATTERNS: &[(u8, &str)] = - &[(0, ""), (3, "%g"), (6, "%f"), (9, "%n")]; -/// Snowflake's TIMESTAMP_NTZ output format, YYYY-MM-DD HH24:MI:SS.FF3, as a strftime pattern. -pub const NTZ_OUTPUT_PATTERN: &str = "%Y-%m-%d %H:%M:%S.%g"; -/// Characters a format may hold between fields, written as they are. -pub const TEMPORAL_TEXT_SEPARATORS: &str = "-_/:. "; /// The pair behind converting `TO_DOUBLE` and `TRY_TO_DOUBLE` as a cast to FLOAT. pub const TO_DOUBLE_PAIR: &str = "to_double_to_cast"; /// The pair behind converting `MOD(a, b)` as `a % b`. @@ -1357,10 +1330,6 @@ pub const DOLLAR_STRING_KIND: &str = "dollar_string"; pub const NULL_ARITHMETIC: &str = "rule.null_arithmetic"; /// The precision an untyped NULL widens a sum to, as recorded. pub const NULL_SUM_PRECISION: u8 = 18; -/// The pieces of a TIME format recorded through TO_VARCHAR: clock fields and separators. -pub const CLOCK_FORMAT_FIELDS: &[&str] = &[ - "HH24", "MI", "SS", "FF1", "FF2", "FF3", "FF4", "FF5", "FF6", ":", ".", -]; /// IFF, which passes either branch up as it is. pub const IFF_NAME: &str = "IFF"; /// Text every converted window function holds, which a window cannot hold inside its partition. diff --git a/crates/sqlquack-rules/src/lib.rs b/crates/sqlquack-rules/src/lib.rs index 4c23bf847..121a48d53 100644 --- a/crates/sqlquack-rules/src/lib.rs +++ b/crates/sqlquack-rules/src/lib.rs @@ -5,3 +5,4 @@ pub mod catalogue; pub mod conversion; pub mod session; +pub(crate) mod text_formats; diff --git a/crates/sqlquack-rules/src/text_formats/_helpers/adjacency.rs b/crates/sqlquack-rules/src/text_formats/_helpers/adjacency.rs new file mode 100644 index 000000000..3fb64ad6d --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/_helpers/adjacency.rs @@ -0,0 +1,60 @@ +//! Which format fields may follow one another with no separator between. + +use crate::text_formats::constants::{ + BARE_FRACTION, BARE_FRACTION_PATTERNS, MONTH_NAME_FIELDS, MONTH_NAME_FOLLOWERS, + TEMPORAL_TEXT_FIELDS, TEMPORAL_TEXT_SEPARATORS, +}; + +/// Whether `next` may follow the field `previous` with no separator between, as recordings show. +pub(crate) fn adjacency_recorded(previous: Option<&str>, next: &str) -> bool { + match previous { + None => true, + Some(previous) => match MONTH_NAME_FOLLOWERS + .iter() + .find(|(name, _)| *name == previous) + { + Some((_, followers)) => followers.contains(&next), + None => !MONTH_NAME_FIELDS.contains(&next), + }, + } +} + +/// What a bare FF writes after `previous` at `precision`, or None where no recording shows it. +pub(crate) fn bare_fraction(previous: Option<&str>, precision: Option) -> Option<&'static str> { + if !adjacency_recorded(previous, BARE_FRACTION) { + return None; + } + BARE_FRACTION_PATTERNS + .iter() + .find(|(digits, _)| Some(*digits) == precision) + .map(|(_, pattern)| *pattern) +} + +/// Whether `token` may stand here, after `previous`, written as recorded (month names upper case). +pub(crate) fn field_recorded( + token: &str, + last_letter: Option, + previous: Option<&str>, + written: &str, +) -> bool { + !(last_letter.is_some_and(|letter| token.starts_with(letter)) + || !adjacency_recorded(previous, token) + || (MONTH_NAME_FIELDS.contains(&token) && !written.starts_with(token))) +} + +/// The field `rest` starts with: its token, strftime spelling and whether it is a time field. +pub(crate) fn field_at( + rest: &str, + with_time: bool, +) -> Option<&'static (&'static str, &'static str, bool)> { + TEMPORAL_TEXT_FIELDS + .iter() + .find(|(token, _, time)| rest.starts_with(token) && (with_time || !time)) +} + +/// The separator `rest` starts with, or None when it starts with no recorded separator. +pub(crate) fn separator_at(rest: &str) -> Option { + rest.chars() + .next() + .filter(|separator| TEMPORAL_TEXT_SEPARATORS.contains(*separator)) +} diff --git a/crates/sqlquack-rules/src/text_formats/_helpers/mod.rs b/crates/sqlquack-rules/src/text_formats/_helpers/mod.rs new file mode 100644 index 000000000..59129057f --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/_helpers/mod.rs @@ -0,0 +1 @@ +pub(crate) mod adjacency; diff --git a/crates/sqlquack-rules/src/text_formats/constants.rs b/crates/sqlquack-rules/src/text_formats/constants.rs new file mode 100644 index 000000000..ed0e26dad --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/constants.rs @@ -0,0 +1,33 @@ +//! Format elements: their strftime spellings, the month names, fractions and separators. + +/// Format fields: the Snowflake token, its strftime spelling, and whether it is a time field. +pub(crate) const TEMPORAL_TEXT_FIELDS: &[(&str, &str, bool)] = &[ + ("YYYY", "%Y", false), + ("MMMM", "%B", false), + ("MON", "%b", false), + ("MM", "%m", false), + ("DD", "%d", false), + ("HH24", "%H", true), + ("MI", "%M", true), + ("SS", "%S", true), + ("FF9", "%n", true), + ("FF6", "%f", true), + ("FF3", "%g", true), +]; +/// Fields writing an English month name, recorded only as written in upper case. +pub(crate) const MONTH_NAME_FIELDS: &[&str] = &["MMMM", "MON"]; +/// The fields recorded directly after each month name: none after MON, whose MONMM writes the full name. +pub(crate) const MONTH_NAME_FOLLOWERS: &[(&str, &[&str])] = &[("MON", &[]), ("MMMM", &["DD"])]; +/// The format element writing as many fraction digits as the timestamp's precision. +pub(crate) const BARE_FRACTION: &str = "FF"; +/// What FF writes for each precision recorded: none for 0, then milli-, micro- and nanoseconds. +pub(crate) const BARE_FRACTION_PATTERNS: &[(u8, &str)] = + &[(0, ""), (3, "%g"), (6, "%f"), (9, "%n")]; +/// Snowflake's TIMESTAMP_NTZ output format, YYYY-MM-DD HH24:MI:SS.FF3, as a strftime pattern. +pub(crate) const NTZ_OUTPUT_PATTERN: &str = "%Y-%m-%d %H:%M:%S.%g"; +/// Characters a format may hold between fields, written as they are. +pub(crate) const TEMPORAL_TEXT_SEPARATORS: &str = "-_/:. "; +/// The pieces of a TIME format recorded through TO_VARCHAR: clock fields and separators. +pub(crate) const CLOCK_FORMAT_FIELDS: &[&str] = &[ + "HH24", "MI", "SS", "FF1", "FF2", "FF3", "FF4", "FF5", "FF6", ":", ".", +]; diff --git a/crates/sqlquack-rules/src/text_formats/main/clock_format.rs b/crates/sqlquack-rules/src/text_formats/main/clock_format.rs new file mode 100644 index 000000000..c92160a77 --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/main/clock_format.rs @@ -0,0 +1,18 @@ +//! `clock_format`: whether a TIME format names only recorded clock fields. + +use crate::text_formats::constants::CLOCK_FORMAT_FIELDS; + +/// Whether a format names only clock fields, HH24, MI, SS and up to six fraction digits. +pub(crate) fn clock_format(written: &str) -> bool { + let mut rest = written; + while !rest.is_empty() { + let Some(field) = CLOCK_FORMAT_FIELDS + .iter() + .find(|field| rest.starts_with(**field)) + else { + return false; + }; + rest = &rest[field.len()..]; + } + true +} diff --git a/crates/sqlquack-rules/src/text_formats/main/mod.rs b/crates/sqlquack-rules/src/text_formats/main/mod.rs new file mode 100644 index 000000000..0e8d35267 --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/main/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod clock_format; +pub(crate) mod strftime_pattern; diff --git a/crates/sqlquack-rules/src/text_formats/main/strftime_pattern.rs b/crates/sqlquack-rules/src/text_formats/main/strftime_pattern.rs new file mode 100644 index 000000000..ed95c8ef9 --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/main/strftime_pattern.rs @@ -0,0 +1,47 @@ +//! `strftime_pattern`: a date or timestamp format as a strftime pattern, or None. + +use crate::text_formats::_helpers::adjacency::{ + bare_fraction, field_at, field_recorded, separator_at, +}; +use crate::text_formats::constants::BARE_FRACTION; + +/// The strftime pattern of a format of known fields between separators, or None. +pub(crate) fn strftime_pattern(format: &str, precision: Option) -> Option { + let with_time = precision.is_some(); + let upper = format.to_ascii_uppercase(); + let mut rest = upper.as_str(); + let mut pattern = String::new(); + let mut last_letter: Option = None; + let mut previous: Option<&str> = None; + while !rest.is_empty() { + let written = &format[format.len() - rest.len()..]; + if let Some(after) = rest.strip_prefix(BARE_FRACTION) + && !after.starts_with(|next: char| next.is_ascii_digit()) + { + pattern.push_str(bare_fraction(previous, precision)?); + rest = after; + last_letter = None; + previous = Some(BARE_FRACTION); + continue; + } + match field_at(rest, with_time) { + Some((token, replacement, _)) => { + if !field_recorded(token, last_letter, previous, written) { + return None; + } + pattern.push_str(replacement); + rest = &rest[token.len()..]; + last_letter = token.chars().last().filter(char::is_ascii_alphabetic); + previous = Some(token); + } + None => { + last_letter = None; + previous = None; + let separator = separator_at(rest)?; + pattern.push(separator); + rest = &rest[separator.len_utf8()..]; + } + } + } + Some(pattern) +} diff --git a/crates/sqlquack-rules/src/text_formats/mod.rs b/crates/sqlquack-rules/src/text_formats/mod.rs new file mode 100644 index 000000000..059c0308e --- /dev/null +++ b/crates/sqlquack-rules/src/text_formats/mod.rs @@ -0,0 +1,5 @@ +//! Snowflake date and time format strings, read as the strftime patterns DuckDB writes text with. + +pub(crate) mod _helpers; +pub(crate) mod constants; +pub(crate) mod main; From 82064e32d3979442075077df5e783db743714e06 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:06:17 +0000 Subject: [PATCH 07/10] VARIANCE, STDDEV and COVAR families from the D267 receipts VARIANCE, VAR_SAMP, VAR_POP and VARIANCE_POP of a NUMBER of scale s are the exact (n*sum(x^2) - sum(x)^2) / (n(n-1)), or / n^2, rounded half up to NUMBER(38, min(6 + 2s, 12)); STDDEV, STDDEV_SAMP and STDDEV_POP are the FLOAT square root of that rounded variance, as recorded bit for bit. FLOAT and text use the same formula in doubles; COVAR_SAMP and COVAR_POP use (sum(xy) - sum(x)sum(y)/n) / d over complete pairs. Both rely on float_sum_order, whose guard raises for the large-mean FLOAT cases, now declared. VARIANCE(DISTINCT) and DISTINCT over an ordered window refuse, as Snowflake rejects them. CORR still refuses: no formula reproduces its NUMBER(10,2) receipt. --- .../functions/covar-pop.covar-pop.toml | 1 + .../functions/covar-samp.covar-samp.toml | 1 + .../functions/variance.variance.toml | 1 + .../sqlquack-rules/src/catalogue/constants.rs | 50 ++- .../conversion/_helpers/expressions/mod.rs | 1 + .../_helpers/expressions/named_aggregates.rs | 13 +- .../conversion/_helpers/expressions/scalar.rs | 5 +- .../_helpers/expressions/statistics.rs | 300 ++++++++++++++++++ .../_helpers/expressions/windows.rs | 16 +- .../src/conversion/constants.rs | 27 ++ sqlquack.toml | 4 +- 11 files changed, 405 insertions(+), 14 deletions(-) create mode 100644 crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs diff --git a/cases/translation/functions/covar-pop.covar-pop.toml b/cases/translation/functions/covar-pop.covar-pop.toml index fae7e7d1b..e2fcf8403 100644 --- a/cases/translation/functions/covar-pop.covar-pop.toml +++ b/cases/translation/functions/covar-pop.covar-pop.toml @@ -83,6 +83,7 @@ rows = [ ] query = "SELECT COVAR_POP(y, x) AS r FROM input" order = "ordered" +expected_divergence = "float_sum_order" tags = ["type-combination"] [[case]] diff --git a/cases/translation/functions/covar-samp.covar-samp.toml b/cases/translation/functions/covar-samp.covar-samp.toml index 5810e7bf1..b751b9549 100644 --- a/cases/translation/functions/covar-samp.covar-samp.toml +++ b/cases/translation/functions/covar-samp.covar-samp.toml @@ -83,6 +83,7 @@ rows = [ ] query = "SELECT COVAR_SAMP(y, x) AS r FROM input" order = "ordered" +expected_divergence = "float_sum_order" tags = ["type-combination"] [[case]] diff --git a/cases/translation/functions/variance.variance.toml b/cases/translation/functions/variance.variance.toml index cac26b93d..a7c2f907a 100644 --- a/cases/translation/functions/variance.variance.toml +++ b/cases/translation/functions/variance.variance.toml @@ -296,4 +296,5 @@ rows = [ ] query = "SELECT VARIANCE(x) AS v, VAR_POP(x) AS p, STDDEV(x) AS s, VARIANCE(-x) AS n FROM input" order = "ordered" +expected_divergence = "float_sum_order" tags = ["type-combination"] diff --git a/crates/sqlquack-rules/src/catalogue/constants.rs b/crates/sqlquack-rules/src/catalogue/constants.rs index befb2e411..1757af411 100644 --- a/crates/sqlquack-rules/src/catalogue/constants.rs +++ b/crates/sqlquack-rules/src/catalogue/constants.rs @@ -25,7 +25,7 @@ use crate::conversion::constants::{ CAST_BOOLEAN, CAST_NUMERIC, CAST_PARSED_TEXT, CAST_SAME_TYPE, CAST_TEMPORAL, CAST_TEXT_LITERAL, CAST_TEXT_TIME, CAST_TEXT_TIMESTAMP, CAST_TO_TEXT, CAST_UTC_ZONED, COALESCE, COLUMN, COMMENT, COMMON_TABLE, COMPARISON, COMPUTED_SUBSTR_LENGTH, CONCAT, CONCAT_WS, CONSTANT_LITERAL, COUNT, - CURRENT_TIME_RULE, DATE_ARITHMETIC, DATE_DAY_ARITHMETIC, DATE_FORMAT_RULE, + COVARIANCE_RULE, CURRENT_TIME_RULE, DATE_ARITHMETIC, DATE_DAY_ARITHMETIC, DATE_FORMAT_RULE, DATE_LITERAL_COMPARISON, DATE_PART_RULE, DAY_NAME_RULE, DECIMAL_ARITHMETIC, DECIMAL_DIVISION, DECIMAL_EPOCH_RULE, DECIMAL_MODULO, DECODE_RULE, DISTINCT, DIV0_RULE, EDIT_DISTANCE, EXISTS, FLATTEN_RULE, FLOAT_ARITHMETIC, FLOAT_MODULO, FLOAT_TO_DOUBLE, FRACTIONAL_DATE_AMOUNT, @@ -48,11 +48,12 @@ use crate::conversion::constants::{ TIME_ARITHMETIC, TIME_CAST, TIME_ZONE_CONVERSION, TIMESTAMP_FORMAT_RULE, TIMESTAMP_TIME_RULE, TO_ARRAY_RULE, TO_BOOLEAN_RULE, TO_DATE_TEMPORAL, TO_TIMESTAMP_TEMPORAL, TO_VARIANT_RULE, TRIM, UNION_BY_NAME, USER_OVERRIDE_RULE, UTC_OFFSET_ARITHMETIC, VALUE_AT_EXTREME_KEY, VALUES_ROWS, - VARIANT_CAST, VARIANT_COMPUTED_GET, VARIANT_EQUALITY, VARIANT_EQUALITY_BY_TEXT, - VARIANT_INSPECT, VARIANT_KIND_CAST, VARIANT_KIND_RULE, VARIANT_MAX, VARIANT_ORDER_KEY, - VARIANT_PARTITION, VARIANT_PATH, VARIANT_TEXT_BRANCHES, VARIANT_TO_VARCHAR, VARIANT_TYPE_NAME, - VARIANT_TYPED_CHECK, WHERE, WINDOW_AGGREGATE, WINDOW_FIRST_LAST, WINDOW_OFFSET, WINDOW_RANKING, - WINDOW_VALUE_FRAME, ZERO_IF_NULL_RULE, ZONE_COLUMNS, ZONED_PARTS, + VARIANCE_FLOAT_RULE, VARIANCE_RULE, VARIANT_CAST, VARIANT_COMPUTED_GET, VARIANT_EQUALITY, + VARIANT_EQUALITY_BY_TEXT, VARIANT_INSPECT, VARIANT_KIND_CAST, VARIANT_KIND_RULE, VARIANT_MAX, + VARIANT_ORDER_KEY, VARIANT_PARTITION, VARIANT_PATH, VARIANT_TEXT_BRANCHES, VARIANT_TO_VARCHAR, + VARIANT_TYPE_NAME, VARIANT_TYPED_CHECK, WHERE, WINDOW_AGGREGATE, WINDOW_FIRST_LAST, + WINDOW_OFFSET, WINDOW_RANKING, WINDOW_VALUE_FRAME, ZERO_IF_NULL_RULE, ZONE_COLUMNS, + ZONED_PARTS, }; use crate::conversion::models::RuleSpec; @@ -399,6 +400,43 @@ pub const RULES: &[RuleSpec] = &[ conditions: "A BOOLEAN condition; branches unify.", failures: &[], }, + RuleSpec { + id: VARIANCE_RULE, + constructs: &[ + "fn:VARIANCE", + "fn:VAR_SAMP", + "fn:VAR_POP", + "fn:VARIANCE_POP", + "fn:STDDEV", + "fn:STDDEV_SAMP", + "fn:STDDEV_POP", + ], + conditions: "Of a NUMBER of scale s at most 12, as an aggregate or over a window with an unordered, running or ROWS frame, DISTINCT included except for VARIANCE, which Snowflake rejects, and except over an ordered window: the exact (n·Σx² − (Σx)²) / (n(n−1)), or / n² for the population, rounded half up to NUMBER(38, min(6 + 2s, 12)), NULL below two values (one for the population), as recorded; the standard deviations are the square root of that rounded variance, as a FLOAT. A total beyond 38 digits raises numeric_range.", + failures: &[FailureMode { + kind: ErrorKind::NumericOverflow, + snowflake: &[NUMBER_OUT_OF_RANGE], + }], + }, + RuleSpec { + id: VARIANCE_FLOAT_RULE, + constructs: &[ + "fn:VARIANCE", + "fn:VAR_SAMP", + "fn:VAR_POP", + "fn:VARIANCE_POP", + "fn:STDDEV", + "fn:STDDEV_SAMP", + "fn:STDDEV_POP", + ], + conditions: "Of a FLOAT, or text cast to FLOAT, without DISTINCT: (n·Σx² − (Σx)²) / (n(n−1)), or / n² for the population, in doubles, cancelling as Snowflake was recorded cancelling, relying on float_sum_order for the sums; NULL below two values (one for the population); the standard deviations are its square root.", + failures: &[], + }, + RuleSpec { + id: COVARIANCE_RULE, + constructs: &["fn:COVAR_SAMP", "fn:COVAR_POP"], + conditions: "Of NUMBER or FLOAT pairs, without DISTINCT, as an aggregate or over a window, pairs with a NULL skipped: (Σxy − Σx·Σy / n) / (n − 1), or / n for the population, in doubles, relying on float_sum_order for the sums; NULL below two pairs (one for the population), as recorded.", + failures: &[], + }, RuleSpec { id: NVL2_RULE, constructs: &["fn:NVL2"], diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/mod.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/mod.rs index b12a65b7f..d0b0d7b18 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/mod.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/mod.rs @@ -18,6 +18,7 @@ pub(crate) mod result_types; pub(crate) mod scalar; pub(crate) mod semi_structured; pub(crate) mod spellings; +pub(crate) mod statistics; pub(crate) mod strings; pub(crate) mod temporal; pub(crate) mod temporal_functions; diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/named_aggregates.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/named_aggregates.rs index f55ed3e32..5114b24c7 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/named_aggregates.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/named_aggregates.rs @@ -22,6 +22,7 @@ use sqlquack_snowflake::typing::main::count::count; use sqlquack_snowflake::typing::models::SnowflakeType; use crate::conversion::_helpers::expressions::scalar::convert_expression; +use crate::conversion::_helpers::expressions::statistics::{is_statistic, statistic}; use crate::conversion::_helpers::expressions::variants::to_variant; use crate::conversion::_helpers::support::divergence::diverge; use crate::conversion::_helpers::support::scope::Emitted; @@ -41,7 +42,7 @@ use sqlquack_policy::policy::constants::{ /// Whether `name` is an aggregate converted here. pub(crate) fn is_named_aggregate(name: &str) -> bool { - NAMED_AGGREGATE_NAMES.contains(&name) + NAMED_AGGREGATE_NAMES.contains(&name) || is_statistic(name) } /// One named aggregate; `window` is the OVER clause when used as a window function. @@ -57,13 +58,13 @@ pub(crate) fn named_aggregate( let payload = payload_object(payload, &construct)?; require_understood(payload, &["name", "args", "distinct"], &construct)?; let distinct = flag(payload, "distinct"); - if distinct != (name == MULTI_COUNT) { + if distinct != (name == MULTI_COUNT) && !is_statistic(name) { return Err(refuse(format!( "{name} with or without DISTINCT as written" ))); } let over = window.map_or_else(String::new, |window| format!(" OVER ({window})")); - if window.is_some() && !WINDOWED_NAMED_AGGREGATES.contains(&name) { + if window.is_some() && !WINDOWED_NAMED_AGGREGATES.contains(&name) && !is_statistic(name) { return Err(refuse(format!("{name} over a window"))); } let site = site.placed(Placement::ROW); @@ -73,6 +74,12 @@ pub(crate) fn named_aggregate( .collect::>()?; let log = site.context.log; match (name, arguments.as_slice()) { + (name, arguments) if is_statistic(name) => statistic( + name, + arguments, + (distinct, &over), + (&construct, site.context), + ), ("ANY_VALUE", [value]) if value.data_type != SnowflakeType::Null => { diverge(site.context, ANY_VALUE_CHOICE, &construct)?; compared_value(value, &construct, site.context)?; diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/scalar.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/scalar.rs index 501895865..5432d8c0c 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/scalar.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/scalar.rs @@ -33,6 +33,7 @@ use crate::conversion::_helpers::expressions::predicates::{ use crate::conversion::_helpers::expressions::result_types::result_type; use crate::conversion::_helpers::expressions::semi_structured::{variant_path, variant_subscript}; use crate::conversion::_helpers::expressions::spellings::{as_typed, as_written, rlike_as_call}; +use crate::conversion::_helpers::expressions::statistics::is_statistic; use crate::conversion::_helpers::expressions::strings::{ case_mapping, concat_operator, text_affix, trim, }; @@ -348,7 +349,9 @@ fn convert_kind(node: &Value, site: Site<'_>) -> Result { .adopt(&typed, arguments.map_or(&[], Vec::as_slice)); convert_expression(&typed, site).map_err(|refusal| as_written(refusal, spelling)) } - None if kind == CALL_KIND => convert_function(payload, site), + None if kind == CALL_KIND && !is_statistic(&call_name(payload)) => { + convert_function(payload, site) + } None if is_named_aggregate(&call_name(payload)) => { if !site.placement.aggregates { return Err(outside_conditions( diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs new file mode 100644 index 000000000..718a17354 --- /dev/null +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs @@ -0,0 +1,300 @@ +//! Variance, standard deviation and covariance aggregates, computed the way Snowflake was recorded. + +use sqlquack_core::classification::models::Construct; +use sqlquack_core::refusals::main::outside_conditions::outside_conditions; +use sqlquack_core::refusals::models::Refusal; +use sqlquack_duckdb::blocks::main::exact_decimal_divide::exact_decimal_divide; +use sqlquack_duckdb::floats::constants::FLOAT_SUM_ORDER; +use sqlquack_duckdb::floats::main::exact_float_sum::exact_float_sum; +use sqlquack_policy::policy::constants::NUMERIC_RANGE; +use sqlquack_snowflake::typing::constants::MAX_PRECISION; +use sqlquack_snowflake::typing::models::SnowflakeType; + +use crate::conversion::_helpers::expressions::casts::cast_to; +use crate::conversion::_helpers::support::divergence::diverge; +use crate::conversion::_helpers::support::scope::{Context, Emitted}; +use crate::conversion::constants::{ + COVARIANCE_AGGREGATES, COVARIANCE_RULE, FLOAT_TYPE_NAME, NO_DISTINCT_VARIANCE, + SQUARED_PARAMETER, VARIANCE_AGGREGATES, VARIANCE_FLOAT_RULE, VARIANCE_RULE, VARIANCE_SCALES, +}; + +/// Whether `name` is a variance, standard deviation or covariance aggregate converted here. +pub(crate) fn is_statistic(name: &str) -> bool { + VARIANCE_AGGREGATES + .iter() + .any(|(known, _, _)| *known == name) + || COVARIANCE_AGGREGATES + .iter() + .any(|(known, _)| *known == name) +} + +/// One statistic of converted `arguments`; `over` is its OVER clause, or empty. +pub(crate) fn statistic( + name: &str, + arguments: &[Emitted], + (distinct, over): (bool, &str), + (construct, context): (&Construct, &Context<'_>), +) -> Result { + let refuse = |reason: String| outside_conditions(construct.clone(), reason); + if distinct && name == NO_DISTINCT_VARIANCE { + return Err(refuse(format!( + "{name}(DISTINCT), which Snowflake rejects as an unsupported feature" + ))); + } + let variance = VARIANCE_AGGREGATES + .iter() + .find(|(known, _, _)| *known == name); + let covariance = COVARIANCE_AGGREGATES + .iter() + .find(|(known, _)| *known == name); + match (variance, covariance, arguments) { + (Some((_, population, root)), _, [value]) => { + let shape = Shape { + population: *population, + distinct, + over, + }; + let sql = match value.data_type { + SnowflakeType::Number { scale, .. } => { + exact_variance(&value.sql, scale, shape, (construct, context))? + } + SnowflakeType::Float | SnowflakeType::Varchar if !distinct => { + let float = as_float(value, (construct, context))?; + float_variance(&float, shape, (construct, context))? + } + other => { + return Err(refuse(format!( + "{name}{} of {other}, which no recording shows", + distinct_text(distinct) + ))); + } + }; + Ok(match (root, value.data_type) { + (true, SnowflakeType::Number { .. }) => { + Emitted::new(format!("sqrt(CAST({sql} AS DOUBLE))"), SnowflakeType::Float) + } + (true, _) => Emitted::new(format!("sqrt({sql})"), SnowflakeType::Float), + (false, SnowflakeType::Number { scale, .. }) => Emitted::new( + sql, + SnowflakeType::Number { + precision: MAX_PRECISION, + scale: variance_scale(scale), + }, + ), + (false, _) => Emitted::new(sql, SnowflakeType::Float), + }) + } + (_, Some((_, population)), [y, x]) if !distinct && numeric(y) && numeric(x) => { + let shape = Shape { + population: *population, + distinct, + over, + }; + let pair = ( + as_float(y, (construct, context))?, + as_float(x, (construct, context))?, + ); + Ok(Emitted::new( + float_covariance((&y.sql, &x.sql), pair, shape, (construct, context))?, + SnowflakeType::Float, + )) + } + (_, _, arguments) => Err(refuse(format!( + "{name}{} of {}, which no recording shows", + distinct_text(distinct), + arguments + .iter() + .map(|argument| argument.data_type.to_string()) + .collect::>() + .join(", ") + ))), + } +} + +/// How a statistic aggregates: over the population or a sample, its distinct values, its window. +#[derive(Clone, Copy)] +struct Shape<'a> { + population: bool, + distinct: bool, + over: &'a str, +} + +impl Shape<'_> { + /// The fewest values with a result: one for a population, two for a sample. + fn fewest(self) -> u8 { + match self.population { + true => 1, + false => 2, + } + } +} + +/// The recorded result scale of the variance of a NUMBER of `scale`. +fn variance_scale(scale: u8) -> u8 { + let (base, widest) = VARIANCE_SCALES; + (base + 2 * scale).min(widest) +} + +/// The exact variance of a NUMBER of `scale`, rounded half up to its recorded scale. +fn exact_variance( + value: &str, + scale: u8, + shape: Shape<'_>, + (construct, context): (&Construct, &Context<'_>), +) -> Result { + let log = context.log; + let _rule = log.rule(VARIANCE_RULE); + let refuse = |reason: String| outside_conditions(construct.clone(), reason); + let (_, widest) = VARIANCE_SCALES; + if scale > widest { + return Err(refuse(format!( + "the variance of a NUMBER of scale {scale}, above the widest recorded ({widest})" + ))); + } + diverge(context, NUMERIC_RANGE, construct)?; + let over = shape.over; + let squares = 2 * scale; + let decimal = |sql: &str, scale: u8| format!("CAST({sql} AS DECIMAL(38,{scale}))"); + let (count, total, sum_of_squares) = match shape.distinct { + true => ( + format!("count(DISTINCT {value}){over}"), + format!("sum(DISTINCT {value}){over}"), + format!( + "list_sum(list_transform(list(DISTINCT {value}){over}, \ + lambda {SQUARED_PARAMETER}: {} * {}))", + decimal(SQUARED_PARAMETER, scale), + decimal(SQUARED_PARAMETER, scale) + ), + ), + false => ( + format!("count({value}){over}"), + format!("sum({value}){over}"), + format!( + "sum({} * {}){over}", + decimal(value, scale), + decimal(value, scale) + ), + ), + }; + let numerator = decimal( + &format!( + "{} * {} - {} * {}", + decimal(&count, 0), + decimal(&sum_of_squares, squares), + decimal(&total, scale), + decimal(&total, scale) + ), + squares, + ); + let result = variance_scale(scale); + let divisor_scale = squares.saturating_sub(result); + let pairs = match shape.population { + true => format!("{count} * {count}"), + false => format!("{count} * ({count} - 1)"), + }; + let (divided, _) = exact_decimal_divide( + (&numerator, MAX_PRECISION, squares), + ( + &decimal(&pairs, divisor_scale), + MAX_PRECISION, + divisor_scale, + ), + ( + SnowflakeType::Number { + precision: MAX_PRECISION, + scale: result, + }, + false, + ), + log, + ) + .map_err(refuse)?; + Ok(format!( + "(CASE WHEN {count} < {} THEN NULL ELSE {divided} END)", + shape.fewest() + )) +} + +/// The variance of FLOAT values `value`, as (n·Σx² − (Σx)²) / (n·d) with exact sums. +fn float_variance( + value: &str, + shape: Shape<'_>, + (construct, context): (&Construct, &Context<'_>), +) -> Result { + let log = context.log; + let _rule = log.rule(VARIANCE_FLOAT_RULE); + diverge(context, FLOAT_SUM_ORDER, construct)?; + let over = shape.over; + let count = format!("count({value}){over}"); + let values = format!("CAST({count} AS DOUBLE)"); + let total = exact_float_sum(value, "", over, log); + let squares = exact_float_sum(&format!("({value}) * ({value})"), "", over, log); + Ok(format!( + "(CASE WHEN {count} < {} THEN NULL ELSE ({values} * {squares} - {total} * {total}) / \ + ({values} * {}) END)", + shape.fewest(), + degrees(&values, shape) + )) +} + +/// The covariance of FLOAT pairs, as (Σxy − Σx·Σy / n) / d with exact sums over complete pairs. +fn float_covariance( + (y_sql, x_sql): (&str, &str), + (y, x): (String, String), + shape: Shape<'_>, + (construct, context): (&Construct, &Context<'_>), +) -> Result { + let log = context.log; + let _rule = log.rule(COVARIANCE_RULE); + diverge(context, FLOAT_SUM_ORDER, construct)?; + let over = shape.over; + let complete = format!("{y_sql} IS NOT NULL AND {x_sql} IS NOT NULL"); + let kept = |sql: &str| format!("CASE WHEN {complete} THEN {sql} END"); + let count = format!("count({}){over}", kept("1")); + let values = format!("CAST({count} AS DOUBLE)"); + let sum = |sql: &str| exact_float_sum(&kept(sql), "", over, log); + Ok(format!( + "(CASE WHEN {count} < {} THEN NULL ELSE ({} - {} * {} / {values}) / {} END)", + shape.fewest(), + sum(&format!("({y}) * ({x})")), + sum(&x), + sum(&y), + degrees(&values, shape) + )) +} + +/// The divisor's degrees of freedom: n for a population, n − 1 for a sample. +fn degrees(values: &str, shape: Shape<'_>) -> String { + match shape.population { + true => values.to_owned(), + false => format!("({values} - 1)"), + } +} + +/// `value` as a FLOAT: as it is, or cast as Snowflake casts a NUMBER or text argument. +fn as_float( + value: &Emitted, + (construct, context): (&Construct, &Context<'_>), +) -> Result { + match value.data_type { + SnowflakeType::Float => Ok(value.sql.clone()), + _ => cast_to(value, FLOAT_TYPE_NAME, false, (context, construct)) + .map_err(|reason| outside_conditions(construct.clone(), reason)), + } +} + +/// Whether a covariance argument is of a type its recordings show: NUMBER or FLOAT. +fn numeric(value: &Emitted) -> bool { + matches!( + value.data_type, + SnowflakeType::Number { .. } | SnowflakeType::Float + ) +} + +/// " (DISTINCT)" when `distinct`, for refusal reasons. +fn distinct_text(distinct: bool) -> &'static str { + match distinct { + true => "(DISTINCT)", + false => "", + } +} diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/windows.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/windows.rs index 9b8676656..86d67174f 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/windows.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/windows.rs @@ -23,6 +23,7 @@ use crate::conversion::_helpers::expressions::named_aggregates::named_aggregate; use crate::conversion::_helpers::expressions::ordered_aggregates::within_group; use crate::conversion::_helpers::expressions::scalar::{call_name, convert_expression}; use crate::conversion::_helpers::expressions::spellings::{as_typed, as_written}; +use crate::conversion::_helpers::expressions::statistics::is_statistic; use crate::conversion::_helpers::support::declared::merged; use crate::conversion::_helpers::support::divergence::diverge; use crate::conversion::_helpers::support::scope::{Context, Emitted}; @@ -124,7 +125,8 @@ pub(crate) fn convert_window(payload: &Value, site: Site<'_>) -> Result WINDOW_FIRST_LAST, "count" | "sum" | "avg" | "min" | "max" | "within_group" => WINDOW_AGGREGATE, "function" | "aggregate_function" - if WINDOWED_NAMED_AGGREGATES.contains(&call_name(function_payload).as_str()) => + if WINDOWED_NAMED_AGGREGATES.contains(&call_name(function_payload).as_str()) + || is_statistic(&call_name(function_payload)) => { WINDOW_AGGREGATE } @@ -182,14 +184,24 @@ pub(crate) fn convert_window(payload: &Value, site: Site<'_>) -> Result ordered_window(function_payload, &over, site), "function" | "aggregate_function" => { let name = call_name(function_payload); + let statistic = is_statistic(&name); if over.frame.is_some_and(|frame| { - name != COUNT_IF_NAME || !is_running(frame) || over.order.is_empty() + let running_count = name == COUNT_IF_NAME && is_running(frame); + let rows = frame.get("kind").and_then(Value::as_str) == Some(ROWS); + !(statistic && rows) && (!running_count || over.order.is_empty()) }) { return Err(outside_conditions( function_construct(&name), format!("{name} over an explicit frame, which no recording shows"), )); } + let distinct = function_payload.get("distinct").and_then(Value::as_bool) == Some(true); + if statistic && distinct && !over.order.is_empty() { + return Err(outside_conditions( + function_construct(&name), + format!("{name}(DISTINCT) over an ordered window, which Snowflake rejects"), + )); + } if UNORDERED_WINDOW_AGGREGATES.contains(&name.as_str()) && !over.order.is_empty() { return Err(outside_conditions( function_construct(&name), diff --git a/crates/sqlquack-rules/src/conversion/constants.rs b/crates/sqlquack-rules/src/conversion/constants.rs index 4f937c834..1e120371d 100644 --- a/crates/sqlquack-rules/src/conversion/constants.rs +++ b/crates/sqlquack-rules/src/conversion/constants.rs @@ -23,6 +23,27 @@ pub const NAMED_AGGREGATE_NAMES: &[&str] = &[ "MEDIAN", MULTI_COUNT, ]; +/// Variances and standard deviations: the name, whether over the population, and whether a root. +pub(crate) const VARIANCE_AGGREGATES: &[(&str, bool, bool)] = &[ + ("VARIANCE", false, false), + ("VAR_SAMP", false, false), + ("VAR_POP", true, false), + ("VARIANCE_POP", true, false), + ("STDDEV", false, true), + ("STDDEV_SAMP", false, true), + ("STDDEV_POP", true, true), +]; +/// Covariances, and whether over the population. +pub(crate) const COVARIANCE_AGGREGATES: &[(&str, bool)] = + &[("COVAR_SAMP", false), ("COVAR_POP", true)]; +/// The variance Snowflake rejects with DISTINCT, though its other spellings accept it. +pub(crate) const NO_DISTINCT_VARIANCE: &str = "VARIANCE"; +/// An exact variance's result scale: this base plus twice the input's scale, at most the widest. +pub(crate) const VARIANCE_SCALES: (u8, u8) = (6, 12); +/// The lambda parameter squaring each distinct value of an exact variance. +pub(crate) const SQUARED_PARAMETER: &str = "__sqlquack_squared"; +/// The type name statistics cast NUMBER and text arguments to. +pub(crate) const FLOAT_TYPE_NAME: &str = "FLOAT"; /// COUNT of several values, which the parser keeps as a named call. pub const MULTI_COUNT: &str = "COUNT"; /// The recorded type of ARRAY_POSITION. @@ -605,6 +626,12 @@ pub const CASE: &str = "rule.case"; pub const COALESCE: &str = "rule.coalesce"; /// IFF. pub const IFF: &str = "rule.iff"; +/// VARIANCE, VAR_POP and STDDEV of a NUMBER, exact and rounded half up to the recorded scale. +pub const VARIANCE_RULE: &str = "rule.variance"; +/// VARIANCE, VAR_POP and STDDEV of a FLOAT or text, with exact sums. +pub const VARIANCE_FLOAT_RULE: &str = "rule.variance_float"; +/// COVAR_SAMP and COVAR_POP of NUMBER or FLOAT pairs, with exact sums. +pub const COVARIANCE_RULE: &str = "rule.covariance"; /// NVL2 of a non-semi-structured value and two branches that unify without reading text. pub const NVL2_RULE: &str = "rule.nvl2"; /// NULLIF. diff --git a/sqlquack.toml b/sqlquack.toml index c8e85799f..0e3f9f55b 100644 --- a/sqlquack.toml +++ b/sqlquack.toml @@ -302,7 +302,7 @@ minimum_bytes = 4194304 # can return another value. [divergences.numeric_range] -constructs = ["fn:SUM", "fn:AVG", "node:add", "node:sub", "node:mul", "node:div", "node:eq", "node:neq", "node:lt", "node:lte", "node:gt", "node:gte", "node:null_safe_eq", "node:null_safe_neq", "node:in", "fn:EQUAL_NULL", "fn:NULLIF", "node:union", "node:except", "node:intersect", "node:case", "fn:COALESCE", "node:coalesce", "fn:DECODE", "fn:GREATEST", "fn:LEAST", "node:nvl", "fn:NVL2", "fn:IFF", "fn:IFNULL", "fn:ZEROIFNULL", "fn:EXP"] +constructs = ["fn:SUM", "fn:AVG", "node:add", "node:sub", "node:mul", "node:div", "node:eq", "node:neq", "node:lt", "node:lte", "node:gt", "node:gte", "node:null_safe_eq", "node:null_safe_neq", "node:in", "fn:EQUAL_NULL", "fn:NULLIF", "node:union", "node:except", "node:intersect", "node:case", "fn:COALESCE", "node:coalesce", "fn:DECODE", "fn:GREATEST", "fn:LEAST", "node:nvl", "fn:NVL2", "fn:IFF", "fn:IFNULL", "fn:ZEROIFNULL", "fn:EXP", "fn:VARIANCE", "fn:VAR_SAMP", "fn:VAR_POP", "fn:VARIANCE_POP", "fn:STDDEV", "fn:STDDEV_SAMP", "fn:STDDEV_POP"] default = "warn" strict = "refuse" failure = "loud" @@ -463,7 +463,7 @@ failure = "loud" reason = "A REGEXP_LIKE pattern from a column cannot be checked before it runs. Snowflake and RE2 were recorded reading literal characters, '.', groups, alternation, plain bracket expressions, single '*', '+' and '?' and anchors at the ends alike; the converted SQL raises for a pattern with any other syntax, such as escapes, counted repetition, POSIX classes or lazy quantifiers. REGEXP_COUNT and REGEXP_INSTR, and REGEXP_REPLACE and REGEXP_SUBSTR of a column pattern, raise for a pattern that matches empty text, whose empty matches no recording shows; column patterns for REGEXP_REPLACE and REGEXP_SUBSTR are checked as REGEXP_LIKE's are. The failure is loud, never a wrong value." [divergences.float_sum_order] -constructs = ["fn:SUM"] +constructs = ["fn:SUM", "fn:VARIANCE", "fn:VAR_SAMP", "fn:VAR_POP", "fn:VARIANCE_POP", "fn:STDDEV", "fn:STDDEV_SAMP", "fn:STDDEV_POP", "fn:COVAR_SAMP", "fn:COVAR_POP"] default = "warn" strict = "refuse" failure = "loud" From 8e604ffd97326da2b7f7801116c07a2a5848ec13 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:24:22 +0000 Subject: [PATCH 08/10] ADD_MONTHS and MONTHS_BETWEEN from the D267 receipts ADD_MONTHS moves by DATEADD of the months rounded half away from zero, and a month's last day on to the last day of the month it lands in, keeping the time of day; a TIMESTAMP_TZ moves on its wall clock where its offset is known. MONTHS_BETWEEN of DATEs and TIMESTAMP_NTZs is the whole months where the days match or both are month ends, else the months plus the day and clock difference over 31 days, exact and rounded at six decimals into NUMBER(27,6). The statistics constants move beside their only reader. --- .../sqlquack-rules/src/catalogue/constants.rs | 20 ++- .../conversion/_helpers/expressions/calls.rs | 8 +- .../_helpers/expressions/statistics.rs | 26 +++- .../expressions/temporal_functions.rs | 145 ++++++++++++++++-- .../src/conversion/constants.rs | 34 ++-- sqlquack.toml | 2 +- 6 files changed, 191 insertions(+), 44 deletions(-) diff --git a/crates/sqlquack-rules/src/catalogue/constants.rs b/crates/sqlquack-rules/src/catalogue/constants.rs index 1757af411..15b81f32b 100644 --- a/crates/sqlquack-rules/src/catalogue/constants.rs +++ b/crates/sqlquack-rules/src/catalogue/constants.rs @@ -17,7 +17,7 @@ use sqlquack_snowflake::typing::constants::VARIANT_COMMON_TYPE; pub const NOT_EXPRESSIBLE: &[NotExpressibleDeclaration] = &[]; use crate::conversion::constants::{ - ALIAS, ARRAY_AGG_RULE, ARRAY_APPEND_RULE, ARRAY_CAT_RULE, ARRAY_COMPACT_RULE, + ADD_MONTHS_RULE, ALIAS, ARRAY_AGG_RULE, ARRAY_APPEND_RULE, ARRAY_CAT_RULE, ARRAY_COMPACT_RULE, ARRAY_CONSTRUCT_COMPACT_RULE, ARRAY_CONSTRUCT_RULE, ARRAY_CONTAINS_RULE, ARRAY_EXCEPT_RULE, ARRAY_EXTREME_RULE, ARRAY_FLATTEN_RULE, ARRAY_POSITION_RULE, ARRAY_RANGE_RULE, ARRAY_REVERSE_RULE, ARRAY_SET_RULE, ARRAY_SIZE_RULE, ARRAY_SLICE_RULE, ARRAY_SORT_RULE, @@ -33,9 +33,9 @@ use crate::conversion::constants::{ HASH_RULE, HAVING, HIERARCHY_RULE, IFF, ILIKE_FOLDING, IN_LIST, IN_SUBQUERY, INTERVAL_ARITHMETIC, IS_NULL, JARO_WINKLER, JOIN_CROSS, JOIN_ON, JOIN_USING, LAMBDA_RULE, LAST_DAY_RULE, LATERAL_SUBQUERY, LEAST_GREATEST, LIKE, LIMIT, LISTAGG_RULE, LOGARITHM, LOGIC, - MEDIAN_RULE, MIN_MAX, MIN_MAX_FLOAT, MODE_RULE, NAMED_AGGREGATES, NEGATE, NOT_IN_SUBQUERY, - NULL_ARITHMETIC, NULL_BRANCHES, NULL_IF_ZERO_RULE, NULL_SAFE_COMPARISON, NULLIF, - NUMBER_LITERAL, NUMBER_TEXT_FORMAT, NUMERIC_FUNCTION, NVL2_RULE, OBJECT_CONSTRUCT_RULE, + MEDIAN_RULE, MIN_MAX, MIN_MAX_FLOAT, MODE_RULE, MONTHS_BETWEEN_RULE, NAMED_AGGREGATES, NEGATE, + NOT_IN_SUBQUERY, NULL_ARITHMETIC, NULL_BRANCHES, NULL_IF_ZERO_RULE, NULL_SAFE_COMPARISON, + NULLIF, NUMBER_LITERAL, NUMBER_TEXT_FORMAT, NUMERIC_FUNCTION, NVL2_RULE, OBJECT_CONSTRUCT_RULE, OBJECT_DELETE_RULE, OBJECT_INSERT_RULE, OBJECT_KEYS_RULE, OBJECT_PICK_RULE, ORDER_BY, PARENTHESES, PARSE_JSON_RULE, PARSE_JSON_VARIANT, PARSE_URL_RULE, PERCENTILE_RULE, PIVOT_RULE, POSITIONAL_COLUMN, POWER_RULE, PROJECT_FUNCTION_CALL, PROJECT_TABLE_FUNCTION, QUALIFY, @@ -400,6 +400,18 @@ pub const RULES: &[RuleSpec] = &[ conditions: "A BOOLEAN condition; branches unify.", failures: &[], }, + RuleSpec { + id: ADD_MONTHS_RULE, + constructs: &["fn:ADD_MONTHS"], + conditions: "ADD_MONTHS of a DATE, a TIMESTAMP_NTZ, or a TIMESTAMP_TZ with its UTC offset known, and a NUMBER of months rounded half away from zero, as DATEADD by month: the day clamped to the month's end, and a month's last day moved on to the last day of the month it lands in, the time of day kept, as recorded; a TIMESTAMP_TZ moves on its wall clock. NULL where either argument is NULL.", + failures: &[], + }, + RuleSpec { + id: MONTHS_BETWEEN_RULE, + constructs: &["fn:MONTHS_BETWEEN"], + conditions: "MONTHS_BETWEEN of DATEs and TIMESTAMP_NTZs, as NUMBER(27,6): the whole months between their calendar months where their days of month match or both are a month's last day, the clock ignored; otherwise those months plus the difference of their days and clocks over 31 days, rounded half away from zero at six decimals, as recorded. DATE inputs never tie; a timestamp tie at the sixth decimal no recording shows. NULL where either is NULL.", + failures: &[], + }, RuleSpec { id: VARIANCE_RULE, constructs: &[ diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs index 661a3437a..949c6277c 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/calls.rs @@ -49,9 +49,9 @@ use crate::conversion::_helpers::expressions::strings::{ try_hex_decode, }; use crate::conversion::_helpers::expressions::temporal_functions::{ - convert_timezone, current_time, date_add, date_diff_call, date_from_parts_call, date_part, - date_trunc, day_name, last_day, temporal_text, timestamp_from_parts_call, to_time, - to_timestamp, to_timestamp_tz, + add_months, convert_timezone, current_time, date_add, date_diff_call, date_from_parts_call, + date_part, date_trunc, day_name, last_day, months_between, temporal_text, + timestamp_from_parts_call, to_time, to_timestamp, to_timestamp_tz, }; use crate::conversion::_helpers::expressions::text_functions::{ left_right, pad, regexp, replace, split_part, stripped_type_name, suffixed_column, @@ -268,6 +268,8 @@ pub(crate) fn convert_function(payload: &Value, site: Site<'_>) -> Result date_trunc(unit, value, &construct, site), ("DAYNAME", parts) => day_name(parts, &construct, site), ("LAST_DAY", parts) => last_day(parts, &construct, site), + ("ADD_MONTHS", parts) => add_months(parts, &construct, site), + ("MONTHS_BETWEEN", parts) => months_between(parts, &construct, site), ("DATEADD" | "TIMEADD" | "TIMESTAMPADD", [unit, amount, value]) => { date_add((unit, amount, value), &construct, site) } diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs index 718a17354..e573efdf1 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/statistics.rs @@ -13,10 +13,28 @@ use sqlquack_snowflake::typing::models::SnowflakeType; use crate::conversion::_helpers::expressions::casts::cast_to; use crate::conversion::_helpers::support::divergence::diverge; use crate::conversion::_helpers::support::scope::{Context, Emitted}; -use crate::conversion::constants::{ - COVARIANCE_AGGREGATES, COVARIANCE_RULE, FLOAT_TYPE_NAME, NO_DISTINCT_VARIANCE, - SQUARED_PARAMETER, VARIANCE_AGGREGATES, VARIANCE_FLOAT_RULE, VARIANCE_RULE, VARIANCE_SCALES, -}; +use crate::conversion::constants::{COVARIANCE_RULE, VARIANCE_FLOAT_RULE, VARIANCE_RULE}; + +/// Variances and standard deviations: the name, whether over the population, and whether a root. +const VARIANCE_AGGREGATES: &[(&str, bool, bool)] = &[ + ("VARIANCE", false, false), + ("VAR_SAMP", false, false), + ("VAR_POP", true, false), + ("VARIANCE_POP", true, false), + ("STDDEV", false, true), + ("STDDEV_SAMP", false, true), + ("STDDEV_POP", true, true), +]; +/// Covariances, and whether over the population. +const COVARIANCE_AGGREGATES: &[(&str, bool)] = &[("COVAR_SAMP", false), ("COVAR_POP", true)]; +/// The variance Snowflake rejects with DISTINCT, though its other spellings accept it. +const NO_DISTINCT_VARIANCE: &str = "VARIANCE"; +/// An exact variance's result scale: this base plus twice the input's scale, at most the widest. +const VARIANCE_SCALES: (u8, u8) = (6, 12); +/// The lambda parameter squaring each distinct value of an exact variance. +const SQUARED_PARAMETER: &str = "__sqlquack_squared"; +/// The type name statistics cast NUMBER and text arguments to. +const FLOAT_TYPE_NAME: &str = "FLOAT"; /// Whether `name` is a variance, standard deviation or covariance aggregate converted here. pub(crate) fn is_statistic(name: &str) -> bool { diff --git a/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs b/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs index c7bbb05e2..a39352929 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/expressions/temporal_functions.rs @@ -7,6 +7,7 @@ use sqlquack_core::refusals::main::outside_conditions::outside_conditions; use sqlquack_core::refusals::models::Refusal; use sqlquack_duckdb::blocks::constants::NANOSECOND_DIGITS; use sqlquack_duckdb::blocks::main::cast::cast; +use sqlquack_duckdb::blocks::main::exact_decimal_divide::exact_decimal_divide; use sqlquack_duckdb::blocks::main::string_literal::string_literal; use sqlquack_duckdb::blocks::models::TextFailure; use sqlquack_duckdb::temporal::constants::DATE_FORMATS; @@ -58,13 +59,15 @@ use crate::conversion::_helpers::support::tree::{ field, node_construct, payload_object, require_understood, }; use crate::conversion::constants::{ - CURRENT_TIME_RULE, DATE_ARITHMETIC, DATE_FORMAT_RULE, DATE_NAME, DATE_PART_RULE, DATE_PARTS, - DAY_NAME_RULE, DECIMAL_EPOCH_RULE, FRACTIONAL_DATE_AMOUNT, FROM_PARTS, LAST_DAY_RULE, - MAX_FRACTION_DIGITS_U8, MICROSECOND_DIGITS, NULL_BRANCHES, NUMBER_TEXT_FORMAT, - OFFSET_FREE_UNITS, RECORDED_TIME_ZONES, SCALE_DIGITS, TEMPORAL_TEXT_FORMAT, TEXT_TO_TIME_RULE, - TEXT_TO_TIMESTAMP_RULE, TIME_ARITHMETIC, TIME_ZONE_CONVERSION, TIMESTAMP_FORMAT_RULE, - TIMESTAMP_NTZ_NAME, TIMESTAMP_TIME_RULE, TO_DATE_TEMPORAL, TO_TIMESTAMP_TEMPORAL, - UTC_OFFSET_ARITHMETIC, UTC_ZONE, ZONE_COLUMNS, ZONED_PARTS, + ADD_MONTHS_RULE, CURRENT_TIME_RULE, DATE_ARITHMETIC, DATE_FORMAT_RULE, DATE_NAME, + DATE_PART_RULE, DATE_PARTS, DAY_NAME_RULE, DAY_UNIT, DECIMAL_EPOCH_RULE, + FRACTIONAL_DATE_AMOUNT, FROM_PARTS, LAST_DAY_RULE, MAX_FRACTION_DIGITS_U8, MICROSECOND_DIGITS, + MONTH_UNIT, MONTHS_BETWEEN_MONTH_NANOSECONDS, MONTHS_BETWEEN_RULE, MONTHS_BETWEEN_TYPE, + NULL_BRANCHES, NUMBER_TEXT_FORMAT, OFFSET_FREE_UNITS, RECORDED_TIME_ZONES, SCALE_DIGITS, + TEMPORAL_TEXT_FORMAT, TEXT_TO_TIME_RULE, TEXT_TO_TIMESTAMP_RULE, TIME_ARITHMETIC, + TIME_ZONE_CONVERSION, TIMESTAMP_FORMAT_RULE, TIMESTAMP_NTZ_NAME, TIMESTAMP_TIME_RULE, + TO_DATE_TEMPORAL, TO_TIMESTAMP_TEMPORAL, UTC_OFFSET_ARITHMETIC, UTC_ZONE, ZONE_COLUMNS, + ZONED_PARTS, }; use crate::text_formats::main::clock_format::clock_format; use crate::text_formats::main::strftime_pattern::strftime_pattern; @@ -358,7 +361,102 @@ pub(crate) fn date_add( let _rule = site.context.log.rule(DATE_ARITHMETIC); let refuse = |reason: String| outside_conditions(construct.clone(), reason); let unit = unit_name(unit).ok_or_else(|| refuse("an unreadable unit".to_owned()))?; - let mut amount = convert_expression(amount, site)?; + let amount = convert_expression(amount, site)?; + let operand = convert_expression(value, site)?; + shift((&unit, amount), operand, false, (construct, site)) +} + +/// ADD_MONTHS(value, months): DATEADD of the rounded months, a month's last day kept the last day. +pub(crate) fn add_months( + parts: &[Value], + construct: &Construct, + site: Site<'_>, +) -> Result { + let _rule = site.context.log.rule(ADD_MONTHS_RULE); + let refuse = |reason: String| outside_conditions(construct.clone(), reason); + let [value, amount] = parts else { + return Err(refuse(format!("ADD_MONTHS of {} arguments", parts.len()))); + }; + let operand = convert_expression(value, site)?; + let amount = convert_expression(amount, site)?; + match (operand.data_type, amount.data_type) { + ( + SnowflakeType::Date + | SnowflakeType::TimestampNtz { .. } + | SnowflakeType::TimestampTz { .. }, + SnowflakeType::Number { .. } | SnowflakeType::Null, + ) => shift((MONTH_UNIT, amount), operand, true, (construct, site)), + (operand, amount) => Err(refuse(format!( + "ADD_MONTHS of {operand} and {amount}, which no recording shows" + ))), + } +} + +/// MONTHS_BETWEEN(a, b) of DATEs and naive timestamps, whole or over a 31-day month, as recorded. +pub(crate) fn months_between( + parts: &[Value], + construct: &Construct, + site: Site<'_>, +) -> Result { + let log = site.context.log; + let _rule = log.rule(MONTHS_BETWEEN_RULE); + let refuse = |reason: String| outside_conditions(construct.clone(), reason); + let [later, earlier] = parts else { + return Err(refuse(format!( + "MONTHS_BETWEEN of {} arguments", + parts.len() + ))); + }; + let (later, earlier) = ( + convert_expression(later, site)?, + convert_expression(earlier, site)?, + ); + let read = |value: &Emitted| match value.data_type { + SnowflakeType::Date => Ok((value.sql.clone(), "0".to_owned())), + SnowflakeType::TimestampNtz { precision } => { + let day = truncate_timestamp_to_date(&value.sql, precision, log); + let clock = format!( + "(epoch_ns({}) - epoch_ns(CAST({day} AS TIMESTAMP)))", + value.sql + ); + Ok((day, clock)) + } + other => Err(refuse(format!( + "MONTHS_BETWEEN of a {other}, which no recording shows" + ))), + }; + let ((a, a_clock), (b, b_clock)) = (read(&later)?, read(&earlier)?); + let whole = format!("((year({a}) - year({b})) * 12 + month({a}) - month({b}))"); + let whole_only = + format!("(day({a}) = day({b}) OR (last_day({a}) = {a} AND last_day({b}) = {b}))"); + let month = MONTHS_BETWEEN_MONTH_NANOSECONDS; + let numerator = format!( + "(CAST({whole} AS HUGEINT) * {month} + CAST(day({a}) - day({b}) AS HUGEINT) * {} \ + + CAST({a_clock} AS HUGEINT) - CAST({b_clock} AS HUGEINT))", + month / 31 + ); + let (divided, _) = exact_decimal_divide( + (&numerator, MAX_PRECISION, 0), + (&month.to_string(), MAX_PRECISION, 0), + (MONTHS_BETWEEN_TYPE, false), + log, + ) + .map_err(refuse)?; + let whole = cast(whole.as_str(), MONTHS_BETWEEN_TYPE, log).map_err(refuse)?; + Ok(Emitted::new( + format!("(CASE WHEN {whole_only} THEN {whole} ELSE {divided} END)"), + MONTHS_BETWEEN_TYPE, + )) +} + +/// `operand` moved by `amount` of `unit`; with `month_end`, a month's last day stays a last day. +fn shift( + (unit, mut amount): (&str, Emitted), + operand: Emitted, + month_end: bool, + (construct, site): (&Construct, Site<'_>), +) -> Result { + let refuse = |reason: String| outside_conditions(construct.clone(), reason); match amount.data_type { SnowflakeType::Number { scale: 0, .. } => {} SnowflakeType::Number { .. } | SnowflakeType::Float => { @@ -371,9 +469,9 @@ pub(crate) fn date_add( }, ); } + SnowflakeType::Null if month_end => {} other => return Err(refuse(format!("a {other} amount"))), } - let operand = convert_expression(value, site)?; let operand = match operand.data_type { SnowflakeType::Varchar => { let ntz = SnowflakeType::TimestampNtz { precision: 9 }; @@ -394,7 +492,7 @@ pub(crate) fn date_add( if let SnowflakeType::Time { .. } = operand.data_type { let _time = site.context.log.rule(TIME_ARITHMETIC); let sql = - time_add_value(&operand.sql, (&unit, &amount.sql), site.context.log).map_err(refuse)?; + time_add_value(&operand.sql, (unit, &amount.sql), site.context.log).map_err(refuse)?; return Ok(Emitted::new(sql, operand.data_type)); } if let SnowflakeType::TimestampNtz { precision } = operand.data_type @@ -404,11 +502,16 @@ pub(crate) fn date_add( } let sql = date_add_value( &operand.sql, - (&unit, &format!("CAST({} AS BIGINT)", amount.sql)), + (unit, &format!("CAST({} AS BIGINT)", amount.sql)), operand.data_type, site.context.log, ) .map_err(refuse)?; + let sql = match month_end { + true => kept_month_end((&operand.sql, sql), operand.data_type, site.context.log) + .map_err(refuse)?, + false => sql, + }; let (sql, data_type) = match operand.data_type { SnowflakeType::TimestampNtz { precision } if precision < MAX_FRACTION_DIGITS_U8 => { let nanoseconds = SnowflakeType::TimestampNtz { @@ -434,6 +537,26 @@ pub(crate) fn date_add( )) } +/// `shifted` moved on to its month's last day where `original`, of `data_type`, is a month's last day. +fn kept_month_end( + (original, shifted): (&str, String), + data_type: SnowflakeType, + log: &ProvenanceLog, +) -> Result { + let day = |sql: &str| match data_type { + SnowflakeType::TimestampNtz { precision } => { + truncate_timestamp_to_date(sql, precision, log) + } + _ => sql.to_owned(), + }; + let (before, after) = (day(original), day(&shifted)); + let days = format!( + "CASE WHEN last_day({before}) = {before} \ + THEN datediff('day', {after}, last_day({after})) ELSE 0 END" + ); + date_add_value(&shifted, (DAY_UNIT, &days), data_type, log) +} + /// DATEDIFF(unit, start, end) with a keyword unit. pub(crate) fn date_diff(payload: &Value, site: Site<'_>) -> Result { let construct = node_construct("date_diff"); diff --git a/crates/sqlquack-rules/src/conversion/constants.rs b/crates/sqlquack-rules/src/conversion/constants.rs index 1e120371d..8d3a4edb0 100644 --- a/crates/sqlquack-rules/src/conversion/constants.rs +++ b/crates/sqlquack-rules/src/conversion/constants.rs @@ -23,27 +23,6 @@ pub const NAMED_AGGREGATE_NAMES: &[&str] = &[ "MEDIAN", MULTI_COUNT, ]; -/// Variances and standard deviations: the name, whether over the population, and whether a root. -pub(crate) const VARIANCE_AGGREGATES: &[(&str, bool, bool)] = &[ - ("VARIANCE", false, false), - ("VAR_SAMP", false, false), - ("VAR_POP", true, false), - ("VARIANCE_POP", true, false), - ("STDDEV", false, true), - ("STDDEV_SAMP", false, true), - ("STDDEV_POP", true, true), -]; -/// Covariances, and whether over the population. -pub(crate) const COVARIANCE_AGGREGATES: &[(&str, bool)] = - &[("COVAR_SAMP", false), ("COVAR_POP", true)]; -/// The variance Snowflake rejects with DISTINCT, though its other spellings accept it. -pub(crate) const NO_DISTINCT_VARIANCE: &str = "VARIANCE"; -/// An exact variance's result scale: this base plus twice the input's scale, at most the widest. -pub(crate) const VARIANCE_SCALES: (u8, u8) = (6, 12); -/// The lambda parameter squaring each distinct value of an exact variance. -pub(crate) const SQUARED_PARAMETER: &str = "__sqlquack_squared"; -/// The type name statistics cast NUMBER and text arguments to. -pub(crate) const FLOAT_TYPE_NAME: &str = "FLOAT"; /// COUNT of several values, which the parser keeps as a named call. pub const MULTI_COUNT: &str = "COUNT"; /// The recorded type of ARRAY_POSITION. @@ -632,6 +611,19 @@ pub const VARIANCE_RULE: &str = "rule.variance"; pub const VARIANCE_FLOAT_RULE: &str = "rule.variance_float"; /// COVAR_SAMP and COVAR_POP of NUMBER or FLOAT pairs, with exact sums. pub const COVARIANCE_RULE: &str = "rule.covariance"; +/// ADD_MONTHS of a DATE or timestamp: DATEADD of the rounded months, a month's last day kept. +pub const ADD_MONTHS_RULE: &str = "rule.add_months"; +/// MONTHS_BETWEEN of DATEs and naive timestamps. +pub const MONTHS_BETWEEN_RULE: &str = "rule.months_between"; +/// MONTHS_BETWEEN's recorded type. +pub const MONTHS_BETWEEN_TYPE: SnowflakeType = SnowflakeType::Number { + precision: 27, + scale: 6, +}; +/// The month MONTHS_BETWEEN divides a partial month by: 31 days, in nanoseconds. +pub(crate) const MONTHS_BETWEEN_MONTH_NANOSECONDS: i128 = 31 * 86_400 * 1_000_000_000; +/// The DATEADD unit ADD_MONTHS moves by. +pub(crate) const MONTH_UNIT: &str = "MONTH"; /// NVL2 of a non-semi-structured value and two branches that unify without reading text. pub const NVL2_RULE: &str = "rule.nvl2"; /// NULLIF. diff --git a/sqlquack.toml b/sqlquack.toml index 0e3f9f55b..8fa1f25dd 100644 --- a/sqlquack.toml +++ b/sqlquack.toml @@ -434,7 +434,7 @@ failure = "loud" reason = "User decision 2026-10-25: a DATE, TIMESTAMP or TIME inside a VARIANT keeps its type in Snowflake, which DuckDB JSON cannot. The converted SQL stores the JSON string Snowflake writes for the value (batch 8 recorded YYYY-MM-DD, YYYY-MM-DD HH:MI:SS.FFF and a ' Z' suffix for TIMESTAMP_LTZ in UTC), raising where that text drops digits or an offset. TYPEOF and IS_DATE-style reads of a string in such a text raise rather than answer VARCHAR or false. TIMESTAMP_TZ (its offset is not kept), TIME and BINARY (its hex text is indistinguishable from plain strings) stay refused." [divergences.timestamp_range] -constructs = ["node:cast", "node:try_cast", "node:add", "node:sub", "fn:DATEADD", "fn:TIMEADD", "fn:TIMESTAMPADD", "fn:CONVERT_TIMEZONE", "fn:TO_TIMESTAMP", "fn:TO_TIMESTAMP_NTZ", "fn:TRY_TO_TIMESTAMP", "fn:TRY_TO_TIMESTAMP_NTZ", "fn:TRY_TO_TIMESTAMP_TZ", "fn:TIMESTAMP_NTZ_FROM_PARTS", "node:eq", "node:neq", "node:lt", "node:lte", "node:gt", "node:gte", "node:in", "fn:TO_TIMESTAMP_TZ", "fn:TO_TIMESTAMP_LTZ", "fn:TRY_TO_TIMESTAMP_LTZ"] +constructs = ["node:cast", "node:try_cast", "node:add", "node:sub", "fn:DATEADD", "fn:TIMEADD", "fn:TIMESTAMPADD", "fn:ADD_MONTHS", "fn:CONVERT_TIMEZONE", "fn:TO_TIMESTAMP", "fn:TO_TIMESTAMP_NTZ", "fn:TRY_TO_TIMESTAMP", "fn:TRY_TO_TIMESTAMP_NTZ", "fn:TRY_TO_TIMESTAMP_TZ", "fn:TIMESTAMP_NTZ_FROM_PARTS", "node:eq", "node:neq", "node:lt", "node:lte", "node:gt", "node:gte", "node:in", "fn:TO_TIMESTAMP_TZ", "fn:TO_TIMESTAMP_LTZ", "fn:TRY_TO_TIMESTAMP_LTZ"] default = "warn" strict = "refuse" failure = "loud" From fd1c3b959489502013252219d9926503755235b2 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:40:43 +0000 Subject: [PATCH 09/10] coverage: re-record the translation results baseline for the D267 rules Nine forms move from refused to matching: NVL2, MONTHS_BETWEEN, VAR_SAMP and its window form, VARIANCE_POP, STDDEV_SAMP, STDDEV_POP, and COVAR_SAMP and COVAR_POP over windows. Nothing regresses. --- coverage/translation/results-baseline.json | 310 +++++++++++---------- 1 file changed, 168 insertions(+), 142 deletions(-) diff --git a/coverage/translation/results-baseline.json b/coverage/translation/results-baseline.json index 1f2b8cd77..610596671 100644 --- a/coverage/translation/results-baseline.json +++ b/coverage/translation/results-baseline.json @@ -5163,8 +5163,8 @@ "generated/null_positions/nullif.toml#nullif_same_types": "matching", "generated/null_positions/nvl.toml#nvl_mixed_types": "matching", "generated/null_positions/nvl.toml#nvl_same_types": "matching", - "generated/null_positions/nvl2.toml#nvl2_mixed_types": "refused", - "generated/null_positions/nvl2.toml#nvl2_same_types": "refused", + "generated/null_positions/nvl2.toml#nvl2_mixed_types": "matching", + "generated/null_positions/nvl2.toml#nvl2_same_types": "matching", "generated/null_positions/pipes.toml#pipes_texts": "matching", "generated/null_positions/zeroifnull.toml#zeroifnull_mixed_types": "matching", "generated/null_positions/zeroifnull.toml#zeroifnull_same_types": "matching", @@ -8551,13 +8551,14 @@ "translation/functions/acosh.toml#t003_null": "refused", "translation/functions/acosh.toml#t004_edge": "refused", "translation/functions/acosh.toml#t005_below_one": "refused", - "translation/functions/add-months.toml#t001_date": "refused", - "translation/functions/add-months.toml#t002_timestamp": "refused", + "translation/functions/add-months.toml#t001_date": "matching", + "translation/functions/add-months.toml#t002_timestamp": "matching", "translation/functions/add-months.toml#t003_timestamp_tz": "refused", - "translation/functions/add-months.toml#t004_null_date_or_timestamp_expr": "refused", - "translation/functions/add-months.toml#t005_null_num_months_expr": "refused", - "translation/functions/add-months.toml#t006_month_end": "refused", - "translation/functions/add-months.toml#t007_fractional": "refused", + "translation/functions/add-months.toml#t004_null_date_or_timestamp_expr": "matching", + "translation/functions/add-months.toml#t005_null_num_months_expr": "matching", + "translation/functions/add-months.toml#t006_month_end": "matching", + "translation/functions/add-months.toml#t007_fractional": "matching", + "translation/functions/add-months.toml#t008_negative_and_day_31": "matching", "translation/functions/any-value.any-value-over.toml#t001_partition": "refused", "translation/functions/any-value.any-value-over.toml#t002_empty_over": "refused", "translation/functions/any-value.any-value-over.toml#t003_null_expr1": "refused", @@ -9151,6 +9152,8 @@ "translation/functions/corr.corr.toml#t004_null_y": "refused", "translation/functions/corr.corr.toml#t005_null_x": "refused", "translation/functions/corr.corr.toml#t006_empty_input": "refused", + "translation/functions/corr.corr.toml#t007_large_mean": "refused", + "translation/functions/corr.corr.toml#t008_decimal": "refused", "translation/functions/corr.x-over.toml#t001_partition": "refused", "translation/functions/corr.x-over.toml#t002_empty_over": "refused", "translation/functions/corr.x-over.toml#t003_null_y": "refused", @@ -9188,24 +9191,28 @@ "translation/functions/count.count-over.toml#t007_null_partition_key": "matching", "translation/functions/count.count-over.toml#t008_distinct_partition": "matching", "translation/functions/count.count-over.toml#t009_star": "matching", - "translation/functions/covar-pop.covar-pop.toml#t001_pairs": "refused", - "translation/functions/covar-pop.covar-pop.toml#t002_float": "refused", - "translation/functions/covar-pop.covar-pop.toml#t003_null_y": "refused", - "translation/functions/covar-pop.covar-pop.toml#t004_null_x": "refused", - "translation/functions/covar-pop.covar-pop.toml#t005_empty_input": "refused", - "translation/functions/covar-pop.x-over.toml#t001_partition": "refused", - "translation/functions/covar-pop.x-over.toml#t002_empty_over": "refused", - "translation/functions/covar-pop.x-over.toml#t003_null_y": "refused", - "translation/functions/covar-pop.x-over.toml#t004_null_partition_key": "refused", - "translation/functions/covar-samp.covar-samp.toml#t001_pairs": "refused", - "translation/functions/covar-samp.covar-samp.toml#t002_float": "refused", - "translation/functions/covar-samp.covar-samp.toml#t003_null_y": "refused", - "translation/functions/covar-samp.covar-samp.toml#t004_null_x": "refused", - "translation/functions/covar-samp.covar-samp.toml#t005_empty_input": "refused", - "translation/functions/covar-samp.x-over.toml#t001_partition": "refused", - "translation/functions/covar-samp.x-over.toml#t002_empty_over": "refused", - "translation/functions/covar-samp.x-over.toml#t003_null_y": "refused", - "translation/functions/covar-samp.x-over.toml#t004_null_partition_key": "refused", + "translation/functions/covar-pop.covar-pop.toml#t001_pairs": "matching", + "translation/functions/covar-pop.covar-pop.toml#t002_float": "matching", + "translation/functions/covar-pop.covar-pop.toml#t003_null_y": "matching", + "translation/functions/covar-pop.covar-pop.toml#t004_null_x": "matching", + "translation/functions/covar-pop.covar-pop.toml#t005_empty_input": "matching", + "translation/functions/covar-pop.covar-pop.toml#t006_large_mean": "declared_divergence", + "translation/functions/covar-pop.covar-pop.toml#t007_decimal": "refused", + "translation/functions/covar-pop.x-over.toml#t001_partition": "matching", + "translation/functions/covar-pop.x-over.toml#t002_empty_over": "matching", + "translation/functions/covar-pop.x-over.toml#t003_null_y": "matching", + "translation/functions/covar-pop.x-over.toml#t004_null_partition_key": "matching", + "translation/functions/covar-samp.covar-samp.toml#t001_pairs": "matching", + "translation/functions/covar-samp.covar-samp.toml#t002_float": "matching", + "translation/functions/covar-samp.covar-samp.toml#t003_null_y": "matching", + "translation/functions/covar-samp.covar-samp.toml#t004_null_x": "matching", + "translation/functions/covar-samp.covar-samp.toml#t005_empty_input": "matching", + "translation/functions/covar-samp.covar-samp.toml#t006_large_mean": "declared_divergence", + "translation/functions/covar-samp.covar-samp.toml#t007_decimal": "refused", + "translation/functions/covar-samp.x-over.toml#t001_partition": "matching", + "translation/functions/covar-samp.x-over.toml#t002_empty_over": "matching", + "translation/functions/covar-samp.x-over.toml#t003_null_y": "matching", + "translation/functions/covar-samp.x-over.toml#t004_null_partition_key": "matching", "translation/functions/cume-dist.toml#t001_ties": "refused", "translation/functions/cume-dist.toml#t002_frame": "refused", "translation/functions/cume-dist.toml#t003_null_order_expr": "refused", @@ -10059,13 +10066,15 @@ "translation/functions/monthname.toml#t001_date": "refused", "translation/functions/monthname.toml#t002_timestamp_tz": "refused", "translation/functions/monthname.toml#t003_null_date_or_timestamp_expr": "refused", - "translation/functions/months-between.toml#t001_date": "refused", - "translation/functions/months-between.toml#t002_timestamp": "refused", - "translation/functions/months-between.toml#t003_mixed": "refused", - "translation/functions/months-between.toml#t004_null_date_expr1": "refused", - "translation/functions/months-between.toml#t005_null_date_expr2": "refused", - "translation/functions/months-between.toml#t006_reversed": "refused", - "translation/functions/months-between.toml#t007_leap": "refused", + "translation/functions/months-between.toml#t001_date": "matching", + "translation/functions/months-between.toml#t002_timestamp": "matching", + "translation/functions/months-between.toml#t003_mixed": "matching", + "translation/functions/months-between.toml#t004_null_date_expr1": "matching", + "translation/functions/months-between.toml#t005_null_date_expr2": "matching", + "translation/functions/months-between.toml#t006_reversed": "matching", + "translation/functions/months-between.toml#t007_leap": "matching", + "translation/functions/months-between.toml#t008_month_ends_with_time": "matching", + "translation/functions/months-between.toml#t009_long_negative": "matching", "translation/functions/next-day.toml#t001_date": "refused", "translation/functions/next-day.toml#t002_day_name_forms": "refused", "translation/functions/next-day.toml#t003_timestamp": "refused", @@ -10092,10 +10101,10 @@ "translation/functions/nvl.toml#t003_null_expr1": "matching", "translation/functions/nvl.toml#t004_null_expr2": "matching", "translation/functions/nvl.toml#t005_unconvertible": "matching_error", - "translation/functions/nvl2.toml#t001_mixed": "refused", - "translation/functions/nvl2.toml#t002_null_expr1": "refused", - "translation/functions/nvl2.toml#t003_null_expr2": "refused", - "translation/functions/nvl2.toml#t004_null_expr3": "refused", + "translation/functions/nvl2.toml#t001_mixed": "matching", + "translation/functions/nvl2.toml#t002_null_expr1": "matching", + "translation/functions/nvl2.toml#t003_null_expr2": "matching", + "translation/functions/nvl2.toml#t004_null_expr3": "matching", "translation/functions/object-agg.object-agg-over.toml#t001_partition": "refused", "translation/functions/object-agg.object-agg-over.toml#t002_empty_over": "refused", "translation/functions/object-agg.object-agg-over.toml#t003_null_value": "refused", @@ -10930,51 +10939,57 @@ "translation/functions/startswith.toml#t002_null_expr1": "matching", "translation/functions/startswith.toml#t003_null_expr2": "matching", "translation/functions/startswith.toml#t004_empty": "matching", - "translation/functions/stddev-pop.stddev-pop-over.toml#t001_partition": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t002_empty_over": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t003_order_ties": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t004_order_desc": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev-pop.stddev-pop-over.toml#t001_partition": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t002_empty_over": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t003_order_ties": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t004_order_desc": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev-pop.stddev-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t001_number": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t002_single_row": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t003_varchar": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t004_distinct": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t005_null_expr1": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t006_empty_input": "refused", - "translation/functions/stddev.stddev-over.toml#t001_partition": "refused", - "translation/functions/stddev.stddev-over.toml#t002_empty_over": "refused", - "translation/functions/stddev.stddev-over.toml#t003_order_ties": "refused", - "translation/functions/stddev.stddev-over.toml#t004_order_desc": "refused", - "translation/functions/stddev.stddev-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev.stddev-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev.stddev-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev-pop.stddev-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t001_number": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t002_single_row": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t003_varchar": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t004_distinct": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t005_null_expr1": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t006_empty_input": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t007_large_mean": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t008_decimal": "refused", + "translation/functions/stddev.stddev-over.toml#t001_partition": "matching", + "translation/functions/stddev.stddev-over.toml#t002_empty_over": "matching", + "translation/functions/stddev.stddev-over.toml#t003_order_ties": "matching", + "translation/functions/stddev.stddev-over.toml#t004_order_desc": "matching", + "translation/functions/stddev.stddev-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev.stddev-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev.stddev-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev.stddev-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev.stddev-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t001_partition": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t002_empty_over": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t003_order_ties": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t004_order_desc": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev.stddev-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t001_partition": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t002_empty_over": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t003_order_ties": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t004_order_desc": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev.stddev-samp-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev.stddev-samp.toml#t001_number": "refused", - "translation/functions/stddev.stddev-samp.toml#t002_single_row": "refused", - "translation/functions/stddev.stddev-samp.toml#t003_varchar": "refused", - "translation/functions/stddev.stddev-samp.toml#t004_distinct": "refused", - "translation/functions/stddev.stddev-samp.toml#t005_null_expr1": "refused", - "translation/functions/stddev.stddev-samp.toml#t006_empty_input": "refused", - "translation/functions/stddev.stddev.toml#t001_number": "refused", - "translation/functions/stddev.stddev.toml#t002_single_row": "refused", - "translation/functions/stddev.stddev.toml#t003_varchar": "refused", - "translation/functions/stddev.stddev.toml#t004_distinct": "refused", - "translation/functions/stddev.stddev.toml#t005_null_expr1": "refused", - "translation/functions/stddev.stddev.toml#t006_empty_input": "refused", + "translation/functions/stddev.stddev-samp-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev.stddev-samp.toml#t001_number": "matching", + "translation/functions/stddev.stddev-samp.toml#t002_single_row": "matching", + "translation/functions/stddev.stddev-samp.toml#t003_varchar": "matching", + "translation/functions/stddev.stddev-samp.toml#t004_distinct": "matching", + "translation/functions/stddev.stddev-samp.toml#t005_null_expr1": "matching", + "translation/functions/stddev.stddev-samp.toml#t006_empty_input": "matching", + "translation/functions/stddev.stddev-samp.toml#t007_large_mean": "matching", + "translation/functions/stddev.stddev-samp.toml#t008_decimal": "refused", + "translation/functions/stddev.stddev.toml#t001_number": "matching", + "translation/functions/stddev.stddev.toml#t002_single_row": "matching", + "translation/functions/stddev.stddev.toml#t003_varchar": "matching", + "translation/functions/stddev.stddev.toml#t004_distinct": "matching", + "translation/functions/stddev.stddev.toml#t005_null_expr1": "matching", + "translation/functions/stddev.stddev.toml#t006_empty_input": "matching", + "translation/functions/stddev.stddev.toml#t007_large_mean": "matching", + "translation/functions/stddev.stddev.toml#t008_decimal": "refused", "translation/functions/strip-null-value.toml#t001_json_null": "matching", "translation/functions/strip-null-value.toml#t002_nested": "matching", "translation/functions/strip-null-value.toml#t003_null_variant_expr": "matching", @@ -11422,64 +11437,75 @@ "translation/functions/uuid-string.toml#t001_random": "refused", "translation/functions/uuid-string.toml#t002_named": "refused", "translation/functions/uuid-string.toml#t003_null_name": "refused", - "translation/functions/var-pop.var-pop-over.toml#t001_partition": "refused", - "translation/functions/var-pop.var-pop-over.toml#t002_empty_over": "refused", - "translation/functions/var-pop.var-pop-over.toml#t003_order_ties": "refused", - "translation/functions/var-pop.var-pop-over.toml#t004_order_desc": "refused", - "translation/functions/var-pop.var-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/var-pop.var-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/var-pop.var-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/var-pop.var-pop-over.toml#t001_partition": "matching", + "translation/functions/var-pop.var-pop-over.toml#t002_empty_over": "matching", + "translation/functions/var-pop.var-pop-over.toml#t003_order_ties": "matching", + "translation/functions/var-pop.var-pop-over.toml#t004_order_desc": "matching", + "translation/functions/var-pop.var-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/var-pop.var-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/var-pop.var-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/var-pop.var-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/var-pop.var-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/var-pop.var-pop.toml#t001_number": "refused", - "translation/functions/var-pop.var-pop.toml#t002_single_row": "refused", - "translation/functions/var-pop.var-pop.toml#t003_varchar": "refused", - "translation/functions/var-pop.var-pop.toml#t004_distinct": "refused", - "translation/functions/var-pop.var-pop.toml#t005_null_expr1": "refused", - "translation/functions/var-pop.var-pop.toml#t006_empty_input": "refused", - "translation/functions/var-samp.var-samp-over.toml#t001_partition": "refused", - "translation/functions/var-samp.var-samp-over.toml#t002_empty_over": "refused", - "translation/functions/var-samp.var-samp-over.toml#t003_order_ties": "refused", - "translation/functions/var-samp.var-samp-over.toml#t004_order_desc": "refused", - "translation/functions/var-samp.var-samp-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/var-samp.var-samp-over.toml#t006_null_expr1": "refused", - "translation/functions/var-samp.var-samp-over.toml#t007_null_partition_key": "refused", - "translation/functions/var-samp.var-samp-over.toml#t008_distinct_ignored": "refused", - "translation/functions/var-samp.var-samp.toml#t001_number": "refused", - "translation/functions/var-samp.var-samp.toml#t002_single_row": "refused", - "translation/functions/var-samp.var-samp.toml#t003_varchar": "refused", - "translation/functions/var-samp.var-samp.toml#t004_distinct": "refused", - "translation/functions/var-samp.var-samp.toml#t005_null_expr1": "refused", - "translation/functions/var-samp.var-samp.toml#t006_empty_input": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t001_partition": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t002_empty_over": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t003_order_ties": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t004_order_desc": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/var-pop.var-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/var-pop.var-pop.toml#t001_number": "matching", + "translation/functions/var-pop.var-pop.toml#t002_single_row": "matching", + "translation/functions/var-pop.var-pop.toml#t003_varchar": "matching", + "translation/functions/var-pop.var-pop.toml#t004_distinct": "matching", + "translation/functions/var-pop.var-pop.toml#t005_null_expr1": "matching", + "translation/functions/var-pop.var-pop.toml#t006_empty_input": "matching", + "translation/functions/var-pop.var-pop.toml#t007_large_mean": "matching", + "translation/functions/var-pop.var-pop.toml#t008_decimal": "refused", + "translation/functions/var-samp.var-samp-over.toml#t001_partition": "matching", + "translation/functions/var-samp.var-samp-over.toml#t002_empty_over": "matching", + "translation/functions/var-samp.var-samp-over.toml#t003_order_ties": "matching", + "translation/functions/var-samp.var-samp-over.toml#t004_order_desc": "matching", + "translation/functions/var-samp.var-samp-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/var-samp.var-samp-over.toml#t006_null_expr1": "matching", + "translation/functions/var-samp.var-samp-over.toml#t007_null_partition_key": "matching", + "translation/functions/var-samp.var-samp-over.toml#t008_distinct_ignored": "matching", + "translation/functions/var-samp.var-samp.toml#t001_number": "matching", + "translation/functions/var-samp.var-samp.toml#t002_single_row": "matching", + "translation/functions/var-samp.var-samp.toml#t003_varchar": "matching", + "translation/functions/var-samp.var-samp.toml#t004_distinct": "matching", + "translation/functions/var-samp.var-samp.toml#t005_null_expr1": "matching", + "translation/functions/var-samp.var-samp.toml#t006_empty_input": "matching", + "translation/functions/var-samp.var-samp.toml#t007_large_mean": "matching", + "translation/functions/var-samp.var-samp.toml#t008_decimal": "refused", + "translation/functions/variance-pop.variance-pop-over.toml#t001_partition": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t002_empty_over": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t003_order_ties": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t004_order_desc": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/variance-pop.variance-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/variance-pop.variance-pop.toml#t001_number": "refused", - "translation/functions/variance-pop.variance-pop.toml#t002_single_row": "refused", - "translation/functions/variance-pop.variance-pop.toml#t003_varchar": "refused", - "translation/functions/variance-pop.variance-pop.toml#t004_distinct": "refused", - "translation/functions/variance-pop.variance-pop.toml#t005_null_expr1": "refused", - "translation/functions/variance-pop.variance-pop.toml#t006_empty_input": "refused", - "translation/functions/variance.variance-over.toml#t001_partition": "refused", - "translation/functions/variance.variance-over.toml#t002_empty_over": "refused", - "translation/functions/variance.variance-over.toml#t003_order_ties": "refused", - "translation/functions/variance.variance-over.toml#t004_order_desc": "refused", - "translation/functions/variance.variance-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/variance.variance-over.toml#t006_null_expr1": "refused", - "translation/functions/variance.variance-over.toml#t007_null_partition_key": "refused", + "translation/functions/variance-pop.variance-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/variance-pop.variance-pop.toml#t001_number": "matching", + "translation/functions/variance-pop.variance-pop.toml#t002_single_row": "matching", + "translation/functions/variance-pop.variance-pop.toml#t003_varchar": "matching", + "translation/functions/variance-pop.variance-pop.toml#t004_distinct": "matching", + "translation/functions/variance-pop.variance-pop.toml#t005_null_expr1": "matching", + "translation/functions/variance-pop.variance-pop.toml#t006_empty_input": "matching", + "translation/functions/variance-pop.variance-pop.toml#t007_large_mean": "matching", + "translation/functions/variance-pop.variance-pop.toml#t008_decimal": "refused", + "translation/functions/variance.variance-over.toml#t001_partition": "matching", + "translation/functions/variance.variance-over.toml#t002_empty_over": "matching", + "translation/functions/variance.variance-over.toml#t003_order_ties": "matching", + "translation/functions/variance.variance-over.toml#t004_order_desc": "matching", + "translation/functions/variance.variance-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/variance.variance-over.toml#t006_null_expr1": "matching", + "translation/functions/variance.variance-over.toml#t007_null_partition_key": "matching", "translation/functions/variance.variance-over.toml#t008_distinct_order": "refused", - "translation/functions/variance.variance.toml#t001_number": "refused", - "translation/functions/variance.variance.toml#t002_single_row": "refused", - "translation/functions/variance.variance.toml#t003_varchar": "refused", + "translation/functions/variance.variance.toml#t001_number": "matching", + "translation/functions/variance.variance.toml#t002_single_row": "matching", + "translation/functions/variance.variance.toml#t003_varchar": "matching", "translation/functions/variance.variance.toml#t004_distinct": "refused", - "translation/functions/variance.variance.toml#t005_null_expr1": "refused", - "translation/functions/variance.variance.toml#t006_empty_input": "refused", + "translation/functions/variance.variance.toml#t005_null_expr1": "matching", + "translation/functions/variance.variance.toml#t006_empty_input": "matching", + "translation/functions/variance.variance.toml#t007_large_mean": "matching", + "translation/functions/variance.variance.toml#t008_decimal": "refused", + "translation/functions/variance.variance.toml#t009_rounding_tie": "matching", + "translation/functions/variance.variance.toml#t010_scales": "refused", + "translation/functions/variance.variance.toml#t011_float_large_mean": "declared_divergence", "translation/functions/vector-avg.toml#t001_float": "refused", "translation/functions/vector-avg.toml#t002_int": "refused", "translation/functions/vector-avg.toml#t003_null_vector_column": "refused", @@ -13198,9 +13224,9 @@ "sf.functions.count.count": "refused", "sf.functions.count.count-over": "refused", "sf.functions.covar-pop.covar-pop": "refused", - "sf.functions.covar-pop.x-over": "refused", + "sf.functions.covar-pop.x-over": "matching", "sf.functions.covar-samp.covar-samp": "refused", - "sf.functions.covar-samp.x-over": "refused", + "sf.functions.covar-samp.x-over": "matching", "sf.functions.cume-dist": "refused", "sf.functions.cumulative-privacy-losses": "not_translatable", "sf.functions.current-account": "not_translatable", @@ -13735,7 +13761,7 @@ "sf.functions.model-monitor-stat-metric.model-monitor-stat-metric": "not_translatable", "sf.functions.model-monitor-stat-metric.model-monitor-stat-metric-service": "not_translatable", "sf.functions.monthname": "refused", - "sf.functions.months-between": "refused", + "sf.functions.months-between": "matching", "sf.functions.my-budget-summary-snowflake-local": "not_translatable", "sf.functions.network-rule-references.container-name-container-type": "not_translatable", "sf.functions.network-rule-references.network-rule-name": "not_translatable", @@ -13747,7 +13773,7 @@ "sf.functions.nullif": "matching", "sf.functions.nullifzero": "matching", "sf.functions.nvl": "matching", - "sf.functions.nvl2": "refused", + "sf.functions.nvl2": "matching", "sf.functions.object-agg.object-agg": "refused", "sf.functions.object-agg.object-agg-over": "refused", "sf.functions.object-construct": "refused", @@ -13985,11 +14011,11 @@ "sf.functions.stage-directory-file-registration-history": "not_translatable", "sf.functions.stage-storage-usage-history": "not_translatable", "sf.functions.startswith": "matching", - "sf.functions.stddev-pop.stddev-pop": "refused", + "sf.functions.stddev-pop.stddev-pop": "matching", "sf.functions.stddev-pop.stddev-pop-over": "refused", "sf.functions.stddev.stddev": "refused", "sf.functions.stddev.stddev-over": "refused", - "sf.functions.stddev.stddev-samp": "refused", + "sf.functions.stddev.stddev-samp": "matching", "sf.functions.stddev.stddev-samp-over": "refused", "sf.functions.storage-lifecycle-policy-history.policy-name": "not_translatable", "sf.functions.storage-lifecycle-policy-history.ref-entity-name-ref-entity-domain": "not_translatable", @@ -14382,9 +14408,9 @@ "sf.functions.validate-pipe-load": "not_translatable", "sf.functions.var-pop.var-pop": "refused", "sf.functions.var-pop.var-pop-over": "refused", - "sf.functions.var-samp.var-samp": "refused", - "sf.functions.var-samp.var-samp-over": "refused", - "sf.functions.variance-pop.variance-pop": "refused", + "sf.functions.var-samp.var-samp": "matching", + "sf.functions.var-samp.var-samp-over": "matching", + "sf.functions.variance-pop.variance-pop": "matching", "sf.functions.variance-pop.variance-pop-over": "refused", "sf.functions.variance.variance": "refused", "sf.functions.variance.variance-over": "refused", From 78910c363d48e5bbecfc627f1b567e0f2fe8252c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 21:37:31 +0000 Subject: [PATCH 10/10] coverage: re-record the translation baseline with the statistics, month and NVL2 rules --- coverage/translation/results-baseline.json | 572 +++++++++++---------- 1 file changed, 299 insertions(+), 273 deletions(-) diff --git a/coverage/translation/results-baseline.json b/coverage/translation/results-baseline.json index fd328a67b..8e22ff07d 100644 --- a/coverage/translation/results-baseline.json +++ b/coverage/translation/results-baseline.json @@ -5163,8 +5163,8 @@ "generated/null_positions/nullif.toml#nullif_same_types": "matching", "generated/null_positions/nvl.toml#nvl_mixed_types": "matching", "generated/null_positions/nvl.toml#nvl_same_types": "matching", - "generated/null_positions/nvl2.toml#nvl2_mixed_types": "refused", - "generated/null_positions/nvl2.toml#nvl2_same_types": "refused", + "generated/null_positions/nvl2.toml#nvl2_mixed_types": "matching", + "generated/null_positions/nvl2.toml#nvl2_same_types": "matching", "generated/null_positions/pipes.toml#pipes_texts": "matching", "generated/null_positions/zeroifnull.toml#zeroifnull_mixed_types": "matching", "generated/null_positions/zeroifnull.toml#zeroifnull_same_types": "matching", @@ -8677,13 +8677,14 @@ "translation/functions/acosh.toml#t003_null": "refused", "translation/functions/acosh.toml#t004_edge": "refused", "translation/functions/acosh.toml#t005_below_one": "refused", - "translation/functions/add-months.toml#t001_date": "refused", - "translation/functions/add-months.toml#t002_timestamp": "refused", + "translation/functions/add-months.toml#t001_date": "matching", + "translation/functions/add-months.toml#t002_timestamp": "matching", "translation/functions/add-months.toml#t003_timestamp_tz": "refused", - "translation/functions/add-months.toml#t004_null_date_or_timestamp_expr": "refused", - "translation/functions/add-months.toml#t005_null_num_months_expr": "refused", - "translation/functions/add-months.toml#t006_month_end": "refused", - "translation/functions/add-months.toml#t007_fractional": "refused", + "translation/functions/add-months.toml#t004_null_date_or_timestamp_expr": "matching", + "translation/functions/add-months.toml#t005_null_num_months_expr": "matching", + "translation/functions/add-months.toml#t006_month_end": "matching", + "translation/functions/add-months.toml#t007_fractional": "matching", + "translation/functions/add-months.toml#t008_negative_and_day_31": "matching", "translation/functions/any-value.any-value-over.toml#t001_partition": "refused", "translation/functions/any-value.any-value-over.toml#t002_empty_over": "refused", "translation/functions/any-value.any-value-over.toml#t003_null_expr1": "refused", @@ -9283,6 +9284,8 @@ "translation/functions/corr.corr.toml#t004_null_y": "refused", "translation/functions/corr.corr.toml#t005_null_x": "refused", "translation/functions/corr.corr.toml#t006_empty_input": "refused", + "translation/functions/corr.corr.toml#t007_large_mean": "refused", + "translation/functions/corr.corr.toml#t008_decimal": "refused", "translation/functions/corr.x-over.toml#t001_partition": "refused", "translation/functions/corr.x-over.toml#t002_empty_over": "refused", "translation/functions/corr.x-over.toml#t003_null_y": "refused", @@ -9325,24 +9328,28 @@ "translation/functions/count.count.toml#t003_distinct_multi": "matching", "translation/functions/count.count.toml#t007_null_expr1": "matching", "translation/functions/count.count.toml#t008_empty_input": "matching", - "translation/functions/covar-pop.covar-pop.toml#t001_pairs": "refused", - "translation/functions/covar-pop.covar-pop.toml#t002_float": "refused", - "translation/functions/covar-pop.covar-pop.toml#t003_null_y": "refused", - "translation/functions/covar-pop.covar-pop.toml#t004_null_x": "refused", - "translation/functions/covar-pop.covar-pop.toml#t005_empty_input": "refused", - "translation/functions/covar-pop.x-over.toml#t001_partition": "refused", - "translation/functions/covar-pop.x-over.toml#t002_empty_over": "refused", - "translation/functions/covar-pop.x-over.toml#t003_null_y": "refused", - "translation/functions/covar-pop.x-over.toml#t004_null_partition_key": "refused", - "translation/functions/covar-samp.covar-samp.toml#t001_pairs": "refused", - "translation/functions/covar-samp.covar-samp.toml#t002_float": "refused", - "translation/functions/covar-samp.covar-samp.toml#t003_null_y": "refused", - "translation/functions/covar-samp.covar-samp.toml#t004_null_x": "refused", - "translation/functions/covar-samp.covar-samp.toml#t005_empty_input": "refused", - "translation/functions/covar-samp.x-over.toml#t001_partition": "refused", - "translation/functions/covar-samp.x-over.toml#t002_empty_over": "refused", - "translation/functions/covar-samp.x-over.toml#t003_null_y": "refused", - "translation/functions/covar-samp.x-over.toml#t004_null_partition_key": "refused", + "translation/functions/covar-pop.covar-pop.toml#t001_pairs": "matching", + "translation/functions/covar-pop.covar-pop.toml#t002_float": "matching", + "translation/functions/covar-pop.covar-pop.toml#t003_null_y": "matching", + "translation/functions/covar-pop.covar-pop.toml#t004_null_x": "matching", + "translation/functions/covar-pop.covar-pop.toml#t005_empty_input": "matching", + "translation/functions/covar-pop.covar-pop.toml#t006_large_mean": "declared_divergence", + "translation/functions/covar-pop.covar-pop.toml#t007_decimal": "refused", + "translation/functions/covar-pop.x-over.toml#t001_partition": "matching", + "translation/functions/covar-pop.x-over.toml#t002_empty_over": "matching", + "translation/functions/covar-pop.x-over.toml#t003_null_y": "matching", + "translation/functions/covar-pop.x-over.toml#t004_null_partition_key": "matching", + "translation/functions/covar-samp.covar-samp.toml#t001_pairs": "matching", + "translation/functions/covar-samp.covar-samp.toml#t002_float": "matching", + "translation/functions/covar-samp.covar-samp.toml#t003_null_y": "matching", + "translation/functions/covar-samp.covar-samp.toml#t004_null_x": "matching", + "translation/functions/covar-samp.covar-samp.toml#t005_empty_input": "matching", + "translation/functions/covar-samp.covar-samp.toml#t006_large_mean": "declared_divergence", + "translation/functions/covar-samp.covar-samp.toml#t007_decimal": "refused", + "translation/functions/covar-samp.x-over.toml#t001_partition": "matching", + "translation/functions/covar-samp.x-over.toml#t002_empty_over": "matching", + "translation/functions/covar-samp.x-over.toml#t003_null_y": "matching", + "translation/functions/covar-samp.x-over.toml#t004_null_partition_key": "matching", "translation/functions/cume-dist.toml#t001_ties": "refused", "translation/functions/cume-dist.toml#t002_frame": "refused", "translation/functions/cume-dist.toml#t003_null_order_expr": "refused", @@ -10224,13 +10231,15 @@ "translation/functions/monthname.toml#t001_date": "refused", "translation/functions/monthname.toml#t002_timestamp_tz": "refused", "translation/functions/monthname.toml#t003_null_date_or_timestamp_expr": "refused", - "translation/functions/months-between.toml#t001_date": "refused", - "translation/functions/months-between.toml#t002_timestamp": "refused", - "translation/functions/months-between.toml#t003_mixed": "refused", - "translation/functions/months-between.toml#t004_null_date_expr1": "refused", - "translation/functions/months-between.toml#t005_null_date_expr2": "refused", - "translation/functions/months-between.toml#t006_reversed": "refused", - "translation/functions/months-between.toml#t007_leap": "refused", + "translation/functions/months-between.toml#t001_date": "matching", + "translation/functions/months-between.toml#t002_timestamp": "matching", + "translation/functions/months-between.toml#t003_mixed": "matching", + "translation/functions/months-between.toml#t004_null_date_expr1": "matching", + "translation/functions/months-between.toml#t005_null_date_expr2": "matching", + "translation/functions/months-between.toml#t006_reversed": "matching", + "translation/functions/months-between.toml#t007_leap": "matching", + "translation/functions/months-between.toml#t008_month_ends_with_time": "matching", + "translation/functions/months-between.toml#t009_long_negative": "matching", "translation/functions/next-day.toml#t001_date": "refused", "translation/functions/next-day.toml#t002_day_name_forms": "refused", "translation/functions/next-day.toml#t003_timestamp": "refused", @@ -10260,10 +10269,10 @@ "translation/functions/nvl.toml#t003_null_expr1": "matching", "translation/functions/nvl.toml#t004_null_expr2": "matching", "translation/functions/nvl.toml#t005_unconvertible": "matching_error", - "translation/functions/nvl2.toml#t001_mixed": "refused", - "translation/functions/nvl2.toml#t002_null_expr1": "refused", - "translation/functions/nvl2.toml#t003_null_expr2": "refused", - "translation/functions/nvl2.toml#t004_null_expr3": "refused", + "translation/functions/nvl2.toml#t001_mixed": "matching", + "translation/functions/nvl2.toml#t002_null_expr1": "matching", + "translation/functions/nvl2.toml#t003_null_expr2": "matching", + "translation/functions/nvl2.toml#t004_null_expr3": "matching", "translation/functions/object-agg.object-agg-over.toml#t001_partition": "refused", "translation/functions/object-agg.object-agg-over.toml#t002_empty_over": "refused", "translation/functions/object-agg.object-agg-over.toml#t003_null_value": "refused", @@ -11098,51 +11107,57 @@ "translation/functions/startswith.toml#t002_null_expr1": "matching", "translation/functions/startswith.toml#t003_null_expr2": "matching", "translation/functions/startswith.toml#t004_empty": "matching", - "translation/functions/stddev-pop.stddev-pop-over.toml#t001_partition": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t002_empty_over": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t003_order_ties": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t004_order_desc": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev-pop.stddev-pop-over.toml#t001_partition": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t002_empty_over": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t003_order_ties": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t004_order_desc": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev-pop.stddev-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev-pop.stddev-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev-pop.stddev-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t001_number": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t002_single_row": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t003_varchar": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t004_distinct": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t005_null_expr1": "refused", - "translation/functions/stddev-pop.stddev-pop.toml#t006_empty_input": "refused", - "translation/functions/stddev.stddev-over.toml#t001_partition": "refused", - "translation/functions/stddev.stddev-over.toml#t002_empty_over": "refused", - "translation/functions/stddev.stddev-over.toml#t003_order_ties": "refused", - "translation/functions/stddev.stddev-over.toml#t004_order_desc": "refused", - "translation/functions/stddev.stddev-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev.stddev-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev.stddev-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev-pop.stddev-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t001_number": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t002_single_row": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t003_varchar": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t004_distinct": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t005_null_expr1": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t006_empty_input": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t007_large_mean": "matching", + "translation/functions/stddev-pop.stddev-pop.toml#t008_decimal": "refused", + "translation/functions/stddev.stddev-over.toml#t001_partition": "matching", + "translation/functions/stddev.stddev-over.toml#t002_empty_over": "matching", + "translation/functions/stddev.stddev-over.toml#t003_order_ties": "matching", + "translation/functions/stddev.stddev-over.toml#t004_order_desc": "matching", + "translation/functions/stddev.stddev-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev.stddev-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev.stddev-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev.stddev-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev.stddev-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t001_partition": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t002_empty_over": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t003_order_ties": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t004_order_desc": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t006_null_expr1": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t007_null_partition_key": "refused", + "translation/functions/stddev.stddev-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t001_partition": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t002_empty_over": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t003_order_ties": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t004_order_desc": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t006_null_expr1": "matching", + "translation/functions/stddev.stddev-samp-over.toml#t007_null_partition_key": "matching", "translation/functions/stddev.stddev-samp-over.toml#t008_distinct_order": "refused", - "translation/functions/stddev.stddev-samp-over.toml#t009_distinct_partition": "refused", - "translation/functions/stddev.stddev-samp.toml#t001_number": "refused", - "translation/functions/stddev.stddev-samp.toml#t002_single_row": "refused", - "translation/functions/stddev.stddev-samp.toml#t003_varchar": "refused", - "translation/functions/stddev.stddev-samp.toml#t004_distinct": "refused", - "translation/functions/stddev.stddev-samp.toml#t005_null_expr1": "refused", - "translation/functions/stddev.stddev-samp.toml#t006_empty_input": "refused", - "translation/functions/stddev.stddev.toml#t001_number": "refused", - "translation/functions/stddev.stddev.toml#t002_single_row": "refused", - "translation/functions/stddev.stddev.toml#t003_varchar": "refused", - "translation/functions/stddev.stddev.toml#t004_distinct": "refused", - "translation/functions/stddev.stddev.toml#t005_null_expr1": "refused", - "translation/functions/stddev.stddev.toml#t006_empty_input": "refused", + "translation/functions/stddev.stddev-samp-over.toml#t009_distinct_partition": "matching", + "translation/functions/stddev.stddev-samp.toml#t001_number": "matching", + "translation/functions/stddev.stddev-samp.toml#t002_single_row": "matching", + "translation/functions/stddev.stddev-samp.toml#t003_varchar": "matching", + "translation/functions/stddev.stddev-samp.toml#t004_distinct": "matching", + "translation/functions/stddev.stddev-samp.toml#t005_null_expr1": "matching", + "translation/functions/stddev.stddev-samp.toml#t006_empty_input": "matching", + "translation/functions/stddev.stddev-samp.toml#t007_large_mean": "matching", + "translation/functions/stddev.stddev-samp.toml#t008_decimal": "refused", + "translation/functions/stddev.stddev.toml#t001_number": "matching", + "translation/functions/stddev.stddev.toml#t002_single_row": "matching", + "translation/functions/stddev.stddev.toml#t003_varchar": "matching", + "translation/functions/stddev.stddev.toml#t004_distinct": "matching", + "translation/functions/stddev.stddev.toml#t005_null_expr1": "matching", + "translation/functions/stddev.stddev.toml#t006_empty_input": "matching", + "translation/functions/stddev.stddev.toml#t007_large_mean": "matching", + "translation/functions/stddev.stddev.toml#t008_decimal": "refused", "translation/functions/strip-null-value.toml#t001_json_null": "matching", "translation/functions/strip-null-value.toml#t002_nested": "matching", "translation/functions/strip-null-value.toml#t003_null_variant_expr": "matching", @@ -11594,64 +11609,75 @@ "translation/functions/uuid-string.toml#t001_random": "refused", "translation/functions/uuid-string.toml#t002_named": "refused", "translation/functions/uuid-string.toml#t003_null_name": "refused", - "translation/functions/var-pop.var-pop-over.toml#t001_partition": "refused", - "translation/functions/var-pop.var-pop-over.toml#t002_empty_over": "refused", - "translation/functions/var-pop.var-pop-over.toml#t003_order_ties": "refused", - "translation/functions/var-pop.var-pop-over.toml#t004_order_desc": "refused", - "translation/functions/var-pop.var-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/var-pop.var-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/var-pop.var-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/var-pop.var-pop-over.toml#t001_partition": "matching", + "translation/functions/var-pop.var-pop-over.toml#t002_empty_over": "matching", + "translation/functions/var-pop.var-pop-over.toml#t003_order_ties": "matching", + "translation/functions/var-pop.var-pop-over.toml#t004_order_desc": "matching", + "translation/functions/var-pop.var-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/var-pop.var-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/var-pop.var-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/var-pop.var-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/var-pop.var-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/var-pop.var-pop.toml#t001_number": "refused", - "translation/functions/var-pop.var-pop.toml#t002_single_row": "refused", - "translation/functions/var-pop.var-pop.toml#t003_varchar": "refused", - "translation/functions/var-pop.var-pop.toml#t004_distinct": "refused", - "translation/functions/var-pop.var-pop.toml#t005_null_expr1": "refused", - "translation/functions/var-pop.var-pop.toml#t006_empty_input": "refused", - "translation/functions/var-samp.var-samp-over.toml#t001_partition": "refused", - "translation/functions/var-samp.var-samp-over.toml#t002_empty_over": "refused", - "translation/functions/var-samp.var-samp-over.toml#t003_order_ties": "refused", - "translation/functions/var-samp.var-samp-over.toml#t004_order_desc": "refused", - "translation/functions/var-samp.var-samp-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/var-samp.var-samp-over.toml#t006_null_expr1": "refused", - "translation/functions/var-samp.var-samp-over.toml#t007_null_partition_key": "refused", - "translation/functions/var-samp.var-samp-over.toml#t008_distinct_ignored": "refused", - "translation/functions/var-samp.var-samp.toml#t001_number": "refused", - "translation/functions/var-samp.var-samp.toml#t002_single_row": "refused", - "translation/functions/var-samp.var-samp.toml#t003_varchar": "refused", - "translation/functions/var-samp.var-samp.toml#t004_distinct": "refused", - "translation/functions/var-samp.var-samp.toml#t005_null_expr1": "refused", - "translation/functions/var-samp.var-samp.toml#t006_empty_input": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t001_partition": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t002_empty_over": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t003_order_ties": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t004_order_desc": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t006_null_expr1": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t007_null_partition_key": "refused", + "translation/functions/var-pop.var-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/var-pop.var-pop.toml#t001_number": "matching", + "translation/functions/var-pop.var-pop.toml#t002_single_row": "matching", + "translation/functions/var-pop.var-pop.toml#t003_varchar": "matching", + "translation/functions/var-pop.var-pop.toml#t004_distinct": "matching", + "translation/functions/var-pop.var-pop.toml#t005_null_expr1": "matching", + "translation/functions/var-pop.var-pop.toml#t006_empty_input": "matching", + "translation/functions/var-pop.var-pop.toml#t007_large_mean": "matching", + "translation/functions/var-pop.var-pop.toml#t008_decimal": "refused", + "translation/functions/var-samp.var-samp-over.toml#t001_partition": "matching", + "translation/functions/var-samp.var-samp-over.toml#t002_empty_over": "matching", + "translation/functions/var-samp.var-samp-over.toml#t003_order_ties": "matching", + "translation/functions/var-samp.var-samp-over.toml#t004_order_desc": "matching", + "translation/functions/var-samp.var-samp-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/var-samp.var-samp-over.toml#t006_null_expr1": "matching", + "translation/functions/var-samp.var-samp-over.toml#t007_null_partition_key": "matching", + "translation/functions/var-samp.var-samp-over.toml#t008_distinct_ignored": "matching", + "translation/functions/var-samp.var-samp.toml#t001_number": "matching", + "translation/functions/var-samp.var-samp.toml#t002_single_row": "matching", + "translation/functions/var-samp.var-samp.toml#t003_varchar": "matching", + "translation/functions/var-samp.var-samp.toml#t004_distinct": "matching", + "translation/functions/var-samp.var-samp.toml#t005_null_expr1": "matching", + "translation/functions/var-samp.var-samp.toml#t006_empty_input": "matching", + "translation/functions/var-samp.var-samp.toml#t007_large_mean": "matching", + "translation/functions/var-samp.var-samp.toml#t008_decimal": "refused", + "translation/functions/variance-pop.variance-pop-over.toml#t001_partition": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t002_empty_over": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t003_order_ties": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t004_order_desc": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t006_null_expr1": "matching", + "translation/functions/variance-pop.variance-pop-over.toml#t007_null_partition_key": "matching", "translation/functions/variance-pop.variance-pop-over.toml#t008_distinct_order": "refused", - "translation/functions/variance-pop.variance-pop-over.toml#t009_distinct_partition": "refused", - "translation/functions/variance-pop.variance-pop.toml#t001_number": "refused", - "translation/functions/variance-pop.variance-pop.toml#t002_single_row": "refused", - "translation/functions/variance-pop.variance-pop.toml#t003_varchar": "refused", - "translation/functions/variance-pop.variance-pop.toml#t004_distinct": "refused", - "translation/functions/variance-pop.variance-pop.toml#t005_null_expr1": "refused", - "translation/functions/variance-pop.variance-pop.toml#t006_empty_input": "refused", - "translation/functions/variance.variance-over.toml#t001_partition": "refused", - "translation/functions/variance.variance-over.toml#t002_empty_over": "refused", - "translation/functions/variance.variance-over.toml#t003_order_ties": "refused", - "translation/functions/variance.variance-over.toml#t004_order_desc": "refused", - "translation/functions/variance.variance-over.toml#t005_nulls_first_frame": "refused", - "translation/functions/variance.variance-over.toml#t006_null_expr1": "refused", - "translation/functions/variance.variance-over.toml#t007_null_partition_key": "refused", + "translation/functions/variance-pop.variance-pop-over.toml#t009_distinct_partition": "matching", + "translation/functions/variance-pop.variance-pop.toml#t001_number": "matching", + "translation/functions/variance-pop.variance-pop.toml#t002_single_row": "matching", + "translation/functions/variance-pop.variance-pop.toml#t003_varchar": "matching", + "translation/functions/variance-pop.variance-pop.toml#t004_distinct": "matching", + "translation/functions/variance-pop.variance-pop.toml#t005_null_expr1": "matching", + "translation/functions/variance-pop.variance-pop.toml#t006_empty_input": "matching", + "translation/functions/variance-pop.variance-pop.toml#t007_large_mean": "matching", + "translation/functions/variance-pop.variance-pop.toml#t008_decimal": "refused", + "translation/functions/variance.variance-over.toml#t001_partition": "matching", + "translation/functions/variance.variance-over.toml#t002_empty_over": "matching", + "translation/functions/variance.variance-over.toml#t003_order_ties": "matching", + "translation/functions/variance.variance-over.toml#t004_order_desc": "matching", + "translation/functions/variance.variance-over.toml#t005_nulls_first_frame": "matching", + "translation/functions/variance.variance-over.toml#t006_null_expr1": "matching", + "translation/functions/variance.variance-over.toml#t007_null_partition_key": "matching", "translation/functions/variance.variance-over.toml#t008_distinct_order": "refused", - "translation/functions/variance.variance.toml#t001_number": "refused", - "translation/functions/variance.variance.toml#t002_single_row": "refused", - "translation/functions/variance.variance.toml#t003_varchar": "refused", + "translation/functions/variance.variance.toml#t001_number": "matching", + "translation/functions/variance.variance.toml#t002_single_row": "matching", + "translation/functions/variance.variance.toml#t003_varchar": "matching", "translation/functions/variance.variance.toml#t004_distinct": "refused", - "translation/functions/variance.variance.toml#t005_null_expr1": "refused", - "translation/functions/variance.variance.toml#t006_empty_input": "refused", + "translation/functions/variance.variance.toml#t005_null_expr1": "matching", + "translation/functions/variance.variance.toml#t006_empty_input": "matching", + "translation/functions/variance.variance.toml#t007_large_mean": "matching", + "translation/functions/variance.variance.toml#t008_decimal": "refused", + "translation/functions/variance.variance.toml#t009_rounding_tie": "matching", + "translation/functions/variance.variance.toml#t010_scales": "refused", + "translation/functions/variance.variance.toml#t011_float_large_mean": "declared_divergence", "translation/functions/vector-avg.toml#t001_float": "refused", "translation/functions/vector-avg.toml#t002_int": "refused", "translation/functions/vector-avg.toml#t003_null_vector_column": "refused", @@ -13405,9 +13431,9 @@ "sf.functions.count.count": "refused", "sf.functions.count.count-over": "refused", "sf.functions.covar-pop.covar-pop": "refused", - "sf.functions.covar-pop.x-over": "refused", + "sf.functions.covar-pop.x-over": "matching", "sf.functions.covar-samp.covar-samp": "refused", - "sf.functions.covar-samp.x-over": "refused", + "sf.functions.covar-samp.x-over": "matching", "sf.functions.cume-dist": "refused", "sf.functions.cumulative-privacy-losses": "not_translatable", "sf.functions.current-account": "not_translatable", @@ -13942,7 +13968,7 @@ "sf.functions.model-monitor-stat-metric.model-monitor-stat-metric": "not_translatable", "sf.functions.model-monitor-stat-metric.model-monitor-stat-metric-service": "not_translatable", "sf.functions.monthname": "refused", - "sf.functions.months-between": "refused", + "sf.functions.months-between": "matching", "sf.functions.my-budget-summary-snowflake-local": "not_translatable", "sf.functions.network-rule-references.container-name-container-type": "not_translatable", "sf.functions.network-rule-references.network-rule-name": "not_translatable", @@ -13954,7 +13980,7 @@ "sf.functions.nullif": "unmet_obligations", "sf.functions.nullifzero": "matching", "sf.functions.nvl": "matching", - "sf.functions.nvl2": "refused", + "sf.functions.nvl2": "matching", "sf.functions.object-agg.object-agg": "refused", "sf.functions.object-agg.object-agg-over": "refused", "sf.functions.object-construct": "refused", @@ -14192,11 +14218,11 @@ "sf.functions.stage-directory-file-registration-history": "not_translatable", "sf.functions.stage-storage-usage-history": "not_translatable", "sf.functions.startswith": "matching", - "sf.functions.stddev-pop.stddev-pop": "refused", + "sf.functions.stddev-pop.stddev-pop": "matching", "sf.functions.stddev-pop.stddev-pop-over": "refused", "sf.functions.stddev.stddev": "refused", "sf.functions.stddev.stddev-over": "refused", - "sf.functions.stddev.stddev-samp": "refused", + "sf.functions.stddev.stddev-samp": "matching", "sf.functions.stddev.stddev-samp-over": "refused", "sf.functions.storage-lifecycle-policy-history.policy-name": "not_translatable", "sf.functions.storage-lifecycle-policy-history.ref-entity-name-ref-entity-domain": "not_translatable", @@ -14589,9 +14615,9 @@ "sf.functions.validate-pipe-load": "not_translatable", "sf.functions.var-pop.var-pop": "refused", "sf.functions.var-pop.var-pop-over": "refused", - "sf.functions.var-samp.var-samp": "refused", - "sf.functions.var-samp.var-samp-over": "refused", - "sf.functions.variance-pop.variance-pop": "refused", + "sf.functions.var-samp.var-samp": "matching", + "sf.functions.var-samp.var-samp-over": "matching", + "sf.functions.variance-pop.variance-pop": "matching", "sf.functions.variance-pop.variance-pop-over": "refused", "sf.functions.variance.variance": "refused", "sf.functions.variance.variance-over": "refused", @@ -15638,13 +15664,13 @@ "sf.functions.acosh.t003": "unmet", "sf.functions.acosh.t004": "unmet", "sf.functions.acosh.t005": "unmet", - "sf.functions.add-months.t001": "unmet", - "sf.functions.add-months.t002": "unmet", + "sf.functions.add-months.t001": "met", + "sf.functions.add-months.t002": "met", "sf.functions.add-months.t003": "unmet", - "sf.functions.add-months.t004": "unmet", - "sf.functions.add-months.t005": "unmet", - "sf.functions.add-months.t006": "unmet", - "sf.functions.add-months.t007": "unmet", + "sf.functions.add-months.t004": "met", + "sf.functions.add-months.t005": "met", + "sf.functions.add-months.t006": "met", + "sf.functions.add-months.t007": "met", "sf.functions.any-value.any-value-over.t001": "unmet", "sf.functions.any-value.any-value-over.t002": "unmet", "sf.functions.any-value.any-value-over.t003": "unmet", @@ -16290,24 +16316,24 @@ "sf.functions.count.count.t006": "held", "sf.functions.count.count.t007": "met", "sf.functions.count.count.t008": "met", - "sf.functions.covar-pop.covar-pop.t001": "unmet", - "sf.functions.covar-pop.covar-pop.t002": "unmet", - "sf.functions.covar-pop.covar-pop.t003": "unmet", - "sf.functions.covar-pop.covar-pop.t004": "unmet", - "sf.functions.covar-pop.covar-pop.t005": "unmet", - "sf.functions.covar-pop.x-over.t001": "unmet", - "sf.functions.covar-pop.x-over.t002": "unmet", - "sf.functions.covar-pop.x-over.t003": "unmet", - "sf.functions.covar-pop.x-over.t004": "unmet", - "sf.functions.covar-samp.covar-samp.t001": "unmet", - "sf.functions.covar-samp.covar-samp.t002": "unmet", - "sf.functions.covar-samp.covar-samp.t003": "unmet", - "sf.functions.covar-samp.covar-samp.t004": "unmet", - "sf.functions.covar-samp.covar-samp.t005": "unmet", - "sf.functions.covar-samp.x-over.t001": "unmet", - "sf.functions.covar-samp.x-over.t002": "unmet", - "sf.functions.covar-samp.x-over.t003": "unmet", - "sf.functions.covar-samp.x-over.t004": "unmet", + "sf.functions.covar-pop.covar-pop.t001": "met", + "sf.functions.covar-pop.covar-pop.t002": "met", + "sf.functions.covar-pop.covar-pop.t003": "met", + "sf.functions.covar-pop.covar-pop.t004": "met", + "sf.functions.covar-pop.covar-pop.t005": "met", + "sf.functions.covar-pop.x-over.t001": "met", + "sf.functions.covar-pop.x-over.t002": "met", + "sf.functions.covar-pop.x-over.t003": "met", + "sf.functions.covar-pop.x-over.t004": "met", + "sf.functions.covar-samp.covar-samp.t001": "met", + "sf.functions.covar-samp.covar-samp.t002": "met", + "sf.functions.covar-samp.covar-samp.t003": "met", + "sf.functions.covar-samp.covar-samp.t004": "met", + "sf.functions.covar-samp.covar-samp.t005": "met", + "sf.functions.covar-samp.x-over.t001": "met", + "sf.functions.covar-samp.x-over.t002": "met", + "sf.functions.covar-samp.x-over.t003": "met", + "sf.functions.covar-samp.x-over.t004": "met", "sf.functions.cume-dist.t001": "unmet", "sf.functions.cume-dist.t002": "unmet", "sf.functions.cume-dist.t003": "unmet", @@ -17195,13 +17221,13 @@ "sf.functions.monthname.t001": "unmet", "sf.functions.monthname.t002": "unmet", "sf.functions.monthname.t003": "unmet", - "sf.functions.months-between.t001": "unmet", - "sf.functions.months-between.t002": "unmet", - "sf.functions.months-between.t003": "unmet", - "sf.functions.months-between.t004": "unmet", - "sf.functions.months-between.t005": "unmet", - "sf.functions.months-between.t006": "unmet", - "sf.functions.months-between.t007": "unmet", + "sf.functions.months-between.t001": "met", + "sf.functions.months-between.t002": "met", + "sf.functions.months-between.t003": "met", + "sf.functions.months-between.t004": "met", + "sf.functions.months-between.t005": "met", + "sf.functions.months-between.t006": "met", + "sf.functions.months-between.t007": "met", "sf.functions.next-day.t001": "unmet", "sf.functions.next-day.t002": "unmet", "sf.functions.next-day.t003": "unmet", @@ -17232,10 +17258,10 @@ "sf.functions.nvl.t003": "met", "sf.functions.nvl.t004": "met", "sf.functions.nvl.t005": "met", - "sf.functions.nvl2.t001": "unmet", - "sf.functions.nvl2.t002": "unmet", - "sf.functions.nvl2.t003": "unmet", - "sf.functions.nvl2.t004": "unmet", + "sf.functions.nvl2.t001": "met", + "sf.functions.nvl2.t002": "met", + "sf.functions.nvl2.t003": "met", + "sf.functions.nvl2.t004": "met", "sf.functions.object-agg.object-agg-over.t001": "unmet", "sf.functions.object-agg.object-agg-over.t002": "unmet", "sf.functions.object-agg.object-agg-over.t003": "unmet", @@ -18070,51 +18096,51 @@ "sf.functions.startswith.t002": "met", "sf.functions.startswith.t003": "met", "sf.functions.startswith.t004": "met", - "sf.functions.stddev-pop.stddev-pop-over.t001": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t002": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t003": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t004": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t005": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t006": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t007": "unmet", + "sf.functions.stddev-pop.stddev-pop-over.t001": "met", + "sf.functions.stddev-pop.stddev-pop-over.t002": "met", + "sf.functions.stddev-pop.stddev-pop-over.t003": "met", + "sf.functions.stddev-pop.stddev-pop-over.t004": "met", + "sf.functions.stddev-pop.stddev-pop-over.t005": "met", + "sf.functions.stddev-pop.stddev-pop-over.t006": "met", + "sf.functions.stddev-pop.stddev-pop-over.t007": "met", "sf.functions.stddev-pop.stddev-pop-over.t008": "unmet", - "sf.functions.stddev-pop.stddev-pop-over.t009": "unmet", - "sf.functions.stddev-pop.stddev-pop.t001": "unmet", - "sf.functions.stddev-pop.stddev-pop.t002": "unmet", - "sf.functions.stddev-pop.stddev-pop.t003": "unmet", - "sf.functions.stddev-pop.stddev-pop.t004": "unmet", - "sf.functions.stddev-pop.stddev-pop.t005": "unmet", - "sf.functions.stddev-pop.stddev-pop.t006": "unmet", - "sf.functions.stddev.stddev-over.t001": "unmet", - "sf.functions.stddev.stddev-over.t002": "unmet", - "sf.functions.stddev.stddev-over.t003": "unmet", - "sf.functions.stddev.stddev-over.t004": "unmet", - "sf.functions.stddev.stddev-over.t005": "unmet", - "sf.functions.stddev.stddev-over.t006": "unmet", - "sf.functions.stddev.stddev-over.t007": "unmet", + "sf.functions.stddev-pop.stddev-pop-over.t009": "met", + "sf.functions.stddev-pop.stddev-pop.t001": "met", + "sf.functions.stddev-pop.stddev-pop.t002": "met", + "sf.functions.stddev-pop.stddev-pop.t003": "met", + "sf.functions.stddev-pop.stddev-pop.t004": "met", + "sf.functions.stddev-pop.stddev-pop.t005": "met", + "sf.functions.stddev-pop.stddev-pop.t006": "met", + "sf.functions.stddev.stddev-over.t001": "met", + "sf.functions.stddev.stddev-over.t002": "met", + "sf.functions.stddev.stddev-over.t003": "met", + "sf.functions.stddev.stddev-over.t004": "met", + "sf.functions.stddev.stddev-over.t005": "met", + "sf.functions.stddev.stddev-over.t006": "met", + "sf.functions.stddev.stddev-over.t007": "met", "sf.functions.stddev.stddev-over.t008": "unmet", - "sf.functions.stddev.stddev-over.t009": "unmet", - "sf.functions.stddev.stddev-samp-over.t001": "unmet", - "sf.functions.stddev.stddev-samp-over.t002": "unmet", - "sf.functions.stddev.stddev-samp-over.t003": "unmet", - "sf.functions.stddev.stddev-samp-over.t004": "unmet", - "sf.functions.stddev.stddev-samp-over.t005": "unmet", - "sf.functions.stddev.stddev-samp-over.t006": "unmet", - "sf.functions.stddev.stddev-samp-over.t007": "unmet", + "sf.functions.stddev.stddev-over.t009": "met", + "sf.functions.stddev.stddev-samp-over.t001": "met", + "sf.functions.stddev.stddev-samp-over.t002": "met", + "sf.functions.stddev.stddev-samp-over.t003": "met", + "sf.functions.stddev.stddev-samp-over.t004": "met", + "sf.functions.stddev.stddev-samp-over.t005": "met", + "sf.functions.stddev.stddev-samp-over.t006": "met", + "sf.functions.stddev.stddev-samp-over.t007": "met", "sf.functions.stddev.stddev-samp-over.t008": "unmet", - "sf.functions.stddev.stddev-samp-over.t009": "unmet", - "sf.functions.stddev.stddev-samp.t001": "unmet", - "sf.functions.stddev.stddev-samp.t002": "unmet", - "sf.functions.stddev.stddev-samp.t003": "unmet", - "sf.functions.stddev.stddev-samp.t004": "unmet", - "sf.functions.stddev.stddev-samp.t005": "unmet", - "sf.functions.stddev.stddev-samp.t006": "unmet", - "sf.functions.stddev.stddev.t001": "unmet", - "sf.functions.stddev.stddev.t002": "unmet", - "sf.functions.stddev.stddev.t003": "unmet", - "sf.functions.stddev.stddev.t004": "unmet", - "sf.functions.stddev.stddev.t005": "unmet", - "sf.functions.stddev.stddev.t006": "unmet", + "sf.functions.stddev.stddev-samp-over.t009": "met", + "sf.functions.stddev.stddev-samp.t001": "met", + "sf.functions.stddev.stddev-samp.t002": "met", + "sf.functions.stddev.stddev-samp.t003": "met", + "sf.functions.stddev.stddev-samp.t004": "met", + "sf.functions.stddev.stddev-samp.t005": "met", + "sf.functions.stddev.stddev-samp.t006": "met", + "sf.functions.stddev.stddev.t001": "met", + "sf.functions.stddev.stddev.t002": "met", + "sf.functions.stddev.stddev.t003": "met", + "sf.functions.stddev.stddev.t004": "met", + "sf.functions.stddev.stddev.t005": "met", + "sf.functions.stddev.stddev.t006": "met", "sf.functions.strip-null-value.t001": "met", "sf.functions.strip-null-value.t002": "met", "sf.functions.strip-null-value.t003": "met", @@ -18567,64 +18593,64 @@ "sf.functions.uuid-string.t001": "unmet", "sf.functions.uuid-string.t002": "unmet", "sf.functions.uuid-string.t003": "unmet", - "sf.functions.var-pop.var-pop-over.t001": "unmet", - "sf.functions.var-pop.var-pop-over.t002": "unmet", - "sf.functions.var-pop.var-pop-over.t003": "unmet", - "sf.functions.var-pop.var-pop-over.t004": "unmet", - "sf.functions.var-pop.var-pop-over.t005": "unmet", - "sf.functions.var-pop.var-pop-over.t006": "unmet", - "sf.functions.var-pop.var-pop-over.t007": "unmet", + "sf.functions.var-pop.var-pop-over.t001": "met", + "sf.functions.var-pop.var-pop-over.t002": "met", + "sf.functions.var-pop.var-pop-over.t003": "met", + "sf.functions.var-pop.var-pop-over.t004": "met", + "sf.functions.var-pop.var-pop-over.t005": "met", + "sf.functions.var-pop.var-pop-over.t006": "met", + "sf.functions.var-pop.var-pop-over.t007": "met", "sf.functions.var-pop.var-pop-over.t008": "unmet", - "sf.functions.var-pop.var-pop-over.t009": "unmet", - "sf.functions.var-pop.var-pop.t001": "unmet", - "sf.functions.var-pop.var-pop.t002": "unmet", - "sf.functions.var-pop.var-pop.t003": "unmet", - "sf.functions.var-pop.var-pop.t004": "unmet", - "sf.functions.var-pop.var-pop.t005": "unmet", - "sf.functions.var-pop.var-pop.t006": "unmet", - "sf.functions.var-samp.var-samp-over.t001": "unmet", - "sf.functions.var-samp.var-samp-over.t002": "unmet", - "sf.functions.var-samp.var-samp-over.t003": "unmet", - "sf.functions.var-samp.var-samp-over.t004": "unmet", - "sf.functions.var-samp.var-samp-over.t005": "unmet", - "sf.functions.var-samp.var-samp-over.t006": "unmet", - "sf.functions.var-samp.var-samp-over.t007": "unmet", - "sf.functions.var-samp.var-samp-over.t008": "unmet", - "sf.functions.var-samp.var-samp.t001": "unmet", - "sf.functions.var-samp.var-samp.t002": "unmet", - "sf.functions.var-samp.var-samp.t003": "unmet", - "sf.functions.var-samp.var-samp.t004": "unmet", - "sf.functions.var-samp.var-samp.t005": "unmet", - "sf.functions.var-samp.var-samp.t006": "unmet", - "sf.functions.variance-pop.variance-pop-over.t001": "unmet", - "sf.functions.variance-pop.variance-pop-over.t002": "unmet", - "sf.functions.variance-pop.variance-pop-over.t003": "unmet", - "sf.functions.variance-pop.variance-pop-over.t004": "unmet", - "sf.functions.variance-pop.variance-pop-over.t005": "unmet", - "sf.functions.variance-pop.variance-pop-over.t006": "unmet", - "sf.functions.variance-pop.variance-pop-over.t007": "unmet", + "sf.functions.var-pop.var-pop-over.t009": "met", + "sf.functions.var-pop.var-pop.t001": "met", + "sf.functions.var-pop.var-pop.t002": "met", + "sf.functions.var-pop.var-pop.t003": "met", + "sf.functions.var-pop.var-pop.t004": "met", + "sf.functions.var-pop.var-pop.t005": "met", + "sf.functions.var-pop.var-pop.t006": "met", + "sf.functions.var-samp.var-samp-over.t001": "met", + "sf.functions.var-samp.var-samp-over.t002": "met", + "sf.functions.var-samp.var-samp-over.t003": "met", + "sf.functions.var-samp.var-samp-over.t004": "met", + "sf.functions.var-samp.var-samp-over.t005": "met", + "sf.functions.var-samp.var-samp-over.t006": "met", + "sf.functions.var-samp.var-samp-over.t007": "met", + "sf.functions.var-samp.var-samp-over.t008": "met", + "sf.functions.var-samp.var-samp.t001": "met", + "sf.functions.var-samp.var-samp.t002": "met", + "sf.functions.var-samp.var-samp.t003": "met", + "sf.functions.var-samp.var-samp.t004": "met", + "sf.functions.var-samp.var-samp.t005": "met", + "sf.functions.var-samp.var-samp.t006": "met", + "sf.functions.variance-pop.variance-pop-over.t001": "met", + "sf.functions.variance-pop.variance-pop-over.t002": "met", + "sf.functions.variance-pop.variance-pop-over.t003": "met", + "sf.functions.variance-pop.variance-pop-over.t004": "met", + "sf.functions.variance-pop.variance-pop-over.t005": "met", + "sf.functions.variance-pop.variance-pop-over.t006": "met", + "sf.functions.variance-pop.variance-pop-over.t007": "met", "sf.functions.variance-pop.variance-pop-over.t008": "unmet", - "sf.functions.variance-pop.variance-pop-over.t009": "unmet", - "sf.functions.variance-pop.variance-pop.t001": "unmet", - "sf.functions.variance-pop.variance-pop.t002": "unmet", - "sf.functions.variance-pop.variance-pop.t003": "unmet", - "sf.functions.variance-pop.variance-pop.t004": "unmet", - "sf.functions.variance-pop.variance-pop.t005": "unmet", - "sf.functions.variance-pop.variance-pop.t006": "unmet", - "sf.functions.variance.variance-over.t001": "unmet", - "sf.functions.variance.variance-over.t002": "unmet", - "sf.functions.variance.variance-over.t003": "unmet", - "sf.functions.variance.variance-over.t004": "unmet", - "sf.functions.variance.variance-over.t005": "unmet", - "sf.functions.variance.variance-over.t006": "unmet", - "sf.functions.variance.variance-over.t007": "unmet", + "sf.functions.variance-pop.variance-pop-over.t009": "met", + "sf.functions.variance-pop.variance-pop.t001": "met", + "sf.functions.variance-pop.variance-pop.t002": "met", + "sf.functions.variance-pop.variance-pop.t003": "met", + "sf.functions.variance-pop.variance-pop.t004": "met", + "sf.functions.variance-pop.variance-pop.t005": "met", + "sf.functions.variance-pop.variance-pop.t006": "met", + "sf.functions.variance.variance-over.t001": "met", + "sf.functions.variance.variance-over.t002": "met", + "sf.functions.variance.variance-over.t003": "met", + "sf.functions.variance.variance-over.t004": "met", + "sf.functions.variance.variance-over.t005": "met", + "sf.functions.variance.variance-over.t006": "met", + "sf.functions.variance.variance-over.t007": "met", "sf.functions.variance.variance-over.t008": "unmet", - "sf.functions.variance.variance.t001": "unmet", - "sf.functions.variance.variance.t002": "unmet", - "sf.functions.variance.variance.t003": "unmet", + "sf.functions.variance.variance.t001": "met", + "sf.functions.variance.variance.t002": "met", + "sf.functions.variance.variance.t003": "met", "sf.functions.variance.variance.t004": "unmet", - "sf.functions.variance.variance.t005": "unmet", - "sf.functions.variance.variance.t006": "unmet", + "sf.functions.variance.variance.t005": "met", + "sf.functions.variance.variance.t006": "met", "sf.functions.vector-avg.t001": "unmet", "sf.functions.vector-avg.t002": "unmet", "sf.functions.vector-avg.t003": "unmet",