// Copyright 2023 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include namespace DB::tests { // TODO support unsigned int as the test framework not supports unsigned int so far. class LastValue : public DB::tests::WindowTest { public: const ASTPtr value_col = col(VALUE_COL_NAME); void initializeContext() override { ExecutorTest::initializeContext(); } template void testInt() { MockWindowFrame unbounded_type_frame{ tipb::WindowFrameType::Rows, mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, true, 0), mock::MockWindowFrameBound(tipb::WindowBoundType::Following, true, 0)}; executeFunctionAndAssert( toNullableVec({1, 5, 5, 5, 5, 10, 10, 10, 10, 10, 13, 13, 13}), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13})}, unbounded_type_frame); executeFunctionAndAssert( toNullableVec({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), LastValue(value_col), {toNullableVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toNullableVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toNullableVec(/*value*/ {{}, 2, 3, 4, {}, 6, 7, 8, 9, {}, 11, 12, {}})}, unbounded_type_frame); } template void testFloat() { MockWindowFrame unbounded_type_frame{ tipb::WindowFrameType::Rows, mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, true, 0), mock::MockWindowFrameBound(tipb::WindowBoundType::Following, true, 0)}; executeFunctionAndAssert( toNullableVec({1, 5, 5, 5, 5, 10, 10, 10, 10, 10, 13, 13, 13}), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13})}, unbounded_type_frame); executeFunctionAndAssert( toNullableVec({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), LastValue(value_col), {toNullableVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toNullableVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toNullableVec(/*value*/ {{}, 2, 3, 4, {}, 6, 7, 8, 9, {}, 11, 12, {}})}, unbounded_type_frame); } void testIntAndTimeOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, static_cast(0)); mock_frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, 0); { // Int type const column std::vector frame_end_range{0, 1, 3, 10}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 4, 4, 4, 8, 3, 3, 13, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 8, 8, 8, 8, 10, 13, 15, 15, 15, 9, 9, 15, 15, 20, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec(/*order*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); std::vector cmp_data_type{ tipb::RangeCmpDataType::Int, tipb::RangeCmpDataType::DateTime, tipb::RangeCmpDataType::Duration}; for (auto type : cmp_data_type) { for (size_t i = 0; i < frame_end_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, type, ORDER_COL_NAME, true, frame_end_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } } { // Float type const column std::vector frame_start_range{0, 1.1, 2.9, 9.9}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 4, 4, 8, 0, 3, 10, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 8, 8, 8, 8, 3, 10, 15, 15, 15, 9, 9, 9, 15, 20, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec(/*order*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // Decimal type const column std::vector> frame_start_range{ DecimalField(0, 0), DecimalField(11, 1), DecimalField(29, 1), DecimalField(99, 1)}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 4, 4, 8, 0, 3, 10, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 8, 8, 8, 8, 3, 10, 15, 15, 15, 9, 9, 9, 15, 20, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec(/*order*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Decimal, ORDER_COL_NAME, true, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } } void testFloatOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, static_cast(0)); mock_frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, static_cast(0)); { // Int type const column std::vector frame_start_range{0, 1, 3, 10}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 4, 4, 4, 8, 0, 3, 10, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 8, 8, 8, 8, 10, 13, 15, 15, 15, 9, 9, 15, 15, 20, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec( /*order*/ {0.1, 1.2, 2.1, 4.1, 8.1, 0.0, 3.1, 10.0, 13.1, 15.1, 1.1, 2.9, 5.1, 9.1, 15.0, 20.1, 31.1}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // Float type const column std::vector frame_start_range{0, 1.1, 2.3, 3.8}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 4, 4, 8, 0, 3, 10, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 4, 4, 4, 8, 3, 3, 13, 15, 15, 3, 5, 5, 9, 15, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec( /*order*/ {0.1, 1.2, 2.1, 4.1, 8.1, 0.0, 3.1, 10.0, 13.1, 15.1, 1.1, 2.9, 5.1, 9.1, 15.0, 20.1, 31.1}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // Decimal type const column std::vector> frame_start_range{ DecimalField(0, 0), DecimalField(11, 1), DecimalField(23, 1), DecimalField(38, 1)}; std::vector>> res{ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 2, 4, 4, 8, 0, 3, 10, 15, 15, 3, 5, 5, 9, 15, 20, 31}, {0, 4, 4, 4, 8, 3, 3, 13, 15, 15, 3, 5, 5, 9, 15, 20, 31}}; auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}); auto order_col = toVec( /*order*/ {0.1, 1.2, 2.1, 4.1, 8.1, 0.0, 3.1, 10.0, 13.1, 15.1, 1.1, 2.9, 5.1, 9.1, 15.0, 20.1, 31.1}); auto val_col = toVec(/*value*/ {0, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}); for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } } void testNullableOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Int, ORDER_COL_NAME, false, static_cast(0)); ; mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Int, ORDER_COL_NAME, true, static_cast(0)); auto partition_col = toVec(/*partition*/ {0, 1, 1, 1, 2, 2, 2, 2, 2}); auto order_col = toNullableVec(/*order*/ {0, {}, 1, 2, {}, 5, 6, 9, 10}); auto val_col = toVec(/*value*/ {1, 2, 3, 4, 5, 6, 7, 8, 9}); { std::vector frame_end_range{0, 1, 3, 10}; std::vector>> res{ {1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 2, 4, 4, 5, 7, 7, 9, 9}, {1, 2, 4, 4, 5, 7, 8, 9, 9}, {1, 2, 4, 4, 5, 9, 9, 9, 9}}; for (size_t i = 0; i < frame_end_range.size(); ++i) { mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Int, ORDER_COL_NAME, true, frame_end_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, static_cast(1)); mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, static_cast(1)); executeFunctionAndAssert( toNullableVec({{}, 2, {}, 3, 5, {}, 6, {}, 8}), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } { // mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, static_cast(1)); mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, static_cast(1)); executeFunctionAndAssert( toNullableVec({{}, 2, 4, {}, 5, 7, {}, 9, {}}), LastValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } void testDecimalOrderByColForRangeFrame() { // TODO we can not assign decimal field's flen now // in MockStorage.cpp::mockColumnInfosToTiDBColumnInfos(). // However, we will test this data type in fullstack tests. } }; TEST_F(LastValue, lastValue) try { { // frame type: unbounded executeFunctionAndAssert( toNullableVec({"1", "5", "5", "5", "5", "10", "10", "10", "10", "10", "13", "13", "13"}), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"})}); executeFunctionAndAssert( toNullableVec({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), LastValue(value_col), {toNullableVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toNullableVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toNullableVec(/*value*/ {{}, "2", "3", "4", {}, "6", "7", "8", "9", {}, "11", "12", {}})}); } { // frame type: offset MockWindowFrame frame; frame.type = tipb::WindowFrameType::Rows; frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, 0); std::vector frame_start_offset{0, 1, 3, 10}; std::vector>> res_not_null{ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}, {"1", "3", "4", "5", "5", "7", "8", "9", "10", "10", "12", "13", "13"}, {"1", "5", "5", "5", "5", "9", "10", "10", "10", "10", "13", "13", "13"}, {"1", "5", "5", "5", "5", "10", "10", "10", "10", "10", "13", "13", "13"}, }; std::vector>> res_null{ {{}, "2", "3", "4", {}, "6", "7", "8", "9", {}, "11", "12", {}}, {{}, "3", "4", {}, {}, "7", "8", "9", {}, {}, "12", {}, {}}, {{}, {}, {}, {}, {}, "9", {}, {}, {}, {}, {}, {}, {}}, {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}, }; for (size_t i = 0; i < frame_start_offset.size(); ++i) { frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, frame_start_offset[i]); executeFunctionAndAssert( toNullableVec(res_not_null[i]), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"})}, frame); executeFunctionAndAssert( toNullableVec(res_null[i]), LastValue(value_col), {toNullableVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toNullableVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toNullableVec(/*value*/ {{}, "2", "3", "4", {}, "6", "7", "8", "9", {}, "11", "12", {}})}, frame); } // The following are tests frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 2); frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 1); executeFunctionAndAssert( toNullableVec({{}, {}, "2", "3", "4", {}, "6", "7", "8", "9", {}, "11", "12"}), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"})}, frame); // The following are tests frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, 1); frame.end = mock::MockWindowFrameBound(tipb::WindowBoundType::Following, false, 2); executeFunctionAndAssert( toNullableVec({{}, "4", "5", "5", {}, "8", "9", "10", "10", {}, "13", "13", {}}), LastValue(value_col), {toVec(/*partition*/ {0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3}), toVec(/*order*/ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), toVec(/*value*/ {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"})}, frame); } testInt(); testInt(); testInt(); testInt(); testFloat(); testFloat(); } CATCH // This is the test just for testing range type frame. // Not every window function needs this test. TEST_F(LastValue, lastValueWithRangeFrameType) try { testIntAndTimeOrderByColForRangeFrame(); testFloatOrderByColForRangeFrame(); testNullableOrderByColForRangeFrame(); // TODO Implement testDecimalOrderByColForRangeFrame() } CATCH } // namespace DB::tests