// 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 #include #include #include #include namespace DB::tests { // TODO support unsigned int as the test framework not supports unsigned int so far. class FirstValue : public DB::tests::WindowTest { public: const ASTPtr value_col = col(VALUE_COL_NAME); void initializeContext() override { ExecutorTest::initializeContext(); } template void testInt() { executeFunctionAndAssert( toNullableVec({1, 2, 2, 2, 2, 6, 6, 6, 6, 6, 11, 11, 11}), FirstValue(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({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), FirstValue(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*/ {{}, {}, 3, 4, 5, {}, 7, 8, 9, 10, {}, 12, 13})}); } template void testFloat() { executeFunctionAndAssert( toNullableVec({1, 2, 2, 2, 2, 6, 6, 6, 6, 6, 11, 11, 11}), FirstValue(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({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), FirstValue(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*/ {{}, {}, 3, 4, 5, {}, 7, 8, 9, 10, {}, 12, 13})}); } void testIntAndTimeOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 0); mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Int, ORDER_COL_NAME, true, 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, 1, 1, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 1, 1, 1, 8, 0, 0, 10, 10, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 1, 0, 0, 0, 3, 10, 1, 1, 1, 1, 5, 15, 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_start_range.size(); ++i) { mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, type, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(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, 1, 1, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 1, 0, 0, 3, 10, 10, 1, 1, 1, 1, 9, 15, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(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, 1, 1, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 1, 0, 0, 3, 10, 10, 1, 1, 1, 1, 9, 15, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Decimal, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } } void testFloatOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 0); mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, true, 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, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 1, 0, 0, 0, 3, 10, 1, 1, 1, 1, 5, 15, 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.0, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // Float type const column std::vector frame_start_range{0, 1.8, 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, 1, 1, 4, 8, 0, 3, 10, 13, 15, 1, 1, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 8, 0, 0, 10, 10, 13, 1, 1, 3, 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.0, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } { // Decimal type const column std::vector> frame_start_range{ DecimalField(0, 0), DecimalField(18, 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, 1, 1, 4, 8, 0, 3, 10, 13, 15, 1, 1, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 8, 0, 0, 10, 10, 13, 1, 1, 3, 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.0, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Float, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } } void testDecimalOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 0); mock_frame.end = buildRangeFrameBound( tipb::WindowBoundType::Following, tipb::RangeCmpDataType::Decimal, ORDER_COL_NAME, true, 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, 1, 2, 4, 8, 0, 3, 10, 13, 15, 1, 3, 5, 9, 15, 20, 31}, {0, 1, 1, 2, 8, 0, 3, 10, 13, 13, 1, 1, 3, 9, 15, 20, 31}, {0, 1, 1, 1, 1, 0, 0, 0, 3, 10, 1, 1, 1, 1, 5, 15, 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 = createColumn(/*order*/ std::make_tuple(3, 1), { DecimalField(1, 1), DecimalField(10, 1), DecimalField(21, 1), DecimalField(41, 1), DecimalField(81, 1), DecimalField(0, 1), DecimalField(31, 1), DecimalField(100, 1), DecimalField(131, 1), DecimalField(151, 1), DecimalField(11, 1), DecimalField(29, 1), DecimalField(51, 1), DecimalField(91, 1), DecimalField(150, 1), DecimalField(201, 1), DecimalField(311, 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.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Decimal, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } // TODO Support Float type const column // TODO Support Decimal type const column } void testNullableOrderByColForRangeFrame() { MockWindowFrame mock_frame; mock_frame.type = tipb::WindowFrameType::Ranges; mock_frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, 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_start_range{0, 1, 3, 10}; std::vector>> res{ {1, 2, 3, 4, 5, 6, 7, 8, 9}, {1, 2, 3, 3, 5, 6, 6, 8, 8}, {1, 2, 3, 3, 5, 6, 6, 7, 8}, {1, 2, 3, 3, 5, 6, 6, 6, 6}}; for (size_t i = 0; i < frame_start_range.size(); ++i) { mock_frame.start = buildRangeFrameBound( tipb::WindowBoundType::Preceding, tipb::RangeCmpDataType::Int, ORDER_COL_NAME, false, frame_start_range[i]); executeFunctionAndAssert( toNullableVec(res[i]), FirstValue(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}), FirstValue(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, {}}), FirstValue(value_col), {partition_col, order_col, val_col}, mock_frame); } } }; TEST_F(FirstValue, firstValueWithRowsFrameType) try { { // boundary type: unbounded executeFunctionAndAssert( toNullableVec({"1", "2", "2", "2", "2", "6", "6", "6", "6", "6", "11", "11", "11"}), FirstValue(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({{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}), FirstValue(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*/ {{}, {}, "3", "4", "5", {}, "7", "8", "9", "10", {}, "12", "13"})}); } { // boundary type: offset MockWindowFrame frame; frame.type = tipb::WindowFrameType::Rows; frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, 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", "2", "2", "3", "4", "6", "6", "7", "8", "9", "11", "11", "12"}, {"1", "2", "2", "2", "2", "6", "6", "6", "6", "7", "11", "11", "11"}, {"1", "2", "2", "2", "2", "6", "6", "6", "6", "6", "11", "11", "11"}}; std::vector>> res_null{ {{}, {}, "3", "4", "5", {}, "7", "8", "9", "10", {}, "12", "13"}, {{}, {}, {}, "3", "4", {}, {}, "7", "8", "9", {}, {}, "12"}, {{}, {}, {}, {}, {}, {}, {}, {}, {}, "7", {}, {}, {}}, {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}}; for (size_t i = 0; i < frame_start_offset.size(); ++i) { frame.start = mock::MockWindowFrameBound(tipb::WindowBoundType::Preceding, false, frame_start_offset[i]); executeFunctionAndAssert( toNullableVec(res_not_null[i]), FirstValue(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]), FirstValue(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*/ {{}, {}, "3", "4", "5", {}, "7", "8", "9", "10", {}, "12", "13"})}, 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", "2", "3", {}, "6", "6", "7", "8", {}, "11", "11"}), FirstValue(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({{}, "3", "4", "5", {}, "7", "8", "9", "10", {}, "12", "13", {}}), FirstValue(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(FirstValue, firstValueWithRangeFrameType) try { testIntAndTimeOrderByColForRangeFrame(); testFloatOrderByColForRangeFrame(); testNullableOrderByColForRangeFrame(); // TODO This test is disabled as we can not assign decimal field's flen now // in MockStorage.cpp::mockColumnInfosToTiDBColumnInfos(). // However, we will test this data type in fullstack tests. // testDecimalOrderByColForRangeFrame(); } CATCH } // namespace DB::tests