From 329b6872c5b261073c8229c9c0f8eb7ca0c69e3a Mon Sep 17 00:00:00 2001 From: kould Date: Tue, 27 Jan 2026 19:06:40 +0800 Subject: [PATCH 1/2] chore: add MinMaxToTopK and EliminateRedundantSort support TopK --- src/db.rs | 25 +- .../rule/normalization/agg_elimination.rs | 43 +++- .../rule/normalization/min_max_top_k.rs | 237 ++++++++++++++++++ src/optimizer/rule/normalization/mod.rs | 5 + 4 files changed, 300 insertions(+), 10 deletions(-) create mode 100644 src/optimizer/rule/normalization/min_max_top_k.rs diff --git a/src/db.rs b/src/db.rs index a1cfb9e7..d6992386 100644 --- a/src/db.rs +++ b/src/db.rs @@ -188,6 +188,14 @@ fn default_optimizer_pipeline() -> HepOptimizerPipeline { NormalizationRuleImpl::PushLimitIntoTableScan, ], ) + .before_batch( + "TopK".to_string(), + HepBatchStrategy::once_topdown(), + vec![ + NormalizationRuleImpl::MinMaxToTopK, + NormalizationRuleImpl::TopK, + ], + ) .before_batch( "Combine Operators".to_string(), HepBatchStrategy::fix_point_topdown(10), @@ -197,11 +205,6 @@ fn default_optimizer_pipeline() -> HepOptimizerPipeline { NormalizationRuleImpl::CombineFilter, ], ) - .before_batch( - "TopK".to_string(), - HepBatchStrategy::once_topdown(), - vec![NormalizationRuleImpl::TopK], - ) .after_batch( "Eliminate Aggregate".to_string(), HepBatchStrategy::once_topdown(), @@ -379,8 +382,16 @@ impl Database { }; let transaction = Box::into_raw(Box::new(self.storage.transaction()?)); let (schema, executor) = - self.state - .execute(unsafe { &mut (*transaction) }, statement, params)?; + match self + .state + .execute(unsafe { &mut (*transaction) }, statement, params) + { + Ok(result) => result, + Err(err) => { + unsafe { drop(Box::from_raw(transaction)) }; + return Err(err); + } + }; let inner = Box::into_raw(Box::new(TransactionIter::new(schema, executor))); Ok(DatabaseIter { transaction, inner }) } diff --git a/src/optimizer/rule/normalization/agg_elimination.rs b/src/optimizer/rule/normalization/agg_elimination.rs index c122e375..6197d60f 100644 --- a/src/optimizer/rule/normalization/agg_elimination.rs +++ b/src/optimizer/rule/normalization/agg_elimination.rs @@ -18,13 +18,14 @@ use crate::expression::ScalarExpression; use crate::optimizer::core::pattern::{Pattern, PatternChildrenPredicate}; use crate::optimizer::core::rule::{MatchPattern, NormalizationRule}; use crate::optimizer::plan_utils::{only_child_mut, replace_with_only_child}; +use crate::planner::operator::limit::LimitOperator; use crate::planner::operator::sort::SortField; use crate::planner::operator::{Operator, PhysicalOption, PlanImpl, SortOption}; use crate::planner::{Childrens, LogicalPlan}; use std::sync::LazyLock; static REDUNDANT_SORT_PATTERN: LazyLock = LazyLock::new(|| Pattern { - predicate: |op| matches!(op, Operator::Sort(_)), + predicate: |op| matches!(op, Operator::Sort(_) | Operator::TopK(_)), children: PatternChildrenPredicate::None, }); @@ -38,8 +39,12 @@ impl MatchPattern for EliminateRedundantSort { impl NormalizationRule for EliminateRedundantSort { fn apply(&self, plan: &mut LogicalPlan) -> Result { - let sort_fields = match &plan.operator { - Operator::Sort(sort_op) => sort_op.sort_fields.clone(), + let (sort_fields, topk_limit) = match &plan.operator { + Operator::Sort(sort_op) => (sort_op.sort_fields.clone(), None), + Operator::TopK(topk_op) => ( + topk_op.sort_fields.clone(), + Some((topk_op.limit, topk_op.offset)), + ), _ => return Ok(false), }; @@ -54,6 +59,15 @@ impl NormalizationRule for EliminateRedundantSort { return Ok(false); } + if let Some((limit, offset)) = topk_limit { + plan.operator = Operator::Limit(LimitOperator { + offset, + limit: Some(limit), + }); + plan.physical_option = Some(PhysicalOption::new(PlanImpl::Limit, SortOption::Follow)); + return Ok(true); + } + Ok(replace_with_only_child(plan)) } } @@ -322,6 +336,7 @@ mod tests { use crate::planner::operator::filter::FilterOperator; use crate::planner::operator::sort::{SortField, SortOperator}; use crate::planner::operator::table_scan::TableScanOperator; + use crate::planner::operator::top_k::TopKOperator; use crate::planner::operator::{Operator, PhysicalOption, PlanImpl, SortOption}; use crate::planner::{Childrens, LogicalPlan}; use crate::types::index::{IndexInfo, IndexMeta, IndexType}; @@ -471,6 +486,28 @@ mod tests { Ok(()) } + #[test] + fn remove_topk_when_index_matches_order() -> Result<(), DatabaseError> { + let sort_field = make_sort_field("c1"); + let mut plan = build_plan(vec![sort_field.clone()], vec![sort_field.clone()], 0); + plan.operator = Operator::TopK(TopKOperator { + sort_fields: vec![sort_field], + limit: 10, + offset: Some(5), + }); + let rule = EliminateRedundantSort; + + assert!(rule.apply(&mut plan)?); + match plan.operator { + Operator::Limit(limit_op) => { + assert_eq!(limit_op.limit, Some(10)); + assert_eq!(limit_op.offset, Some(5)); + } + _ => unreachable!("expected limit operator after removing topk"), + } + Ok(()) + } + #[test] fn remove_sort_when_prefix_can_be_ignored() -> Result<(), DatabaseError> { let c1 = make_sort_field("c1"); diff --git a/src/optimizer/rule/normalization/min_max_top_k.rs b/src/optimizer/rule/normalization/min_max_top_k.rs new file mode 100644 index 00000000..38b0ea14 --- /dev/null +++ b/src/optimizer/rule/normalization/min_max_top_k.rs @@ -0,0 +1,237 @@ +// Copyright 2024 KipData/KiteSQL +// +// 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. + +use crate::errors::DatabaseError; +use crate::expression::agg::AggKind; +use crate::expression::ScalarExpression; +use crate::optimizer::core::pattern::{Pattern, PatternChildrenPredicate}; +use crate::optimizer::core::rule::{MatchPattern, NormalizationRule}; +use crate::optimizer::plan_utils::{only_child, wrap_child_with}; +use crate::planner::operator::filter::FilterOperator; +use crate::planner::operator::sort::SortField; +use crate::planner::operator::top_k::TopKOperator; +use crate::planner::operator::Operator; +use crate::planner::LogicalPlan; +use std::sync::LazyLock; + +static MIN_MAX_TOPK_PATTERN: LazyLock = LazyLock::new(|| Pattern { + predicate: |op| matches!(op, Operator::Aggregate(_)), + children: PatternChildrenPredicate::None, +}); + +pub struct MinMaxToTopK; + +impl MatchPattern for MinMaxToTopK { + fn pattern(&self) -> &Pattern { + &MIN_MAX_TOPK_PATTERN + } +} + +impl NormalizationRule for MinMaxToTopK { + fn apply(&self, plan: &mut LogicalPlan) -> Result { + let Operator::Aggregate(op) = &plan.operator else { + return Ok(false); + }; + if !op.groupby_exprs.is_empty() || op.agg_calls.len() != 1 { + return Ok(false); + } + + let ScalarExpression::AggCall { kind, args, .. } = &op.agg_calls[0] else { + return Ok(false); + }; + if args.len() != 1 { + return Ok(false); + } + + let asc = match kind { + AggKind::Min => true, + AggKind::Max => false, + _ => return Ok(false), + }; + + let sort_field = SortField::new(args[0].clone(), asc, true); + let already_topk = match only_child(plan) { + Some(child) => match &child.operator { + Operator::TopK(topk) => { + topk.limit == 1 + && topk.offset.is_none() + && topk.sort_fields == vec![sort_field.clone()] + } + _ => false, + }, + None => return Ok(false), + }; + + if already_topk { + return Ok(false); + } + + // IndexScan prioritizes indexed columns as null first. + // Therefore, to ensure Top K is eliminated when an index exists, + // we set it to null first and filter null rows. + let predicate = ScalarExpression::IsNull { + negated: true, + expr: Box::new(args[0].clone()), + }; + let filter = Operator::Filter(FilterOperator { + predicate, + is_optimized: false, + having: false, + }); + + if !wrap_child_with(plan, 0, filter) { + return Ok(false); + } + + // Agg do not remove, because when the table is empty, MIN/MAX should return a NULL row. + Ok(wrap_child_with( + plan, + 0, + Operator::TopK(TopKOperator { + sort_fields: vec![sort_field], + limit: 1, + offset: None, + }), + )) + } +} + +#[cfg(all(test, not(target_arch = "wasm32")))] +mod tests { + use super::MinMaxToTopK; + use crate::binder::test::build_t1_table; + use crate::errors::DatabaseError; + use crate::optimizer::core::rule::NormalizationRule; + use crate::planner::operator::Operator; + use crate::planner::Childrens; + + fn find_aggregate<'a>( + plan: &'a crate::planner::LogicalPlan, + ) -> &'a crate::planner::LogicalPlan { + if matches!(plan.operator, Operator::Aggregate(_)) { + return plan; + } + match plan.childrens.as_ref() { + Childrens::Only(child) => find_aggregate(child), + _ => panic!("Aggregate operator not found"), + } + } + + fn find_aggregate_mut<'a>( + plan: &'a mut crate::planner::LogicalPlan, + ) -> &'a mut crate::planner::LogicalPlan { + if matches!(plan.operator, Operator::Aggregate(_)) { + return plan; + } + match plan.childrens.as_mut() { + Childrens::Only(child) => find_aggregate_mut(child), + _ => panic!("Aggregate operator not found"), + } + } + + #[test] + fn test_min_to_topk() -> Result<(), DatabaseError> { + let table_state = build_t1_table()?; + let mut plan = table_state.plan("select min(c1) from t1")?; + + let agg_plan = find_aggregate_mut(&mut plan); + assert!(MinMaxToTopK.apply(agg_plan)?); + + let agg_plan = find_aggregate(&plan); + let Operator::Aggregate(op) = &agg_plan.operator else { + unreachable!("Aggregate operator not found"); + }; + let child = match agg_plan.childrens.as_ref() { + Childrens::Only(child) => child.as_ref(), + _ => unreachable!("Aggregate should have one child"), + }; + let topk_plan = match &child.operator { + Operator::TopK(_) => child, + _ => unreachable!("Expected TopK under Aggregate"), + }; + let Operator::TopK(topk) = &topk_plan.operator else { + unreachable!("Expected TopK under Aggregate"); + }; + assert_eq!(topk.limit, 1); + assert!(topk.offset.is_none()); + assert_eq!(topk.sort_fields.len(), 1); + assert!(topk.sort_fields[0].asc); + assert!(topk.sort_fields[0].nulls_first); + let args = match &op.agg_calls[0] { + crate::expression::ScalarExpression::AggCall { args, .. } => args, + _ => unreachable!("Aggregate should use AggCall"), + }; + assert_eq!(topk.sort_fields[0].expr, args[0]); + + let filter_plan = match topk_plan.childrens.as_ref() { + Childrens::Only(child) => child.as_ref(), + _ => unreachable!("TopK should have one child"), + }; + match &filter_plan.operator { + Operator::Filter(filter_op) => match &filter_op.predicate { + crate::expression::ScalarExpression::IsNull { negated, expr } => { + assert!(*negated); + assert_eq!(**expr, args[0]); + } + _ => unreachable!("Expected IS NOT NULL filter under TopK"), + }, + _ => unreachable!("Expected Filter under TopK"), + } + + Ok(()) + } + + #[test] + fn test_max_to_topk() -> Result<(), DatabaseError> { + let table_state = build_t1_table()?; + let mut plan = table_state.plan("select max(c2) from t1")?; + + let agg_plan = find_aggregate_mut(&mut plan); + assert!(MinMaxToTopK.apply(agg_plan)?); + + let agg_plan = find_aggregate(&plan); + let child = match agg_plan.childrens.as_ref() { + Childrens::Only(child) => child.as_ref(), + _ => unreachable!("Aggregate should have one child"), + }; + let topk_plan = match &child.operator { + Operator::TopK(_) => child, + _ => unreachable!("Expected TopK under Aggregate"), + }; + let Operator::TopK(topk) = &topk_plan.operator else { + unreachable!("Expected TopK under Aggregate"); + }; + assert_eq!(topk.limit, 1); + assert!(topk.offset.is_none()); + assert_eq!(topk.sort_fields.len(), 1); + assert!(!topk.sort_fields[0].asc); + assert!(topk.sort_fields[0].nulls_first); + + let filter_plan = match topk_plan.childrens.as_ref() { + Childrens::Only(child) => child.as_ref(), + _ => unreachable!("TopK should have one child"), + }; + match &filter_plan.operator { + Operator::Filter(filter_op) => match &filter_op.predicate { + crate::expression::ScalarExpression::IsNull { negated, .. } => { + assert!(*negated); + } + _ => unreachable!("Expected IS NOT NULL filter under TopK"), + }, + _ => unreachable!("Expected Filter under TopK"), + } + + Ok(()) + } +} diff --git a/src/optimizer/rule/normalization/mod.rs b/src/optimizer/rule/normalization/mod.rs index 348cc4d8..f5276cbb 100644 --- a/src/optimizer/rule/normalization/mod.rs +++ b/src/optimizer/rule/normalization/mod.rs @@ -27,6 +27,7 @@ use crate::optimizer::rule::normalization::compilation_in_advance::{ use crate::optimizer::rule::normalization::agg_elimination::{ EliminateRedundantSort, UseStreamDistinct, }; +use crate::optimizer::rule::normalization::min_max_top_k::MinMaxToTopK; use crate::optimizer::rule::normalization::pushdown_limit::{ LimitProjectTranspose, PushLimitIntoScan, PushLimitThroughJoin, }; @@ -41,6 +42,7 @@ mod agg_elimination; mod column_pruning; mod combine_operators; mod compilation_in_advance; +mod min_max_top_k; mod pushdown_limit; mod pushdown_predicates; mod simplification; @@ -69,6 +71,7 @@ pub enum NormalizationRuleImpl { // CompilationInAdvance BindExpressionPosition, EvaluatorBind, + MinMaxToTopK, TopK, EliminateRedundantSort, UseStreamDistinct, @@ -91,6 +94,7 @@ impl MatchPattern for NormalizationRuleImpl { NormalizationRuleImpl::ConstantCalculation => ConstantCalculation.pattern(), NormalizationRuleImpl::BindExpressionPosition => BindExpressionPosition.pattern(), NormalizationRuleImpl::EvaluatorBind => EvaluatorBind.pattern(), + NormalizationRuleImpl::MinMaxToTopK => MinMaxToTopK.pattern(), NormalizationRuleImpl::TopK => TopK.pattern(), NormalizationRuleImpl::EliminateRedundantSort => EliminateRedundantSort.pattern(), NormalizationRuleImpl::UseStreamDistinct => UseStreamDistinct.pattern(), @@ -117,6 +121,7 @@ impl NormalizationRule for NormalizationRuleImpl { NormalizationRuleImpl::ConstantCalculation => ConstantCalculation.apply(plan), NormalizationRuleImpl::BindExpressionPosition => BindExpressionPosition.apply(plan), NormalizationRuleImpl::EvaluatorBind => EvaluatorBind.apply(plan), + NormalizationRuleImpl::MinMaxToTopK => MinMaxToTopK.apply(plan), NormalizationRuleImpl::TopK => TopK.apply(plan), NormalizationRuleImpl::EliminateRedundantSort => EliminateRedundantSort.apply(plan), NormalizationRuleImpl::UseStreamDistinct => UseStreamDistinct.apply(plan), From 8f0634852317ad9ff66359f2945a83521f8dd120 Mon Sep 17 00:00:00 2001 From: kould Date: Tue, 27 Jan 2026 19:33:41 +0800 Subject: [PATCH 2/2] perf: Adjust TupleKey encoding to default to NULLS LAST, allowing Min/Max to be rewritten as TopK without filtering NULLs --- README.md | 12 +- src/binder/aggregate.rs | 2 +- src/optimizer/core/memo.rs | 2 +- .../rule/normalization/agg_elimination.rs | 10 +- .../rule/normalization/min_max_top_k.rs | 53 +--- src/planner/operator/table_scan.rs | 2 +- src/storage/table_codec.rs | 5 +- src/types/value.rs | 30 +-- tests/slt/crdb/join.slt | 74 +++--- tests/slt/crdb/order_by.slt | 22 +- tests/slt/explain.slt.reference | 236 ------------------ tests/slt/order_by.slt | 12 +- tpcc/README.md | 93 ++++--- 13 files changed, 135 insertions(+), 418 deletions(-) delete mode 100644 tests/slt/explain.slt.reference diff --git a/README.md b/README.md index 692b37f4..18a953fc 100755 --- a/README.md +++ b/README.md @@ -93,13 +93,13 @@ Run `make tpcc-dual` to mirror every TPCC statement to an in-memory SQLite datab All cases have been fully optimized. ```shell <90th Percentile RT (MaxRT)> - New-Order : 0.002 (0.006) - Payment : 0.001 (0.019) -Order-Status : 0.001 (0.003) - Delivery : 0.022 (0.038) - Stock-Level : 0.002 (0.005) + New-Order : 0.002 (0.005) + Payment : 0.001 (0.013) +Order-Status : 0.002 (0.006) + Delivery : 0.010 (0.023) + Stock-Level : 0.002 (0.017) -18432 Tpmc +27226 Tpmc ``` #### 👉[check more](tpcc/README.md) diff --git a/src/binder/aggregate.rs b/src/binder/aggregate.rs index 7c74afb2..d6780d23 100644 --- a/src/binder/aggregate.rs +++ b/src/binder/aggregate.rs @@ -98,7 +98,7 @@ impl> Binder<'_, '_, T, A> return_orderby.push(SortField::new( expr, asc.is_none_or(|asc| asc), - nulls_first.unwrap_or(true), + nulls_first.unwrap_or(false), )); } Some(return_orderby) diff --git a/src/optimizer/core/memo.rs b/src/optimizer/core/memo.rs index dd99392f..914475b5 100644 --- a/src/optimizer/core/memo.rs +++ b/src/optimizer/core/memo.rs @@ -191,7 +191,7 @@ mod tests { let sort_fields = vec![SortField::new( ScalarExpression::column_expr(c1_column.clone()), true, - true, + false, )]; let scala_functions = Default::default(); let table_functions = Default::default(); diff --git a/src/optimizer/rule/normalization/agg_elimination.rs b/src/optimizer/rule/normalization/agg_elimination.rs index 6197d60f..fffc69f2 100644 --- a/src/optimizer/rule/normalization/agg_elimination.rs +++ b/src/optimizer/rule/normalization/agg_elimination.rs @@ -179,7 +179,7 @@ fn distinct_sort_fields(groupby_exprs: &[ScalarExpression]) -> Vec { groupby_exprs .iter() .cloned() - .map(|expr| SortField::new(expr, true, true)) + .map(|expr| SortField::new(expr, true, false)) .collect() } @@ -349,7 +349,7 @@ mod tests { fn make_sort_field(name: &str) -> SortField { let column = ColumnRef::from(ColumnCatalog::new_dummy(name.to_string())); - SortField::new(ScalarExpression::column_expr(column), true, true) + SortField::new(ScalarExpression::column_expr(column), true, false) } fn build_plan( @@ -427,7 +427,7 @@ mod tests { let sort_fields = vec![SortField::new( ScalarExpression::column_expr(c1.clone()), true, - true, + false, )]; let sort_option = SortOption::OrderBy { fields: sort_fields.clone(), @@ -523,7 +523,7 @@ mod tests { #[test] fn annotate_sets_sort_hint_on_table_scan() -> Result<(), DatabaseError> { let column = ColumnRef::from(ColumnCatalog::new_dummy("c1".to_string())); - let sort_field = SortField::new(ScalarExpression::column_expr(column.clone()), true, true); + let sort_field = SortField::new(ScalarExpression::column_expr(column.clone()), true, false); let (index_info, _) = build_index_info(vec![sort_field.clone()], 0); let mut columns = BTreeMap::new(); @@ -625,7 +625,7 @@ mod tests { #[test] fn promote_index_to_remove_sort() -> Result<(), DatabaseError> { let column = ColumnRef::from(ColumnCatalog::new_dummy("c_first".to_string())); - let sort_field = SortField::new(ScalarExpression::column_expr(column.clone()), true, true); + let sort_field = SortField::new(ScalarExpression::column_expr(column.clone()), true, false); let (mut index_info, _) = build_index_info(vec![sort_field.clone()], 0); index_info.range = Some(Range::Scope { min: Bound::Unbounded, diff --git a/src/optimizer/rule/normalization/min_max_top_k.rs b/src/optimizer/rule/normalization/min_max_top_k.rs index 38b0ea14..e678c43d 100644 --- a/src/optimizer/rule/normalization/min_max_top_k.rs +++ b/src/optimizer/rule/normalization/min_max_top_k.rs @@ -18,7 +18,6 @@ use crate::expression::ScalarExpression; use crate::optimizer::core::pattern::{Pattern, PatternChildrenPredicate}; use crate::optimizer::core::rule::{MatchPattern, NormalizationRule}; use crate::optimizer::plan_utils::{only_child, wrap_child_with}; -use crate::planner::operator::filter::FilterOperator; use crate::planner::operator::sort::SortField; use crate::planner::operator::top_k::TopKOperator; use crate::planner::operator::Operator; @@ -60,7 +59,7 @@ impl NormalizationRule for MinMaxToTopK { _ => return Ok(false), }; - let sort_field = SortField::new(args[0].clone(), asc, true); + let sort_field = SortField::new(args[0].clone(), asc, false); let already_topk = match only_child(plan) { Some(child) => match &child.operator { Operator::TopK(topk) => { @@ -77,23 +76,6 @@ impl NormalizationRule for MinMaxToTopK { return Ok(false); } - // IndexScan prioritizes indexed columns as null first. - // Therefore, to ensure Top K is eliminated when an index exists, - // we set it to null first and filter null rows. - let predicate = ScalarExpression::IsNull { - negated: true, - expr: Box::new(args[0].clone()), - }; - let filter = Operator::Filter(FilterOperator { - predicate, - is_optimized: false, - having: false, - }); - - if !wrap_child_with(plan, 0, filter) { - return Ok(false); - } - // Agg do not remove, because when the table is empty, MIN/MAX should return a NULL row. Ok(wrap_child_with( plan, @@ -167,28 +149,13 @@ mod tests { assert!(topk.offset.is_none()); assert_eq!(topk.sort_fields.len(), 1); assert!(topk.sort_fields[0].asc); - assert!(topk.sort_fields[0].nulls_first); + assert!(!topk.sort_fields[0].nulls_first); let args = match &op.agg_calls[0] { crate::expression::ScalarExpression::AggCall { args, .. } => args, _ => unreachable!("Aggregate should use AggCall"), }; assert_eq!(topk.sort_fields[0].expr, args[0]); - let filter_plan = match topk_plan.childrens.as_ref() { - Childrens::Only(child) => child.as_ref(), - _ => unreachable!("TopK should have one child"), - }; - match &filter_plan.operator { - Operator::Filter(filter_op) => match &filter_op.predicate { - crate::expression::ScalarExpression::IsNull { negated, expr } => { - assert!(*negated); - assert_eq!(**expr, args[0]); - } - _ => unreachable!("Expected IS NOT NULL filter under TopK"), - }, - _ => unreachable!("Expected Filter under TopK"), - } - Ok(()) } @@ -216,21 +183,7 @@ mod tests { assert!(topk.offset.is_none()); assert_eq!(topk.sort_fields.len(), 1); assert!(!topk.sort_fields[0].asc); - assert!(topk.sort_fields[0].nulls_first); - - let filter_plan = match topk_plan.childrens.as_ref() { - Childrens::Only(child) => child.as_ref(), - _ => unreachable!("TopK should have one child"), - }; - match &filter_plan.operator { - Operator::Filter(filter_op) => match &filter_op.predicate { - crate::expression::ScalarExpression::IsNull { negated, .. } => { - assert!(*negated); - } - _ => unreachable!("Expected IS NOT NULL filter under TopK"), - }, - _ => unreachable!("Expected Filter under TopK"), - } + assert!(!topk.sort_fields[0].nulls_first); Ok(()) } diff --git a/src/planner/operator/table_scan.rs b/src/planner/operator/table_scan.rs index 2918fc02..72d9fa1f 100644 --- a/src/planner/operator/table_scan.rs +++ b/src/planner/operator/table_scan.rs @@ -70,7 +70,7 @@ impl TableScanOperator { sort_fields.push(SortField { expr: ScalarExpression::column_expr(column.clone()), asc: true, - nulls_first: true, + nulls_first: false, }) } diff --git a/src/storage/table_codec.rs b/src/storage/table_codec.rs index 1ac359f6..193f33b3 100644 --- a/src/storage/table_codec.rs +++ b/src/storage/table_codec.rs @@ -30,8 +30,9 @@ use std::sync::LazyLock; pub(crate) const BOUND_MIN_TAG: u8 = u8::MIN; pub(crate) const BOUND_MAX_TAG: u8 = u8::MAX; -pub(crate) const NULL_TAG: u8 = 0u8; -pub(crate) const NOTNULL_TAG: u8 = 1u8; +// Nulls Last default +pub(crate) const NULL_TAG: u8 = 1u8; +pub(crate) const NOTNULL_TAG: u8 = 0u8; const TABLE_NAME_HASH_LEN: usize = 8; const KEY_TYPE_TAG_LEN: usize = 1; const KEY_BOUND_LEN: usize = 1; diff --git a/src/types/value.rs b/src/types/value.rs index 0753ecea..d7b060ca 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -756,9 +756,9 @@ impl DataValue { nulls_first: bool, ) -> Result<(), DatabaseError> { let (null_tag, not_null_tag) = if nulls_first { - (NULL_TAG, NOTNULL_TAG) - } else { (NOTNULL_TAG, NULL_TAG) + } else { + (NULL_TAG, NOTNULL_TAG) }; if let DataValue::Null = self { b.push(null_tag); @@ -822,7 +822,7 @@ impl DataValue { #[inline] pub fn memcomparable_encode(&self, b: &mut BumpBytes) -> Result<(), DatabaseError> { - self.memcomparable_encode_with_null_order(b, true) + self.memcomparable_encode_with_null_order(b, false) } pub fn memcomparable_decode( @@ -839,7 +839,7 @@ impl DataValue { // for index cover mapping reduce one layer of conversion tuple_mapping: Option>, ) -> Result { - if reader.read_u8()? == 0u8 { + if reader.read_u8()? == NULL_TAG { return Ok(DataValue::Null); } match ty { @@ -2273,9 +2273,9 @@ mod test { println!("{key_i8_0:?} < {key_i8_1:?}"); println!("{key_i8_1:?} < {key_i8_2:?}"); println!("{key_i8_2:?} < {key_i8_3:?}"); - assert!(key_i8_0 < key_i8_1); assert!(key_i8_1 < key_i8_2); assert!(key_i8_2 < key_i8_3); + assert!(key_i8_3 < key_i8_0); assert_eq!( value_0, @@ -2329,9 +2329,9 @@ mod test { v_i8_2.memcomparable_encode(&mut key_i8_2)?; v_i8_3.memcomparable_encode(&mut key_i8_3)?; - assert!(key_i8_0 < key_i8_1); assert!(key_i8_1 < key_i8_2); assert!(key_i8_2 < key_i8_3); + assert!(key_i8_3 < key_i8_0); assert_eq!( v_i8_0, @@ -2378,9 +2378,9 @@ mod test { v_i16_2.memcomparable_encode(&mut key_i16_2)?; v_i16_3.memcomparable_encode(&mut key_i16_3)?; - assert!(key_i16_0 < key_i16_1); assert!(key_i16_1 < key_i16_2); assert!(key_i16_2 < key_i16_3); + assert!(key_i16_3 < key_i16_0); assert_eq!( v_i16_0, @@ -2427,9 +2427,9 @@ mod test { v_i32_2.memcomparable_encode(&mut key_i32_2)?; v_i32_3.memcomparable_encode(&mut key_i32_3)?; - assert!(key_i32_0 < key_i32_1); assert!(key_i32_1 < key_i32_2); assert!(key_i32_2 < key_i32_3); + assert!(key_i32_3 < key_i32_0); assert_eq!( v_i32_0, @@ -2476,9 +2476,9 @@ mod test { v_i64_2.memcomparable_encode(&mut key_i64_2)?; v_i64_3.memcomparable_encode(&mut key_i64_3)?; - assert!(key_i64_0 < key_i64_1); assert!(key_i64_1 < key_i64_2); assert!(key_i64_2 < key_i64_3); + assert!(key_i64_3 < key_i64_0); assert_eq!( v_i64_0, @@ -2532,9 +2532,9 @@ mod test { v_f32_2.memcomparable_encode(&mut key_f32_2)?; v_f32_3.memcomparable_encode(&mut key_f32_3)?; - assert!(key_f32_0 < key_f32_1); assert!(key_f32_1 < key_f32_2); assert!(key_f32_2 < key_f32_3); + assert!(key_f32_3 < key_f32_0); assert_eq!( v_f32_0, @@ -2569,9 +2569,9 @@ mod test { v_f64_2.memcomparable_encode(&mut key_f64_2)?; v_f64_3.memcomparable_encode(&mut key_f64_3)?; - assert!(key_f64_0 < key_f64_1); assert!(key_f64_1 < key_f64_2); assert!(key_f64_2 < key_f64_3); + assert!(key_f64_3 < key_f64_0); assert_eq!( v_f64_0, @@ -2628,9 +2628,9 @@ mod test { println!("{key_decimal_1:?} < {key_decimal_2:?}"); println!("{key_decimal_2:?} < {key_decimal_3:?}"); - assert!(key_decimal_0 < key_decimal_1); assert!(key_decimal_1 < key_decimal_2); assert!(key_decimal_2 < key_decimal_3); + assert!(key_decimal_3 < key_decimal_0); assert_eq!( v_decimal_0, @@ -2694,8 +2694,8 @@ mod test { println!("{key_tuple_1:?} < {key_tuple_2:?}"); println!("{key_tuple_2:?} < {key_tuple_3:?}"); - assert!(key_tuple_1 < key_tuple_2); assert!(key_tuple_2 < key_tuple_3); + assert!(key_tuple_3 < key_tuple_1); assert_eq!( v_tuple_1, @@ -2766,8 +2766,8 @@ mod test { v_tuple_2.memcomparable_encode(&mut key_tuple_2)?; v_tuple_3.memcomparable_encode(&mut key_tuple_3)?; - assert!(key_tuple_1 < key_tuple_2); assert!(key_tuple_2 < key_tuple_3); + assert!(key_tuple_3 < key_tuple_1); let ty = LogicalType::Tuple(vec![ LogicalType::Tinyint, @@ -2865,10 +2865,10 @@ mod test { v_zh.memcomparable_encode(&mut key_zh)?; // ordering - assert!(key_null < key_a); assert!(key_a < key_ab); assert!(key_ab < key_b); assert!(key_b < key_zh); + assert!(key_zh < key_null); // decode check assert_eq!( diff --git a/tests/slt/crdb/join.slt b/tests/slt/crdb/join.slt index c1066d3f..63bd0e37 100644 --- a/tests/slt/crdb/join.slt +++ b/tests/slt/crdb/join.slt @@ -10,15 +10,15 @@ INSERT INTO onecolumn(id, x) VALUES (0, 44), (1, NULL), (2, 42) query II SELECT * FROM onecolumn AS a(aid, x) CROSS JOIN onecolumn AS b(bid, y) order by x ---- -1 null 0 44 -1 null 1 null -1 null 2 42 2 42 0 44 2 42 1 null 2 42 2 42 0 44 0 44 0 44 1 null 0 44 2 42 +1 null 0 44 +1 null 1 null +1 null 2 42 statement error SELECT x FROM onecolumn AS a, onecolumn AS b; @@ -44,16 +44,16 @@ SELECT * FROM onecolumn AS a NATURAL JOIN onecolumn as b order by a.x desc query II SELECT * FROM onecolumn AS a(aid, x) LEFT OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y order by a.x ---- -1 null null null 2 42 2 42 0 44 0 44 +1 null null null query I SELECT * FROM onecolumn AS a LEFT OUTER JOIN onecolumn AS b USING(x) ORDER BY x ---- -1 null null 2 42 2 0 44 0 +1 null null statement error SELECT * FROM onecolumn AS a, onecolumn AS b ORDER BY x @@ -61,30 +61,30 @@ SELECT * FROM onecolumn AS a, onecolumn AS b ORDER BY x query I SELECT * FROM onecolumn AS a NATURAL LEFT OUTER JOIN onecolumn AS b order by a.x ---- -1 null 2 42 0 44 +1 null query II SELECT * FROM onecolumn AS a(aid, x) RIGHT OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y order by x ---- -null null 1 null 2 42 2 42 0 44 0 44 +null null 1 null query I SELECT * FROM onecolumn AS a RIGHT OUTER JOIN onecolumn AS b USING(x) ORDER BY x ---- -null 1 null 2 2 42 0 0 44 +null 1 null query I SELECT * FROM onecolumn AS a NATURAL RIGHT OUTER JOIN onecolumn AS b order by x ---- -1 null 2 42 0 44 +1 null statement ok drop table if exists onecolumn_w @@ -117,52 +117,52 @@ INSERT INTO othercolumn(o_id, x) VALUES (0, 43),(1, 42),(2, 16) query II SELECT * FROM onecolumn AS a FULL OUTER JOIN othercolumn AS b ON a.x = b.x ORDER BY a.x,b.x ---- -1 null null null -null null 2 16 -null null 0 43 2 42 1 42 0 44 null null +null null 2 16 +null null 0 43 +1 null null null query II SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON a.x = b.x and a.x > 16 order by a.x ---- +2 42 1 42 +0 44 null null null null 0 43 null null 2 16 1 null null null -2 42 1 42 -0 44 null null query II SELECT * FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON a.x = b.x and b.x > 16 order by b.x,a.x ---- -1 null null null -0 44 null null null null 2 16 2 42 1 42 null null 0 43 +0 44 null null +1 null null null query II SELECT a.x, b.x FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON false order by a.x, b.x ---- -null null +42 null +44 null null 16 null 42 null 43 -42 null -44 null +null null query II SELECT a.x, b.x FROM onecolumn AS a full OUTER JOIN othercolumn AS b ON true order by a.x, b.x ---- -null 16 -null 42 -null 43 42 16 42 42 42 43 44 16 44 42 44 43 +null 16 +null 42 +null 43 # Tips: This case will make x take the value of both sides # query @@ -210,9 +210,9 @@ SELECT * FROM empty AS a JOIN onecolumn AS b USING(x) query IT SELECT * FROM onecolumn AS a(aid, x) LEFT OUTER JOIN empty AS b(bid, y) ON a.x = b.y ORDER BY a.x ---- -null null 1 null null null 2 42 null null 0 44 +null null 1 null query I rowsort SELECT * FROM onecolumn AS a LEFT OUTER JOIN empty AS b USING(x) ORDER BY x @@ -240,9 +240,9 @@ SELECT * FROM onecolumn AS a RIGHT OUTER JOIN empty AS b USING(x) query II SELECT * FROM empty AS a(aid, x) FULL OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y ORDER BY b.y ---- -null null 1 null null null 2 42 null null 0 44 +null null 1 null statement ok SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x @@ -250,9 +250,9 @@ SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x query IIII SELECT * FROM onecolumn AS a(aid, x) FULL OUTER JOIN empty AS b(bid, y) ON a.x = b.y ORDER BY a.x ---- -null null 1 null null null 2 42 null null 0 44 +null null 1 null query III rowsort SELECT * FROM onecolumn AS a FULL OUTER JOIN empty AS b USING(x) ORDER BY x @@ -264,9 +264,9 @@ null 2 42 query II SELECT * FROM empty AS a(aid, x) FULL OUTER JOIN onecolumn AS b(bid, y) ON a.x = b.y ORDER BY b.y ---- -null null 1 null null null 2 42 null null 0 44 +null null 1 null # query # SELECT * FROM empty AS a FULL OUTER JOIN onecolumn AS b USING(x) ORDER BY x @@ -302,23 +302,23 @@ SELECT o.x, t.y FROM onecolumn o INNER JOIN twocolumn t ON (o.x=t.x AND t.y=53) query IT SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND t.y=53) order by o.x ---- -null null 42 53 44 null +null null query II SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND o.x=44) order by o.x ---- -null null 42 null 44 51 +null null query II SELECT o.x, t.y FROM onecolumn o LEFT OUTER JOIN twocolumn t ON (o.x=t.x AND t.x=44) order by o.x ---- -null null 42 null 44 51 +null null # query # SELECT * FROM (SELECT x, 2 two FROM onecolumn) NATURAL FULL JOIN (SELECT x, y+1 plus1 FROM twocolumn) @@ -364,19 +364,19 @@ null null 2 4 false query III SELECT * FROM a FULL OUTER JOIN b ON a.i = b.i order by b ---- -0 1 null null null null null 2 4 false 1 2 0 2 true 2 3 1 3 true +0 1 null null null query III SELECT * FROM a FULL OUTER JOIN b ON (a.i = b.i and a.i>2) ORDER BY a.i, b.i ---- -null null 0 2 true -null null 2 4 false 0 1 null null null 1 2 null null null 2 3 1 3 true +null null 0 2 true +null null 2 4 false statement ok INSERT INTO b VALUES (3, 3, false) @@ -392,11 +392,11 @@ null null 2 4 false query III SELECT * FROM a FULL OUTER JOIN b ON a.i=b.i ORDER BY b.i, b.b ---- -0 1 null null null 1 2 0 2 true 2 3 3 3 false 2 3 1 3 true null null 2 4 false +0 1 null null null # TODO # query IIIIII @@ -537,10 +537,6 @@ SELECT * FROM pairs, square WHERE pairs.a + pairs.b = square.sq query III SELECT a, b, n, sq FROM pairs FULL OUTER JOIN square ON pairs.a + pairs.b = square.sq order by a ---- -null null 1 1 -null null 4 16 -null null 5 25 -null null 6 36 1 1 null null 1 2 null null 1 3 2 4 @@ -556,6 +552,10 @@ null null 6 36 3 6 3 9 4 5 3 9 4 6 null null +null null 1 1 +null null 4 16 +null null 5 25 +null null 6 36 query IIII SELECT pairs.a, pairs.b, square.* FROM pairs FULL OUTER JOIN square ON pairs.a + pairs.b = square.sq WHERE pairs.b%2 <> square.sq%2 order by a diff --git a/tests/slt/crdb/order_by.slt b/tests/slt/crdb/order_by.slt index 35fc5b64..a960d3fc 100644 --- a/tests/slt/crdb/order_by.slt +++ b/tests/slt/crdb/order_by.slt @@ -10,23 +10,23 @@ INSERT INTO t VALUES (1, 9, true), (2, 8, false), (3, 7, NULL) query I SELECT c FROM t ORDER BY c ---- -null false true +null query I SELECT c FROM t ORDER BY c ---- -null false true +null query I SELECT c FROM t ORDER BY c DESC ---- -null true false +null query II SELECT a, b FROM t ORDER BY b @@ -354,20 +354,20 @@ null 6 query II SELECT x, y FROM xy ORDER BY x, y ---- -null null -null 6 -2 null 2 5 +2 null 4 8 +null 6 +null null query IT SELECT x, y FROM xy ORDER BY x, y DESC NULLS FIRST ---- -null null -null 6 2 null 2 5 4 8 +null null +null 6 query IT SELECT x, y FROM xy ORDER BY x NULLS LAST, y DESC NULLS FIRST @@ -390,10 +390,10 @@ null null query TT SELECT x, y FROM xy ORDER BY x NULLS FIRST, y DESC ---- -null null null 6 -2 null +null null 2 5 +2 null 4 8 query TI @@ -403,4 +403,4 @@ null null null 6 2 null 2 5 -4 8 \ No newline at end of file +4 8 diff --git a/tests/slt/explain.slt.reference b/tests/slt/explain.slt.reference deleted file mode 100644 index f238e53d..00000000 --- a/tests/slt/explain.slt.reference +++ /dev/null @@ -1,236 +0,0 @@ -statement ok -create table t1(id int primary key, c1 int, c2 varchar) - -statement ok -create table t2(id int primary key, c3 int, c4 varchar) - -query T -COPY t1 FROM 'tests/data/row_20000.csv' ( DELIMITER '|' ); ----- -import 20000 rows - -statement ok -analyze table t1 - -query I -explain select * from t1 ----- -Projection [id, c1, c2] [Project] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain select c1 from t1 ----- -Projection [c1] [Project] - Scan t1 -> [c1] [SeqScan] - -query T -explain select c1 from t1 limit 10 ----- -Projection [c1] [Project] - Scan t1 -> [c1], Limit: 10 [SeqScan] - -query T -explain select c1 from t1 limit 10 offset 5 ----- -Projection [c1] [Project] - Scan t1 -> [c1], Limit: 10, Offset: 5 [SeqScan] - -query T -explain select c1 from t1 where c1 + 1 = 1 or c2 > 1 ----- -Projection [c1] [Project] - Filter ((c1 = 0) || (c2 > 1)), Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1 from t1 where c1 > 1 + 3 and c1 < 10 ----- -Projection [c1] [Project] - Filter ((c1 > 4) && (c1 < 10)), Is Having: false [Filter] - Scan t1 -> [c1] [SeqScan] - -query T -explain select c1 from t1 where c1 in (1, 2, 3) ----- -Projection [c1] [Project] - Filter ((c1 = 3) || ((c1 = 2) || ((c1 = 1)))), Is Having: false [Filter] - Scan t1 -> [c1] [SeqScan] - -query T -explain select c1 from t1 where c1 not in (1, 2, 3) ----- -Projection [c1] [Project] - Filter ((c1 != 3) && ((c1 != 2) && ((c1 != 1)))), Is Having: false [Filter] - Scan t1 -> [c1] [SeqScan] - -query T -explain select c1 from t1 where c2 like 'lol%' ----- -Projection [c1] [Project] - Filter (c2 like lol%), Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1 from t1 where c3 not like 'lol%' ----- -Projection [c1] [Project] - Filter (c2 not like lol%), Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1 from t1 where c2 is null ----- -Projection [c1] [Project] - Filter c2 is null, Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1 from t1 where c2 is not null ----- -Projection [c1] [Project] - Filter c2 is not null, Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1 from t1 order by c1 ----- -Projection [c1] [Project] - Sort By c1 Asc Nulls First [RadixSort] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain select c1 from t1 order by c1 desc, c2 ----- -Projection [c1] [Project] - Sort By c1 Desc Null First, c2 Asc Nulls First [RadixSort] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain select c1 from t1 order by c1 nulls last ----- -Projection [c1] [Project] - Sort By c1 Asc Nulls Last [RadixSort] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain select sum(c1) from t1 ----- -Projection [Sum(c1)] [Project] - Aggregate [Sum(c1)] [SimpleAggregate] - Scan t1 -> [c1] [SeqScan] - -query T -explain select c1, sum(c2) from t1 group by c1 ----- -Projection [c1, Sum(c2)] [Project] - Aggregate [Sum(c2)] -> Group By [c1] [HashAggregate] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1, sum(c2) from t1 where c1 > 10 group by c1 ----- -Projection [c1, Sum(c2)] [Project] - Aggregate [Sum(c2)] -> Group By [c1] [HashAggregate] - Filter (c1 > 10), Is Having: false [Filter] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select c1, sum(c2) from t1 group by c1 having c1 > 10 ----- -Projection [c1, Sum(c2)] [Project] - Filter (c1 > 10), Is Having: true [Filter] - Aggregate [Sum(c2)] -> Group By [c1] [HashAggregate] - Scan t1 -> [c1, c2] [SeqScan] - -query T -explain select * from t1 left join t2 on c1 = c2 and c1 > 10 ----- -Projection [id, c1, c2, id, c3, c4] [Project] - Left Join On Where ((c1 = c2) && (c1 > 10)) [HashJoin] - Scan t1 -> [id, c1, c2] [SeqScan] - Scan t2 -> [id, c3, c4] [SeqScan] - -query T -explain select * from t1 right join t2 on c1 = c2 and c1 > 10 ----- -Projection [id, c1, c2, id, c3, c4] [Project] - Right Join On Where ((c1 = c2) && (c1 > 10)) [HashJoin] - Scan t1 -> [id, c1, c2] [SeqScan] - Scan t2 -> [id, c3, c4] [SeqScan] - -query T -explain select * from t1 inner join t2 on c1 = c2 and c1 > 10 ----- -Projection [id, c1, c2, id, c3, c4] [Project] - Inner Join On Where ((c1 = c2) && (c1 > 10)) [HashJoin] - Scan t1 -> [id, c1, c2] [SeqScan] - Scan t2 -> [id, c3, c4] [SeqScan] - -query T -explain select * from t1 full join t2 on c1 = c2 and c1 > 10 ----- -Projection [id, c1, c2, id, c3, c4] [Project] - Full Join On Where ((c1 = c2) && (c1 > 10)) [HashJoin] - Scan t1 -> [id, c1, c2] [SeqScan] - Scan t2 -> [id, c3, c4] [SeqScan] - -query T -explain show tables ----- -Show Tables - -query T -explain insert into t1 values (200001,1,10) ----- -Insert t1, Is Overwrite: false [Insert] - Values [id, c1, c2], RowsLen: 1 [Values] - -query T -explain insert overwrite t1 values (200001,1,10) ----- -Insert t1, Is Overwrite: true [Insert] - Values [id, c1, c2], RowsLen: 1 [Values] - -query T -explain update t1 set c1 = 0 where id = 0 ----- -Update t1 [Update] - Filter (id = 0), Is Having: false [Filter] - Scan t1 -> [id, c1, c2] [IndexScan By pk_id => 0] - Values [c1], RowsLen: 1 [Values] - -query T -explain delete from t1 where id = 0 ----- -Delete t1 [Delete] - Filter (id = 0), Is Having: false [Filter] - Scan t1 -> [id, c1, c2] [IndexScan By pk_id => 0] - -query T -explain alter table t1 add column if not exists c8 int default 0 ----- -Add c8 -> t1, If Not Exists: true [AddColumn] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain alter table t1 drop column if exists c2 ----- -Drop c2 -> t1, If Exists: true [DropColumn] - Scan t1 -> [id, c1, c2] [SeqScan] - -query T -explain truncate t1 ----- -Truncate t1 - -query T -explain copy t1 from 'tests/data/row_20000.csv' ( DELIMITER '|' ); ----- -Copy tests/data/row_20000.tbl -> t1 [id, c1, c2] [CopyFromFile] - -statement ok -drop table t1 - -statement ok -drop table t2 \ No newline at end of file diff --git a/tests/slt/order_by.slt b/tests/slt/order_by.slt index e8cc4599..fe7e75be 100644 --- a/tests/slt/order_by.slt +++ b/tests/slt/order_by.slt @@ -55,20 +55,20 @@ insert into t values (0, 1, 0), (1, 2, 2), (2, null, 5), (3, 2, null), (4, null, query II select v1, v2 from t order by v1 asc, v2 asc ---- -null null -null 5 1 0 -2 null 2 2 +2 null +null 5 +null null query II select v1, v2 from t order by v1 asc nulls last, v2 asc ---- 1 0 -2 null 2 2 -null null +2 null null 5 +null null statement ok drop table t @@ -90,4 +90,4 @@ select v1 as a from t order by a 10 statement ok -drop table t \ No newline at end of file +drop table t diff --git a/tpcc/README.md b/tpcc/README.md index 00348317..a38586df 100644 --- a/tpcc/README.md +++ b/tpcc/README.md @@ -12,11 +12,11 @@ Transaction Summary (elapsed 720.0s) +--------------+---------+------+---------+-------+ | Transaction | Success | Late | Failure | Total | +--------------+---------+------+---------+-------+ -| New-Order | 221183 | 0 | 2284 | 223467 | -| Payment | 221160 | 0 | 7346 | 228506 | -| Order-Status | 22116 | 0 | 493 | 22609 | -| Delivery | 22117 | 0 | 0 | 22117 | -| Stock-Level | 22116 | 0 | 0 | 22116 | +| New-Order | 326708 | 0 | 3334 | 330042 | +| Payment | 326683 | 0 | 16218 | 342901 | +| Order-Status | 32669 | 0 | 547 | 33216 | +| Delivery | 32669 | 0 | 0 | 32669 | +| Stock-Level | 32668 | 0 | 0 | 32668 | +--------------+---------+------+---------+-------+ (all must be [OK]) [transaction percentage] @@ -36,66 +36,65 @@ Transaction Summary (elapsed 720.0s) 1.New-Order -0.001, 153654 -0.002, 66775 -0.003, 17 +0.001, 219955 +0.002, 106100 +0.003, 44 0.004, 2 -0.006, 1 +0.005, 1 2.Payment -0.001, 220847 -0.002, 11 +0.001, 326442 +0.002, 66 0.003, 1 3.Order-Status -0.001, 20887 -0.002, 946 -0.003, 123 +0.001, 28771 +0.002, 2736 +0.003, 454 +0.004, 145 +0.005, 22 +0.006, 2 4.Delivery -0.009, 342 -0.010, 802 -0.011, 862 -0.012, 1017 -0.013, 1309 -0.014, 1514 -0.015, 1647 -0.016, 1788 -0.017, 1797 -0.018, 1792 -0.019, 1747 -0.020, 1533 -0.021, 1333 -0.022, 1179 -0.023, 719 -0.024, 358 -0.025, 153 -0.026, 13 -0.027, 3 -0.029, 2 -0.037, 1 -0.038, 1 -0.039, 1 +0.003, 11 +0.004, 6201 +0.005, 6965 +0.006, 6338 +0.007, 5805 +0.008, 2971 +0.009, 355 +0.010, 535 +0.011, 690 +0.012, 980 +0.013, 273 +0.014, 693 +0.015, 132 +0.016, 43 +0.017, 2 +0.019, 1 +0.021, 3 +0.022, 1 +0.024, 1 5.Stock-Level -0.001, 10061 -0.002, 7547 -0.003, 1844 -0.004, 25 +0.001, 15844 +0.002, 13502 +0.003, 2228 +0.004, 163 0.005, 1 <90th Percentile RT (MaxRT)> - New-Order : 0.002 (0.006) - Payment : 0.001 (0.019) -Order-Status : 0.001 (0.003) - Delivery : 0.022 (0.038) - Stock-Level : 0.002 (0.005) + New-Order : 0.002 (0.005) + Payment : 0.001 (0.013) +Order-Status : 0.002 (0.006) + Delivery : 0.010 (0.023) + Stock-Level : 0.002 (0.017) -18432 Tpmc +27226 Tpmc ``` ## Refer to