|
| 1 | +# Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +# or more contributor license agreements. See the NOTICE file |
| 3 | +# distributed with this work for additional information |
| 4 | +# regarding copyright ownership. The ASF licenses this file |
| 5 | +# to you under the Apache License, Version 2.0 (the |
| 6 | +# "License"); you may not use this file except in compliance |
| 7 | +# with the License. You may obtain a copy of the License at |
| 8 | + |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + |
| 11 | +# Unless required by applicable law or agreed to in writing, |
| 12 | +# software distributed under the License is distributed on an |
| 13 | +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +# KIND, either express or implied. See the License for the |
| 15 | +# specific language governing permissions and limitations |
| 16 | +# under the License. |
| 17 | + |
| 18 | +########## |
| 19 | +## Floor Preimage Tests |
| 20 | +## |
| 21 | +## Tests for floor function preimage optimization: |
| 22 | +## floor(col) = N transforms to col >= N AND col < N + 1 |
| 23 | +## |
| 24 | +## Uses representative types only (Float64, Int32, Decimal128). |
| 25 | +## Unit tests cover all type variants. |
| 26 | +########## |
| 27 | + |
| 28 | +# Setup: Single table with representative types |
| 29 | +statement ok |
| 30 | +CREATE TABLE test_data ( |
| 31 | + id INT, |
| 32 | + float_val DOUBLE, |
| 33 | + int_val INT, |
| 34 | + decimal_val DECIMAL(10,2) |
| 35 | +) AS VALUES |
| 36 | + (1, 5.3, 100, 100.00), |
| 37 | + (2, 5.7, 101, 100.50), |
| 38 | + (3, 6.0, 102, 101.00), |
| 39 | + (4, 6.5, -5, 101.99), |
| 40 | + (5, 7.0, 0, 102.00), |
| 41 | + (6, NULL, NULL, NULL); |
| 42 | + |
| 43 | +########## |
| 44 | +## Data Correctness Tests |
| 45 | +########## |
| 46 | + |
| 47 | +# Float64: floor(x) = 5 matches values in [5.0, 6.0) |
| 48 | +query I rowsort |
| 49 | +SELECT id FROM test_data WHERE floor(float_val) = arrow_cast(5, 'Float64'); |
| 50 | +---- |
| 51 | +1 |
| 52 | +2 |
| 53 | + |
| 54 | +# Int32: floor(x) = 100 matches values in [100, 101) |
| 55 | +query I rowsort |
| 56 | +SELECT id FROM test_data WHERE floor(int_val) = 100; |
| 57 | +---- |
| 58 | +1 |
| 59 | + |
| 60 | +# Decimal128: floor(x) = 100 matches values in [100.00, 101.00) |
| 61 | +query I rowsort |
| 62 | +SELECT id FROM test_data WHERE floor(decimal_val) = arrow_cast(100, 'Decimal128(10,2)'); |
| 63 | +---- |
| 64 | +1 |
| 65 | +2 |
| 66 | + |
| 67 | +# Negative value: floor(x) = -5 matches values in [-5, -4) |
| 68 | +query I rowsort |
| 69 | +SELECT id FROM test_data WHERE floor(int_val) = -5; |
| 70 | +---- |
| 71 | +4 |
| 72 | + |
| 73 | +# Zero value: floor(x) = 0 matches values in [0, 1) |
| 74 | +query I rowsort |
| 75 | +SELECT id FROM test_data WHERE floor(int_val) = 0; |
| 76 | +---- |
| 77 | +5 |
| 78 | + |
| 79 | +# Column on RHS (same result as LHS) |
| 80 | +query I rowsort |
| 81 | +SELECT id FROM test_data WHERE arrow_cast(5, 'Float64') = floor(float_val); |
| 82 | +---- |
| 83 | +1 |
| 84 | +2 |
| 85 | + |
| 86 | +# IS NOT DISTINCT FROM (excludes NULLs) |
| 87 | +query I rowsort |
| 88 | +SELECT id FROM test_data WHERE floor(float_val) IS NOT DISTINCT FROM arrow_cast(5, 'Float64'); |
| 89 | +---- |
| 90 | +1 |
| 91 | +2 |
| 92 | + |
| 93 | +# IS DISTINCT FROM (includes NULLs) |
| 94 | +query I rowsort |
| 95 | +SELECT id FROM test_data WHERE floor(float_val) IS DISTINCT FROM arrow_cast(5, 'Float64'); |
| 96 | +---- |
| 97 | +3 |
| 98 | +4 |
| 99 | +5 |
| 100 | +6 |
| 101 | + |
| 102 | +# Non-integer literal (empty result - floor returns integers) |
| 103 | +query I rowsort |
| 104 | +SELECT id FROM test_data WHERE floor(float_val) = arrow_cast(5.5, 'Float64'); |
| 105 | +---- |
| 106 | + |
| 107 | +########## |
| 108 | +## EXPLAIN Tests - Plan Optimization |
| 109 | +########## |
| 110 | + |
| 111 | +statement ok |
| 112 | +set datafusion.explain.logical_plan_only = true; |
| 113 | + |
| 114 | +# 1. Basic: Float64 - floor(col) = N transforms to col >= N AND col < N+1 |
| 115 | +query TT |
| 116 | +EXPLAIN SELECT * FROM test_data WHERE floor(float_val) = arrow_cast(5, 'Float64'); |
| 117 | +---- |
| 118 | +logical_plan |
| 119 | +01)Filter: test_data.float_val >= Float64(5) AND test_data.float_val < Float64(6) |
| 120 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 121 | + |
| 122 | +# 2. Basic: Int32 - transformed (coerced to Float64) |
| 123 | +query TT |
| 124 | +EXPLAIN SELECT * FROM test_data WHERE floor(int_val) = 100; |
| 125 | +---- |
| 126 | +logical_plan |
| 127 | +01)Projection: test_data.id, test_data.float_val, test_data.int_val, test_data.decimal_val |
| 128 | +02)--Filter: __common_expr_3 >= Float64(100) AND __common_expr_3 < Float64(101) |
| 129 | +03)----Projection: CAST(test_data.int_val AS Float64) AS __common_expr_3, test_data.id, test_data.float_val, test_data.int_val, test_data.decimal_val |
| 130 | +04)------TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 131 | + |
| 132 | +# 3. Basic: Decimal128 - same transformation |
| 133 | +query TT |
| 134 | +EXPLAIN SELECT * FROM test_data WHERE floor(decimal_val) = arrow_cast(100, 'Decimal128(10,2)'); |
| 135 | +---- |
| 136 | +logical_plan |
| 137 | +01)Filter: test_data.decimal_val >= Decimal128(Some(10000),10,2) AND test_data.decimal_val < Decimal128(Some(10100),10,2) |
| 138 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 139 | + |
| 140 | +# 4. Column on RHS - same transformation |
| 141 | +query TT |
| 142 | +EXPLAIN SELECT * FROM test_data WHERE arrow_cast(5, 'Float64') = floor(float_val); |
| 143 | +---- |
| 144 | +logical_plan |
| 145 | +01)Filter: test_data.float_val >= Float64(5) AND test_data.float_val < Float64(6) |
| 146 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 147 | + |
| 148 | +# 5. IS NOT DISTINCT FROM - adds IS NOT NULL |
| 149 | +query TT |
| 150 | +EXPLAIN SELECT * FROM test_data WHERE floor(float_val) IS NOT DISTINCT FROM arrow_cast(5, 'Float64'); |
| 151 | +---- |
| 152 | +logical_plan |
| 153 | +01)Filter: test_data.float_val IS NOT NULL AND test_data.float_val >= Float64(5) AND test_data.float_val < Float64(6) |
| 154 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 155 | + |
| 156 | +# 6. IS DISTINCT FROM - includes NULL check |
| 157 | +query TT |
| 158 | +EXPLAIN SELECT * FROM test_data WHERE floor(float_val) IS DISTINCT FROM arrow_cast(5, 'Float64'); |
| 159 | +---- |
| 160 | +logical_plan |
| 161 | +01)Filter: test_data.float_val < Float64(5) OR test_data.float_val >= Float64(6) OR test_data.float_val IS NULL |
| 162 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 163 | + |
| 164 | +# 7. Non-optimizable: non-integer literal (original predicate preserved) |
| 165 | +query TT |
| 166 | +EXPLAIN SELECT * FROM test_data WHERE floor(float_val) = arrow_cast(5.5, 'Float64'); |
| 167 | +---- |
| 168 | +logical_plan |
| 169 | +01)Filter: floor(test_data.float_val) = Float64(5.5) |
| 170 | +02)--TableScan: test_data projection=[id, float_val, int_val, decimal_val] |
| 171 | + |
| 172 | +########## |
| 173 | +## Cleanup |
| 174 | +########## |
| 175 | + |
| 176 | +statement ok |
| 177 | +DROP TABLE test_data; |
0 commit comments