From 7db8cfa686150018f2dd9a3b8529d063322c2905 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 30 Oct 2025 16:29:49 +0300 Subject: [PATCH 01/43] Add antlr+llvm template --- .clang-format | 16 +++ .gitignore | 4 + CMakeLists.txt | 27 ++++ Makefile | 5 + cmake/FetchAntlr4.cmake | 17 +++ cmake/FetchGTest.cmake | 14 ++ cmake/SetupLLVM.cmake | 7 + compile_commands.json | 1 + docs/TLexer.g4 | 37 +++++ docs/TParser.g4 | 37 +++++ flake.nix | 5 + src/stewkk/sql/CMakeLists.txt | 30 ++++ src/stewkk/sql/logic/llvm_ir_builder.cpp | 171 +++++++++++++++++++++++ src/stewkk/sql/logic/llvm_ir_builder.hpp | 46 ++++++ src/stewkk/sql/main.cpp | 39 ++++++ src/stewkk/sql/some_test.cpp | 15 ++ test/CMakeLists.txt | 11 ++ 17 files changed, 482 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100644 cmake/FetchAntlr4.cmake create mode 100644 cmake/FetchGTest.cmake create mode 100644 cmake/SetupLLVM.cmake create mode 120000 compile_commands.json create mode 100644 docs/TLexer.g4 create mode 100644 docs/TParser.g4 create mode 100644 src/stewkk/sql/CMakeLists.txt create mode 100644 src/stewkk/sql/logic/llvm_ir_builder.cpp create mode 100644 src/stewkk/sql/logic/llvm_ir_builder.hpp create mode 100644 src/stewkk/sql/main.cpp create mode 100644 src/stewkk/sql/some_test.cpp create mode 100644 test/CMakeLists.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ba558c0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +--- + BasedOnStyle: Google + AccessModifierOffset: '-2' + AlignTrailingComments: 'true' + AllowAllParametersOfDeclarationOnNextLine: 'false' + AlwaysBreakTemplateDeclarations: 'No' + BreakBeforeBraces: Attach + ColumnLimit: '100' + ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' + IncludeBlocks: Preserve + IndentPPDirectives: AfterHash + IndentWidth: '2' + NamespaceIndentation: None + BreakBeforeBinaryOperators: All + BreakBeforeTernaryOperators: 'true' +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f923ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.cache/ +/build/ +/CMakeFiles/ +/src/stewkk/sql/codegen/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..abe5f38 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.15..3.29) + +project( + sql-compiler + VERSION 0.0.1 + LANGUAGES CXX +) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +set(FETCHCONTENT_QUIET OFF) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_subdirectory(src/stewkk/sql) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) + if(BUILD_TESTING) + add_subdirectory(test) + endif() +endif() diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b66f30f --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ + +codegen: + @cd docs && antlr -Dlanguage=Cpp -visitor -o ../src/stewkk/sql/codegen -package stewkk::sql::codegen TParser.g4 TLexer.g4 + +.PHONY: codegen diff --git a/cmake/FetchAntlr4.cmake b/cmake/FetchAntlr4.cmake new file mode 100644 index 0000000..0a764d8 --- /dev/null +++ b/cmake/FetchAntlr4.cmake @@ -0,0 +1,17 @@ +include(FetchContent) + +set(ANTLR_BUILD_STATIC ON) +set(ANTLR_BUILD_SHARED OFF) +set(DISABLE_WARNINGS ON) +set(ANTLR_BUILD_CPP_TESTS OFF) +set(ANTLR_EXECUTABLE $ENV{ANTLR_JAR}) + +FetchContent_Declare( + antlr4 + GIT_REPOSITORY https://github.com/antlr/antlr4.git + GIT_TAG cc82115a4e7f53d71d9d905caa2c2dfa4da58899 + SOURCE_SUBDIR runtime/Cpp +) + +FetchContent_MakeAvailable(antlr4) +include(${antlr4_SOURCE_DIR}/runtime/Cpp/cmake/FindANTLR.cmake) diff --git a/cmake/FetchGTest.cmake b/cmake/FetchGTest.cmake new file mode 100644 index 0000000..318358c --- /dev/null +++ b/cmake/FetchGTest.cmake @@ -0,0 +1,14 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 03597a01ee50ed33e9dfd640b249b4be3799d395 +) + +FetchContent_GetProperties(googletest) +if (NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} + EXCLUDE_FROM_ALL) +endif () diff --git a/cmake/SetupLLVM.cmake b/cmake/SetupLLVM.cmake new file mode 100644 index 0000000..a61d32d --- /dev/null +++ b/cmake/SetupLLVM.cmake @@ -0,0 +1,7 @@ +find_package(ZLIB QUIET) + +set(LLVM_DIR "/home/st/c/llvm-project/build/lib/cmake/llvm") +find_package(LLVM REQUIRED CONFIG) + +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") diff --git a/compile_commands.json b/compile_commands.json new file mode 120000 index 0000000..25eb4b2 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1 @@ +build/compile_commands.json \ No newline at end of file diff --git a/docs/TLexer.g4 b/docs/TLexer.g4 new file mode 100644 index 0000000..b88fe74 --- /dev/null +++ b/docs/TLexer.g4 @@ -0,0 +1,37 @@ +lexer grammar TLexer; + +channels { CommentsChannel } + +tokens { + DUMMY +} + +Return: 'return'; +Continue: 'continue'; +Break: 'break'; +If: 'if'; +While: 'while'; + +INT: Digit+; +Digit: [0-9]; + +ID: LETTER (LETTER | '0'..'9')*; +fragment LETTER : [a-zA-Z\u0080-\u{10FFFF}]; + +LessThan: '<'; +GreaterThan: '>'; +Assign: '='; +Equal: '=='; +NotEqual: '!='; + +Semicolon: ';'; +Plus: '+'; +Minus: '-'; +Star: '*'; +OpenPar: '('; +ClosePar: ')'; +OpenCurly: '{'; +CloseCurly: '}'; + +Comment : '#' ~[\r\n]* '\r'? '\n' -> channel(CommentsChannel); +WS: [ \t\r\n]+ -> channel(HIDDEN); diff --git a/docs/TParser.g4 b/docs/TParser.g4 new file mode 100644 index 0000000..d63015d --- /dev/null +++ b/docs/TParser.g4 @@ -0,0 +1,37 @@ +parser grammar TParser; + +options { + tokenVocab = TLexer; +} + +main: stat+ EOF; + +stat: ID Assign expr Semicolon # Assign + | Return expr Semicolon # Return + | Break Semicolon # Break + | Continue Semicolon # Continue + | control OpenPar cond ClosePar OpenCurly stat+ CloseCurly # FlowControl +; + +control: If + | While +; + +cond: lhs=expr condOp rhs=expr; + +condOp: Equal + | NotEqual + | LessThan + | GreaterThan +; + +expr: lhs=expr op rhs=expr # BinaryOp + | OpenPar expr ClosePar # Nested + | identifier = ID # Ident + | INT # Int +; + +op: Star + | Minus + | Plus +; diff --git a/flake.nix b/flake.nix index b8a043f..f14e4ed 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,10 @@ buildInputs = with pkgs; [ code-cursor-fhs pythonEnv + antlr + cmake + zlib + zlib.dev ]; nativeBuildInputs = [ @@ -36,6 +40,7 @@ NIX_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc pkgs.zlib + pkgs.zlib.dev ]; NIX_LD = pkgs.lib.fileContents "${pkgs.stdenv.cc}/nix-support/dynamic-linker"; diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt new file mode 100644 index 0000000..dceb7c3 --- /dev/null +++ b/src/stewkk/sql/CMakeLists.txt @@ -0,0 +1,30 @@ +include(FetchAntlr4) +include(SetupLLVM) + +add_definitions(-DANTLR4CPP_STATIC) + +add_executable(sql + main.cpp + codegen/TParser.cpp + codegen/TLexer.cpp + codegen/TParserBaseVisitor.cpp + codegen/TParserVisitor.cpp + logic/llvm_ir_builder.cpp +) + +target_compile_features(sql PRIVATE cxx_std_20) +set_target_properties(sql PROPERTIES + CXX_STANDART 20 + CXX_STANDART_REQUIRED YES + CXX_EXTENSIONS YES +) + +target_compile_options(sql PRIVATE ${BASE_COMPILE_FLAGS}) +target_include_directories(sql PRIVATE + ${ANTLR4_INCLUDE_DIRS} + ${LLVM_INCLUDE_DIRS} + $ +) +llvm_map_components_to_libnames(llvm_libs support core irreader) +target_link_libraries(sql PRIVATE antlr4_static ${llvm_libs} +) diff --git a/src/stewkk/sql/logic/llvm_ir_builder.cpp b/src/stewkk/sql/logic/llvm_ir_builder.cpp new file mode 100644 index 0000000..8f9982f --- /dev/null +++ b/src/stewkk/sql/logic/llvm_ir_builder.cpp @@ -0,0 +1,171 @@ +#include + +#include + +namespace stewkk::sql::logic { + +Visitor::Visitor() + : llvm_context_(std::make_unique()), + ir_builder_( + std::make_unique>(*llvm_context_, llvm::NoFolder())), + ir_module_(std::make_unique("sql program", *llvm_context_)) { + llvm::FunctionType* ft = + llvm::FunctionType::get(llvm::Type::getInt32Ty(*llvm_context_), {}, false); + + llvm::Function* function = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, "main", ir_module_.get()); + + llvm::BasicBlock *bb = llvm::BasicBlock::Create(*llvm_context_, "entry", function); + + ir_builder_->SetInsertPoint(bb); +} + +std::any Visitor::visitInt(codegen::TParser::IntContext *ctx) { + auto i = ctx->INT(); + std::int32_t val = std::stoi(i->getText()); + return static_cast(llvm::ConstantInt::get(*llvm_context_, llvm::APInt(32, val))); +} + +std::any Visitor::visitIdent(codegen::TParser::IdentContext *ctx) { + auto name = ctx->ID()->getText(); + auto alloca = named_values_[name]; + return static_cast(ir_builder_->CreateLoad(alloca->getAllocatedType(), alloca, name)); +} + +std::any Visitor::visitFlowControl(codegen::TParser::FlowControlContext *ctx) { + if (ctx->control()->getText() == "if") { + llvm::Value *cond = std::any_cast(visit(ctx->cond())); + if (!cond) { + return nullptr; + } + + llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); + llvm::BasicBlock *then_bb = llvm::BasicBlock::Create(*llvm_context_, "then", function); + llvm::BasicBlock *merge_bb = llvm::BasicBlock::Create(*llvm_context_, "ifcont"); + ir_builder_->CreateCondBr(cond, then_bb, merge_bb); + + ir_builder_->SetInsertPoint(then_bb); + + for (auto stat : ctx->stat()) { + visit(stat); + } + + ir_builder_->CreateBr(merge_bb); + then_bb = ir_builder_->GetInsertBlock(); + + function->insert(function->end(), merge_bb); + ir_builder_->SetInsertPoint(merge_bb); + + return nullptr; + } else { + llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); + + llvm::BasicBlock *loop_bb = llvm::BasicBlock::Create(*llvm_context_, "loop", function); + llvm::BasicBlock *body_bb = llvm::BasicBlock::Create(*llvm_context_, "body", function); + llvm::BasicBlock *after_bb = llvm::BasicBlock::Create(*llvm_context_, "afterloop", function); + ir_builder_->CreateBr(loop_bb); + ir_builder_->SetInsertPoint(loop_bb); + + llvm::Value *cond = std::any_cast(visit(ctx->cond())); + if (!cond) { + return nullptr; + } + + ir_builder_->CreateCondBr(cond, body_bb, after_bb); + + ir_builder_->SetInsertPoint(body_bb); + + for (auto stat : ctx->stat()) { + visit(stat); + } + + ir_builder_->CreateBr(loop_bb); + + ir_builder_->SetInsertPoint(after_bb); + + return nullptr; + } +} + +llvm::AllocaInst* Visitor::CreateEntryBlockAlloca(llvm::Function *function, + llvm::StringRef varname) { + llvm::IRBuilder<> tmp(&function->getEntryBlock(), + function->getEntryBlock().begin()); + return tmp.CreateAlloca(llvm::Type::getInt32Ty(*llvm_context_), nullptr, varname); +} + +std::any Visitor::visitAssign(codegen::TParser::AssignContext *ctx) { + auto varname = ctx->ID()->getText(); + std::cerr << "visitAssign " << varname << std::endl; + llvm::Value* val = std::any_cast(visit(ctx->expr())); + if (!val) { + return nullptr; + } + + std::cerr << "got expr" << std::endl; + llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); + llvm::AllocaInst *alloca; + if (auto it = named_values_.find(varname); it != named_values_.end()) { + alloca = it->second; + } else { + alloca = CreateEntryBlockAlloca(function, varname); + } + ir_builder_->CreateStore(val, alloca); + + named_values_[varname] = alloca; + + return nullptr; +} + +std::any Visitor::visitCond(codegen::TParser::CondContext *ctx) { + llvm::Value *lhs = std::any_cast(visit(ctx->lhs)); + llvm::Value *rhs = std::any_cast(visit(ctx->rhs)); + + if (!lhs || !rhs) { + return nullptr; + } + + auto op = ctx->condOp()->getText(); + if (op == "<") { + return ir_builder_->CreateICmpSLE(lhs, rhs); + } else if (op == ">") { + return ir_builder_->CreateICmpSGE(lhs, rhs); + } else if (op == "==") { + return ir_builder_->CreateICmpEQ(lhs, rhs); + } else if (op == "!=") { + return ir_builder_->CreateICmpNE(lhs, rhs); + } + throw std::logic_error{"invalid binary operator"}; +} + +std::any Visitor::visitBinaryOp(codegen::TParser::BinaryOpContext *ctx) { + std::cerr << "binary op" << std::endl; + llvm::Value *lhs = std::any_cast(visit(ctx->lhs)); + llvm::Value *rhs = std::any_cast(visit(ctx->rhs)); + + if (!lhs || !rhs) { + return nullptr; + } + + // FIXME: dirty way to get op + auto op = ctx->op()->getText(); + if (op == "+") { + return ir_builder_->CreateAdd(lhs, rhs); + } else if (op == "-") { + return ir_builder_->CreateSub(lhs, rhs); + } else if (op == "*") { + return ir_builder_->CreateMul(lhs, rhs); + } + throw std::logic_error{"invalid binary operator"}; +} + +std::any Visitor::visitReturn(codegen::TParser::ReturnContext *ctx) { + llvm::Value* val = std::any_cast(visit(ctx->expr())); + ir_builder_->CreateRet(val); + return nullptr; +} + +const llvm::Module* Visitor::GetIr() const { + return ir_module_.get(); +} + +} // namespace stewkk::sql::logic diff --git a/src/stewkk/sql/logic/llvm_ir_builder.hpp b/src/stewkk/sql/logic/llvm_ir_builder.hpp new file mode 100644 index 0000000..dba2faf --- /dev/null +++ b/src/stewkk/sql/logic/llvm_ir_builder.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +namespace stewkk::sql::logic { + +class Visitor : public codegen::TParserBaseVisitor { +public: + Visitor(); + + virtual std::any visitInt(codegen::TParser::IntContext *ctx) override; + virtual std::any visitIdent(codegen::TParser::IdentContext *ctx) override; + virtual std::any visitBinaryOp(codegen::TParser::BinaryOpContext *ctx) override; + virtual std::any visitFlowControl(codegen::TParser::FlowControlContext *ctx) override; + virtual std::any visitAssign(codegen::TParser::AssignContext *ctx) override; + virtual std::any visitReturn(codegen::TParser::ReturnContext *ctx) override; + virtual std::any visitCond(codegen::TParser::CondContext *ctx) override; + + llvm::AllocaInst* CreateEntryBlockAlloca(llvm::Function *TheFunction, + llvm::StringRef VarName); + + const llvm::Module* GetIr() const; + +private: + std::unique_ptr llvm_context_; + std::unique_ptr> ir_builder_; + std::unique_ptr ir_module_; + std::map named_values_; +}; + +} // namespace stewkk::sql::logic diff --git a/src/stewkk/sql/main.cpp b/src/stewkk/sql/main.cpp new file mode 100644 index 0000000..91e5881 --- /dev/null +++ b/src/stewkk/sql/main.cpp @@ -0,0 +1,39 @@ +#include + +#include + +#include +#include +#include + +#include + +using namespace stewkk::sql::codegen; +using namespace stewkk::sql::logic; +using namespace antlr4; + +int main() { + std::ifstream f("example.txt"); + + ANTLRInputStream input(f); + TLexer lexer(&input); + CommonTokenStream tokens(&lexer); + + tokens.fill(); + for (auto token : tokens.getTokens()) { + std::cout << token->toString() << std::endl; + } + + TParser parser(&tokens); + tree::ParseTree* tree = parser.main(); + + std::cout << tree->toStringTree(&parser) << std::endl << std::endl; + + Visitor visitor; + visitor.visit(tree); + + auto ir = visitor.GetIr(); + ir->print(llvm::errs(), nullptr); + + return 0; +} diff --git a/src/stewkk/sql/some_test.cpp b/src/stewkk/sql/some_test.cpp new file mode 100644 index 0000000..c518b23 --- /dev/null +++ b/src/stewkk/sql/some_test.cpp @@ -0,0 +1,15 @@ +#include + +using ::testing::Eq; +using ::testing::Optional; + +using std::string_literals::operator""s; +using std::string_view_literals::operator""sv; + +namespace stewkk::sql { + +TEST(ExampleTest, APlusB) { + ASSERT_THAT(2*2, Eq(4)); +} + +} // namespace stewkk::sql diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..fc0fbfc --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,11 @@ +include(FetchGTest) +include(GoogleTest) + +add_executable(unittests) +target_sources(unittests PRIVATE + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/some_test.cpp +) +target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) +target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) +target_link_libraries(unittests PRIVATE gmock_main) +gtest_discover_tests(unittests) From 39d4db66585da7aaa6047ffa5f383b2136bd1557 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 30 Oct 2025 17:02:35 +0300 Subject: [PATCH 02/43] Add postgresql grammar --- .gitignore | 2 +- Makefile | 10 +- docs/TLexer.g4 | 37 - docs/TParser.g4 | 37 - src/stewkk/sql/CMakeLists.txt | 18 +- src/stewkk/sql/logic/llvm_ir_builder.cpp | 171 - src/stewkk/sql/logic/llvm_ir_builder.hpp | 46 - .../sql/logic/parser/PostgreSQLLexer.g4 | 1474 +++++ .../sql/logic/parser/PostgreSQLLexerBase.cpp | 81 + .../sql/logic/parser/PostgreSQLLexerBase.h | 24 + .../sql/logic/parser/PostgreSQLParser.g4 | 5464 +++++++++++++++++ .../sql/logic/parser/PostgreSQLParserBase.cpp | 17 + .../sql/logic/parser/PostgreSQLParserBase.h | 10 + src/stewkk/sql/main.cpp | 31 +- 14 files changed, 7095 insertions(+), 327 deletions(-) delete mode 100644 docs/TLexer.g4 delete mode 100644 docs/TParser.g4 delete mode 100644 src/stewkk/sql/logic/llvm_ir_builder.cpp delete mode 100644 src/stewkk/sql/logic/llvm_ir_builder.hpp create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLLexer.g4 create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLLexerBase.cpp create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLLexerBase.h create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLParser.g4 create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLParserBase.cpp create mode 100644 src/stewkk/sql/logic/parser/PostgreSQLParserBase.h diff --git a/.gitignore b/.gitignore index 8f923ad..bd2af54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /.cache/ /build/ /CMakeFiles/ -/src/stewkk/sql/codegen/ +**/codegen/ diff --git a/Makefile b/Makefile index b66f30f..b7b89d1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ +CURRENT_DIR := $(shell pwd) +CODEGEN_DIR := $(CURRENT_DIR)/src/stewkk/sql/logic/parser/codegen +PARSER_SOURCE_DIR := $(CURRENT_DIR)/src/stewkk/sql/logic/parser + +build: + cmake --build build -- -j 6 codegen: - @cd docs && antlr -Dlanguage=Cpp -visitor -o ../src/stewkk/sql/codegen -package stewkk::sql::codegen TParser.g4 TLexer.g4 + @antlr -Dlanguage=Cpp -visitor -o $(CODEGEN_DIR) -package stewkk::sql::codegen $(PARSER_SOURCE_DIR)/PostgreSQLParser.g4 $(PARSER_SOURCE_DIR)/PostgreSQLLexer.g4 -.PHONY: codegen +.PHONY: codegen build diff --git a/docs/TLexer.g4 b/docs/TLexer.g4 deleted file mode 100644 index b88fe74..0000000 --- a/docs/TLexer.g4 +++ /dev/null @@ -1,37 +0,0 @@ -lexer grammar TLexer; - -channels { CommentsChannel } - -tokens { - DUMMY -} - -Return: 'return'; -Continue: 'continue'; -Break: 'break'; -If: 'if'; -While: 'while'; - -INT: Digit+; -Digit: [0-9]; - -ID: LETTER (LETTER | '0'..'9')*; -fragment LETTER : [a-zA-Z\u0080-\u{10FFFF}]; - -LessThan: '<'; -GreaterThan: '>'; -Assign: '='; -Equal: '=='; -NotEqual: '!='; - -Semicolon: ';'; -Plus: '+'; -Minus: '-'; -Star: '*'; -OpenPar: '('; -ClosePar: ')'; -OpenCurly: '{'; -CloseCurly: '}'; - -Comment : '#' ~[\r\n]* '\r'? '\n' -> channel(CommentsChannel); -WS: [ \t\r\n]+ -> channel(HIDDEN); diff --git a/docs/TParser.g4 b/docs/TParser.g4 deleted file mode 100644 index d63015d..0000000 --- a/docs/TParser.g4 +++ /dev/null @@ -1,37 +0,0 @@ -parser grammar TParser; - -options { - tokenVocab = TLexer; -} - -main: stat+ EOF; - -stat: ID Assign expr Semicolon # Assign - | Return expr Semicolon # Return - | Break Semicolon # Break - | Continue Semicolon # Continue - | control OpenPar cond ClosePar OpenCurly stat+ CloseCurly # FlowControl -; - -control: If - | While -; - -cond: lhs=expr condOp rhs=expr; - -condOp: Equal - | NotEqual - | LessThan - | GreaterThan -; - -expr: lhs=expr op rhs=expr # BinaryOp - | OpenPar expr ClosePar # Nested - | identifier = ID # Ident - | INT # Int -; - -op: Star - | Minus - | Plus -; diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index dceb7c3..46a70e0 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -5,13 +5,19 @@ add_definitions(-DANTLR4CPP_STATIC) add_executable(sql main.cpp - codegen/TParser.cpp - codegen/TLexer.cpp - codegen/TParserBaseVisitor.cpp - codegen/TParserVisitor.cpp - logic/llvm_ir_builder.cpp + logic/parser/codegen/PostgreSQLLexer.cpp + logic/parser/codegen/PostgreSQLParser.cpp + logic/parser/codegen/PostgreSQLParserBaseListener.cpp + logic/parser/codegen/PostgreSQLParserBaseVisitor.cpp + logic/parser/codegen/PostgreSQLParserListener.cpp + logic/parser/codegen/PostgreSQLParserVisitor.cpp + logic/parser/PostgreSQLLexerBase.cpp + logic/parser/PostgreSQLParserBase.cpp +) +target_include_directories(sql PRIVATE + $ + $ ) - target_compile_features(sql PRIVATE cxx_std_20) set_target_properties(sql PROPERTIES CXX_STANDART 20 diff --git a/src/stewkk/sql/logic/llvm_ir_builder.cpp b/src/stewkk/sql/logic/llvm_ir_builder.cpp deleted file mode 100644 index 8f9982f..0000000 --- a/src/stewkk/sql/logic/llvm_ir_builder.cpp +++ /dev/null @@ -1,171 +0,0 @@ -#include - -#include - -namespace stewkk::sql::logic { - -Visitor::Visitor() - : llvm_context_(std::make_unique()), - ir_builder_( - std::make_unique>(*llvm_context_, llvm::NoFolder())), - ir_module_(std::make_unique("sql program", *llvm_context_)) { - llvm::FunctionType* ft = - llvm::FunctionType::get(llvm::Type::getInt32Ty(*llvm_context_), {}, false); - - llvm::Function* function = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, "main", ir_module_.get()); - - llvm::BasicBlock *bb = llvm::BasicBlock::Create(*llvm_context_, "entry", function); - - ir_builder_->SetInsertPoint(bb); -} - -std::any Visitor::visitInt(codegen::TParser::IntContext *ctx) { - auto i = ctx->INT(); - std::int32_t val = std::stoi(i->getText()); - return static_cast(llvm::ConstantInt::get(*llvm_context_, llvm::APInt(32, val))); -} - -std::any Visitor::visitIdent(codegen::TParser::IdentContext *ctx) { - auto name = ctx->ID()->getText(); - auto alloca = named_values_[name]; - return static_cast(ir_builder_->CreateLoad(alloca->getAllocatedType(), alloca, name)); -} - -std::any Visitor::visitFlowControl(codegen::TParser::FlowControlContext *ctx) { - if (ctx->control()->getText() == "if") { - llvm::Value *cond = std::any_cast(visit(ctx->cond())); - if (!cond) { - return nullptr; - } - - llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); - llvm::BasicBlock *then_bb = llvm::BasicBlock::Create(*llvm_context_, "then", function); - llvm::BasicBlock *merge_bb = llvm::BasicBlock::Create(*llvm_context_, "ifcont"); - ir_builder_->CreateCondBr(cond, then_bb, merge_bb); - - ir_builder_->SetInsertPoint(then_bb); - - for (auto stat : ctx->stat()) { - visit(stat); - } - - ir_builder_->CreateBr(merge_bb); - then_bb = ir_builder_->GetInsertBlock(); - - function->insert(function->end(), merge_bb); - ir_builder_->SetInsertPoint(merge_bb); - - return nullptr; - } else { - llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); - - llvm::BasicBlock *loop_bb = llvm::BasicBlock::Create(*llvm_context_, "loop", function); - llvm::BasicBlock *body_bb = llvm::BasicBlock::Create(*llvm_context_, "body", function); - llvm::BasicBlock *after_bb = llvm::BasicBlock::Create(*llvm_context_, "afterloop", function); - ir_builder_->CreateBr(loop_bb); - ir_builder_->SetInsertPoint(loop_bb); - - llvm::Value *cond = std::any_cast(visit(ctx->cond())); - if (!cond) { - return nullptr; - } - - ir_builder_->CreateCondBr(cond, body_bb, after_bb); - - ir_builder_->SetInsertPoint(body_bb); - - for (auto stat : ctx->stat()) { - visit(stat); - } - - ir_builder_->CreateBr(loop_bb); - - ir_builder_->SetInsertPoint(after_bb); - - return nullptr; - } -} - -llvm::AllocaInst* Visitor::CreateEntryBlockAlloca(llvm::Function *function, - llvm::StringRef varname) { - llvm::IRBuilder<> tmp(&function->getEntryBlock(), - function->getEntryBlock().begin()); - return tmp.CreateAlloca(llvm::Type::getInt32Ty(*llvm_context_), nullptr, varname); -} - -std::any Visitor::visitAssign(codegen::TParser::AssignContext *ctx) { - auto varname = ctx->ID()->getText(); - std::cerr << "visitAssign " << varname << std::endl; - llvm::Value* val = std::any_cast(visit(ctx->expr())); - if (!val) { - return nullptr; - } - - std::cerr << "got expr" << std::endl; - llvm::Function *function = ir_builder_->GetInsertBlock()->getParent(); - llvm::AllocaInst *alloca; - if (auto it = named_values_.find(varname); it != named_values_.end()) { - alloca = it->second; - } else { - alloca = CreateEntryBlockAlloca(function, varname); - } - ir_builder_->CreateStore(val, alloca); - - named_values_[varname] = alloca; - - return nullptr; -} - -std::any Visitor::visitCond(codegen::TParser::CondContext *ctx) { - llvm::Value *lhs = std::any_cast(visit(ctx->lhs)); - llvm::Value *rhs = std::any_cast(visit(ctx->rhs)); - - if (!lhs || !rhs) { - return nullptr; - } - - auto op = ctx->condOp()->getText(); - if (op == "<") { - return ir_builder_->CreateICmpSLE(lhs, rhs); - } else if (op == ">") { - return ir_builder_->CreateICmpSGE(lhs, rhs); - } else if (op == "==") { - return ir_builder_->CreateICmpEQ(lhs, rhs); - } else if (op == "!=") { - return ir_builder_->CreateICmpNE(lhs, rhs); - } - throw std::logic_error{"invalid binary operator"}; -} - -std::any Visitor::visitBinaryOp(codegen::TParser::BinaryOpContext *ctx) { - std::cerr << "binary op" << std::endl; - llvm::Value *lhs = std::any_cast(visit(ctx->lhs)); - llvm::Value *rhs = std::any_cast(visit(ctx->rhs)); - - if (!lhs || !rhs) { - return nullptr; - } - - // FIXME: dirty way to get op - auto op = ctx->op()->getText(); - if (op == "+") { - return ir_builder_->CreateAdd(lhs, rhs); - } else if (op == "-") { - return ir_builder_->CreateSub(lhs, rhs); - } else if (op == "*") { - return ir_builder_->CreateMul(lhs, rhs); - } - throw std::logic_error{"invalid binary operator"}; -} - -std::any Visitor::visitReturn(codegen::TParser::ReturnContext *ctx) { - llvm::Value* val = std::any_cast(visit(ctx->expr())); - ir_builder_->CreateRet(val); - return nullptr; -} - -const llvm::Module* Visitor::GetIr() const { - return ir_module_.get(); -} - -} // namespace stewkk::sql::logic diff --git a/src/stewkk/sql/logic/llvm_ir_builder.hpp b/src/stewkk/sql/logic/llvm_ir_builder.hpp deleted file mode 100644 index dba2faf..0000000 --- a/src/stewkk/sql/logic/llvm_ir_builder.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include - -namespace stewkk::sql::logic { - -class Visitor : public codegen::TParserBaseVisitor { -public: - Visitor(); - - virtual std::any visitInt(codegen::TParser::IntContext *ctx) override; - virtual std::any visitIdent(codegen::TParser::IdentContext *ctx) override; - virtual std::any visitBinaryOp(codegen::TParser::BinaryOpContext *ctx) override; - virtual std::any visitFlowControl(codegen::TParser::FlowControlContext *ctx) override; - virtual std::any visitAssign(codegen::TParser::AssignContext *ctx) override; - virtual std::any visitReturn(codegen::TParser::ReturnContext *ctx) override; - virtual std::any visitCond(codegen::TParser::CondContext *ctx) override; - - llvm::AllocaInst* CreateEntryBlockAlloca(llvm::Function *TheFunction, - llvm::StringRef VarName); - - const llvm::Module* GetIr() const; - -private: - std::unique_ptr llvm_context_; - std::unique_ptr> ir_builder_; - std::unique_ptr ir_module_; - std::map named_values_; -}; - -} // namespace stewkk::sql::logic diff --git a/src/stewkk/sql/logic/parser/PostgreSQLLexer.g4 b/src/stewkk/sql/logic/parser/PostgreSQLLexer.g4 new file mode 100644 index 0000000..1c71cd6 --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLLexer.g4 @@ -0,0 +1,1474 @@ +/* +based on +https://github.com/tunnelvisionlabs/antlr4-grammar-postgresql/blob/master/src/com/tunnelvisionlabs/postgresql/PostgreSqlLexer.g4 +*/ + +/* + * [The "MIT license"] + * Copyright (C) 2014 Sam Harwell, Tunnel Vision Laboratories, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * 3. Except as contained in this notice, the name of Tunnel Vision + * Laboratories, LLC. shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without prior + * written authorization from Tunnel Vision Laboratories, LLC. + */ + +// $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine +// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true + +lexer grammar PostgreSQLLexer; +/* Reference: + * http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html + */ + +options { + superClass = PostgreSQLLexerBase; + caseInsensitive = true; +} + +@header {#include "PostgreSQLLexerBase.h"} + +Dollar: '$'; + +OPEN_PAREN: '('; + +CLOSE_PAREN: ')'; + +OPEN_BRACKET: '['; + +CLOSE_BRACKET: ']'; + +COMMA: ','; + +SEMI: ';'; + +COLON: ':'; + +STAR: '*'; + +EQUAL: '='; + +DOT: '.'; +//NamedArgument : ':='; + +PLUS: '+'; + +MINUS: '-'; + +SLASH: '/'; + +CARET: '^'; + +LT: '<'; + +GT: '>'; + +LESS_LESS: '<<'; + +GREATER_GREATER: '>>'; + +COLON_EQUALS: ':='; + +LESS_EQUALS: '<='; + +EQUALS_GREATER: '=>'; + +GREATER_EQUALS: '>='; + +DOT_DOT: '..'; + +NOT_EQUALS: '<>'; + +TYPECAST: '::'; + +PERCENT: '%'; + +PARAM: '$' ([0-9])+; +// + +// OPERATORS (4.1.3) + +// + +// this rule does not allow + or - at the end of a multi-character operator + +Operator: + ( + ( + OperatorCharacter + | ('+' | '-' {this->CheckLaMinus()}? )+ (OperatorCharacter | '/' {this->CheckLaStar()}? ) + | '/' {this->CheckLaStar()}? + )+ + | // special handling for the single-character operators + and - + [+-] + ) + //TODO somehow rewrite this part without using Actions + {this->HandleLessLessGreaterGreater();} +; +/* This rule handles operators which end with + or -, and sets the token type to Operator. It is comprised of four + * parts, in order: + * + * 1. A prefix, which does not contain a character from the required set which allows + or - to appear at the end of + * the operator. + * 2. A character from the required set which allows + or - to appear at the end of the operator. + * 3. An optional sub-token which takes the form of an operator which does not include a + or - at the end of the + * sub-token. + * 4. A suffix sequence of + and - characters. + */ + +OperatorEndingWithPlusMinus: + (OperatorCharacterNotAllowPlusMinusAtEnd | '-' {this->CheckLaMinus()}? | '/' {this->CheckLaStar()}? )* OperatorCharacterAllowPlusMinusAtEnd Operator? ( + '+' + | '-' {this->CheckLaMinus()}? + )+ -> type (Operator) +; +// Each of the following fragment rules omits the +, -, and / characters, which must always be handled in a special way + +// by the operator rules above. + +fragment OperatorCharacter: [*<>=~!@%^&|`?#]; +// these are the operator characters that don't count towards one ending with + or - + +fragment OperatorCharacterNotAllowPlusMinusAtEnd: [*<>=+]; +// an operator may end with + or - if it contains one of these characters + +fragment OperatorCharacterAllowPlusMinusAtEnd: [~!@%^&|`?#]; +// + +// KEYWORDS (Appendix C) + + + +JSON: 'JSON'; +JSON_ARRAY: 'JSON_ARRAY'; +JSON_ARRAYAGG: 'JSON_ARRAYAGG'; +JSON_EXISTS: 'JSON_EXISTS'; +JSON_OBJECT: 'JSON_OBJECT'; +JSON_OBJECTAGG: 'JSON_OBJECTAGG'; +JSON_QUERY: 'JSON_QUERY'; +JSON_SCALAR: 'JSON_SCALAR'; +JSON_SERIALIZE: 'JSON_SERIALIZE'; +JSON_TABLE: 'JSON_TABLE'; +JSON_VALUE: 'JSON_VALUE'; +MERGE_ACTION: 'MERGE_ACTION'; + +SYSTEM_USER: 'SYSTEM_USER'; + +ABSENT: 'ABSENT'; +ASENSITIVE: 'ASENSITIVE'; +ATOMIC: 'ATOMIC'; +BREADTH: 'BREATH'; +COMPRESSION: 'COMPRESSION'; +CONDITIONAL: 'CONDITIONAL'; +DEPTH: 'DEPTH'; +EMPTY_P: 'EMPTY'; +FINALIZE: 'FINALIZE'; +INDENT: 'INDENT'; +KEEP: 'KEEP'; +KEYS: 'KEYS'; +NESTED: 'NESTED'; +OMIT: 'OMIT'; +PARAMETER: 'PARAMETER'; +PATH: 'PATH'; +PLAN: 'PLAN'; +QUOTES: 'QUOTES'; +SCALAR: 'SCALAR'; +SOURCE: 'SOURCE'; +STRING_P: 'STRING'; +TARGET: 'TARGET'; +UNCONDITIONAL: 'UNCONDITIONAL'; + +PERIOD: 'PERIOD'; + +FORMAT_LA: 'FORMAT_LA'; + +// + +// + +// reserved keywords + +// + +ALL: 'ALL'; + +ANALYSE: 'ANALYSE'; + +ANALYZE: 'ANALYZE'; + +AND: 'AND'; + +ANY: 'ANY'; + +ARRAY: 'ARRAY'; + +AS: 'AS'; + +ASC: 'ASC'; + +ASYMMETRIC: 'ASYMMETRIC'; + +BOTH: 'BOTH'; + +CASE: 'CASE'; + +CAST: 'CAST'; + +CHECK: 'CHECK'; + +COLLATE: 'COLLATE'; + +COLUMN: 'COLUMN'; + +CONSTRAINT: 'CONSTRAINT'; + +CREATE: 'CREATE'; + +CURRENT_CATALOG: 'CURRENT_CATALOG'; + +CURRENT_DATE: 'CURRENT_DATE'; + +CURRENT_ROLE: 'CURRENT_ROLE'; + +CURRENT_TIME: 'CURRENT_TIME'; + +CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'; + +CURRENT_USER: 'CURRENT_USER'; + +DEFAULT: 'DEFAULT'; + +DEFERRABLE: 'DEFERRABLE'; + +DESC: 'DESC'; + +DISTINCT: 'DISTINCT'; + +DO: 'DO'; + +ELSE: 'ELSE'; + +EXCEPT: 'EXCEPT'; + +FALSE_P: 'FALSE'; + +FETCH: 'FETCH'; + +FOR: 'FOR'; + +FOREIGN: 'FOREIGN'; + +FROM: 'FROM'; + +GRANT: 'GRANT'; + +GROUP_P: 'GROUP'; + +HAVING: 'HAVING'; + +IN_P: 'IN'; + +INITIALLY: 'INITIALLY'; + +INTERSECT: 'INTERSECT'; + +INTO: 'INTO'; + +LATERAL_P: 'LATERAL'; + +LEADING: 'LEADING'; + +LIMIT: 'LIMIT'; + +LOCALTIME: 'LOCALTIME'; + +LOCALTIMESTAMP: 'LOCALTIMESTAMP'; + +NOT: 'NOT'; + +NULL_P: 'NULL'; + +OFFSET: 'OFFSET'; + +ON: 'ON'; + +ONLY: 'ONLY'; + +OR: 'OR'; + +ORDER: 'ORDER'; + +PLACING: 'PLACING'; + +PRIMARY: 'PRIMARY'; + +REFERENCES: 'REFERENCES'; + +RETURNING: 'RETURNING'; + +SELECT: 'SELECT'; + +SESSION_USER: 'SESSION_USER'; + +SOME: 'SOME'; + +SYMMETRIC: 'SYMMETRIC'; + +TABLE: 'TABLE'; + +THEN: 'THEN'; + +TO: 'TO'; + +TRAILING: 'TRAILING'; + +TRUE_P: 'TRUE'; + +UNION: 'UNION'; + +UNIQUE: 'UNIQUE'; + +USER: 'USER'; + +USING: 'USING'; + +VARIADIC: 'VARIADIC'; + +WHEN: 'WHEN'; + +WHERE: 'WHERE'; + +WINDOW: 'WINDOW'; + +WITH: 'WITH'; + +// + +// reserved keywords (can be function or type) + +// + +AUTHORIZATION: 'AUTHORIZATION'; + +BINARY: 'BINARY'; + +COLLATION: 'COLLATION'; + +CONCURRENTLY: 'CONCURRENTLY'; + +CROSS: 'CROSS'; + +CURRENT_SCHEMA: 'CURRENT_SCHEMA'; + +FREEZE: 'FREEZE'; + +FULL: 'FULL'; + +ILIKE: 'ILIKE'; + +INNER_P: 'INNER'; + +IS: 'IS'; + +ISNULL: 'ISNULL'; + +JOIN: 'JOIN'; + +LEFT: 'LEFT'; + +LIKE: 'LIKE'; + +NATURAL: 'NATURAL'; + +NOTNULL: 'NOTNULL'; + +OUTER_P: 'OUTER'; + +OVER: 'OVER'; + +OVERLAPS: 'OVERLAPS'; + +RIGHT: 'RIGHT'; + +SIMILAR: 'SIMILAR'; + +VERBOSE: 'VERBOSE'; +// + +// non-reserved keywords + +// + +ABORT_P: 'ABORT'; + +ABSOLUTE_P: 'ABSOLUTE'; + +ACCESS: 'ACCESS'; + +ACTION: 'ACTION'; + +ADD_P: 'ADD'; + +ADMIN: 'ADMIN'; + +AFTER: 'AFTER'; + +AGGREGATE: 'AGGREGATE'; + +ALSO: 'ALSO'; + +ALTER: 'ALTER'; + +ALWAYS: 'ALWAYS'; + +ASSERTION: 'ASSERTION'; + +ASSIGNMENT: 'ASSIGNMENT'; + +AT: 'AT'; + +ATTRIBUTE: 'ATTRIBUTE'; + +BACKWARD: 'BACKWARD'; + +BEFORE: 'BEFORE'; + +BEGIN_P: 'BEGIN'; + +BY: 'BY'; + +CACHE: 'CACHE'; + +CALLED: 'CALLED'; + +CASCADE: 'CASCADE'; + +CASCADED: 'CASCADED'; + +CATALOG: 'CATALOG'; + +CHAIN: 'CHAIN'; + +CHARACTERISTICS: 'CHARACTERISTICS'; + +CHECKPOINT: 'CHECKPOINT'; + +CLASS: 'CLASS'; + +CLOSE: 'CLOSE'; + +CLUSTER: 'CLUSTER'; + +COMMENT: 'COMMENT'; + +COMMENTS: 'COMMENTS'; + +COMMIT: 'COMMIT'; + +COMMITTED: 'COMMITTED'; + +CONFIGURATION: 'CONFIGURATION'; + +CONNECTION: 'CONNECTION'; + +CONSTRAINTS: 'CONSTRAINTS'; + +CONTENT_P: 'CONTENT'; + +CONTINUE_P: 'CONTINUE'; + +CONVERSION_P: 'CONVERSION'; + +COPY: 'COPY'; + +COST: 'COST'; + +CSV: 'CSV'; + +CURSOR: 'CURSOR'; + +CYCLE: 'CYCLE'; + +DATA_P: 'DATA'; + +DATABASE: 'DATABASE'; + +DAY_P: 'DAY'; + +DEALLOCATE: 'DEALLOCATE'; + +DECLARE: 'DECLARE'; + +DEFAULTS: 'DEFAULTS'; + +DEFERRED: 'DEFERRED'; + +DEFINER: 'DEFINER'; + +DELETE_P: 'DELETE'; + +DELIMITER: 'DELIMITER'; + +DELIMITERS: 'DELIMITERS'; + +DICTIONARY: 'DICTIONARY'; + +DISABLE_P: 'DISABLE'; + +DISCARD: 'DISCARD'; + +DOCUMENT_P: 'DOCUMENT'; + +DOMAIN_P: 'DOMAIN'; + +DOUBLE_P: 'DOUBLE'; + +DROP: 'DROP'; + +EACH: 'EACH'; + +ENABLE_P: 'ENABLE'; + +ENCODING: 'ENCODING'; + +ENCRYPTED: 'ENCRYPTED'; + +ENUM_P: 'ENUM'; + +ESCAPE: 'ESCAPE'; + +EVENT: 'EVENT'; + +EXCLUDE: 'EXCLUDE'; + +EXCLUDING: 'EXCLUDING'; + +EXCLUSIVE: 'EXCLUSIVE'; + +EXECUTE: 'EXECUTE'; + +EXPLAIN: 'EXPLAIN'; + +EXTENSION: 'EXTENSION'; + +EXTERNAL: 'EXTERNAL'; + +FAMILY: 'FAMILY'; + +FIRST_P: 'FIRST'; + +FOLLOWING: 'FOLLOWING'; + +FORCE: 'FORCE'; + +FORWARD: 'FORWARD'; + +FUNCTION: 'FUNCTION'; + +FUNCTIONS: 'FUNCTIONS'; + +GLOBAL: 'GLOBAL'; + +GRANTED: 'GRANTED'; + +HANDLER: 'HANDLER'; + +HEADER_P: 'HEADER'; + +HOLD: 'HOLD'; + +HOUR_P: 'HOUR'; + +IDENTITY_P: 'IDENTITY'; + +IF_P: 'IF'; + +IMMEDIATE: 'IMMEDIATE'; + +IMMUTABLE: 'IMMUTABLE'; + +IMPLICIT_P: 'IMPLICIT'; + +INCLUDING: 'INCLUDING'; + +INCREMENT: 'INCREMENT'; + +INDEX: 'INDEX'; + +INDEXES: 'INDEXES'; + +INHERIT: 'INHERIT'; + +INHERITS: 'INHERITS'; + +INLINE_P: 'INLINE'; + +INSENSITIVE: 'INSENSITIVE'; + +INSERT: 'INSERT'; + +INSTEAD: 'INSTEAD'; + +INVOKER: 'INVOKER'; + +ISOLATION: 'ISOLATION'; + +KEY: 'KEY'; + +LABEL: 'LABEL'; + +LANGUAGE: 'LANGUAGE'; + +LARGE_P: 'LARGE'; + +LAST_P: 'LAST'; +//LC_COLLATE : 'LC'_'COLLATE; + +//LC_CTYPE : 'LC'_'CTYPE; + +LEAKPROOF: 'LEAKPROOF'; + +LEVEL: 'LEVEL'; + +LISTEN: 'LISTEN'; + +LOAD: 'LOAD'; + +LOCAL: 'LOCAL'; + +LOCATION: 'LOCATION'; + +LOCK_P: 'LOCK'; + +MAPPING: 'MAPPING'; + +MATCH: 'MATCH'; + +MATCHED: 'MATCHED'; + +MATERIALIZED: 'MATERIALIZED'; + +MAXVALUE: 'MAXVALUE'; + +MERGE: 'MERGE'; + +MINUTE_P: 'MINUTE'; + +MINVALUE: 'MINVALUE'; + +MODE: 'MODE'; + +MONTH_P: 'MONTH'; + +MOVE: 'MOVE'; + +NAME_P: 'NAME'; + +NAMES: 'NAMES'; + +NEXT: 'NEXT'; + +NO: 'NO'; + +NOTHING: 'NOTHING'; + +NOTIFY: 'NOTIFY'; + +NOWAIT: 'NOWAIT'; + +NULLS_P: 'NULLS'; + +OBJECT_P: 'OBJECT'; + +OF: 'OF'; + +OFF: 'OFF'; + +OIDS: 'OIDS'; + +OPERATOR: 'OPERATOR'; + +OPTION: 'OPTION'; + +OPTIONS: 'OPTIONS'; + +OWNED: 'OWNED'; + +OWNER: 'OWNER'; + +PARSER: 'PARSER'; + +PARTIAL: 'PARTIAL'; + +PARTITION: 'PARTITION'; + +PASSING: 'PASSING'; + +PASSWORD: 'PASSWORD'; + +PLANS: 'PLANS'; + +PRECEDING: 'PRECEDING'; + +PREPARE: 'PREPARE'; + +PREPARED: 'PREPARED'; + +PRESERVE: 'PRESERVE'; + +PRIOR: 'PRIOR'; + +PRIVILEGES: 'PRIVILEGES'; + +PROCEDURAL: 'PROCEDURAL'; + +PROCEDURE: 'PROCEDURE'; + +PROGRAM: 'PROGRAM'; + +QUOTE: 'QUOTE'; + +RANGE: 'RANGE'; + +READ: 'READ'; + +REASSIGN: 'REASSIGN'; + +RECHECK: 'RECHECK'; + +RECURSIVE: 'RECURSIVE'; + +REF: 'REF'; + +REFRESH: 'REFRESH'; + +REINDEX: 'REINDEX'; + +RELATIVE_P: 'RELATIVE'; + +RELEASE: 'RELEASE'; + +RENAME: 'RENAME'; + +REPEATABLE: 'REPEATABLE'; + +REPLACE: 'REPLACE'; + +REPLICA: 'REPLICA'; + +RESET: 'RESET'; + +RESTART: 'RESTART'; + +RESTRICT: 'RESTRICT'; + +RETURNS: 'RETURNS'; + +REVOKE: 'REVOKE'; + +ROLE: 'ROLE'; + +ROLLBACK: 'ROLLBACK'; + +ROWS: 'ROWS'; + +RULE: 'RULE'; + +SAVEPOINT: 'SAVEPOINT'; + +SCHEMA: 'SCHEMA'; + +SCROLL: 'SCROLL'; + +SEARCH: 'SEARCH'; + +SECOND_P: 'SECOND'; + +SECURITY: 'SECURITY'; + +SEQUENCE: 'SEQUENCE'; + +SEQUENCES: 'SEQUENCES'; + +SERIALIZABLE: 'SERIALIZABLE'; + +SERVER: 'SERVER'; + +SESSION: 'SESSION'; + +SET: 'SET'; + +SHARE: 'SHARE'; + +SHOW: 'SHOW'; + +SIMPLE: 'SIMPLE'; + +SNAPSHOT: 'SNAPSHOT'; + +STABLE: 'STABLE'; + +STANDALONE_P: 'STANDALONE'; + +START: 'START'; + +STATEMENT: 'STATEMENT'; + +STATISTICS: 'STATISTICS'; + +STDIN: 'STDIN'; + +STDOUT: 'STDOUT'; + +STORAGE: 'STORAGE'; + +STRICT_P: 'STRICT'; + +STRIP_P: 'STRIP'; + +SYSID: 'SYSID'; + +SYSTEM_P: 'SYSTEM'; + +TABLES: 'TABLES'; + +TABLESPACE: 'TABLESPACE'; + +TEMP: 'TEMP'; + +TEMPLATE: 'TEMPLATE'; + +TEMPORARY: 'TEMPORARY'; + +TEXT_P: 'TEXT'; + +TRANSACTION: 'TRANSACTION'; + +TRIGGER: 'TRIGGER'; + +TRUNCATE: 'TRUNCATE'; + +TRUSTED: 'TRUSTED'; + +TYPE_P: 'TYPE'; + +TYPES_P: 'TYPES'; + +UNBOUNDED: 'UNBOUNDED'; + +UNCOMMITTED: 'UNCOMMITTED'; + +UNENCRYPTED: 'UNENCRYPTED'; + +UNKNOWN: 'UNKNOWN'; + +UNLISTEN: 'UNLISTEN'; + +UNLOGGED: 'UNLOGGED'; + +UNTIL: 'UNTIL'; + +UPDATE: 'UPDATE'; + +VACUUM: 'VACUUM'; + +VALID: 'VALID'; + +VALIDATE: 'VALIDATE'; + +VALIDATOR: 'VALIDATOR'; +//VALUE : 'VALUE; + +VARYING: 'VARYING'; + +VERSION_P: 'VERSION'; + +VIEW: 'VIEW'; + +VOLATILE: 'VOLATILE'; + +WHITESPACE_P: 'WHITESPACE'; + +WITHOUT: 'WITHOUT'; + +WORK: 'WORK'; + +WRAPPER: 'WRAPPER'; + +WRITE: 'WRITE'; + +XML_P: 'XML'; + +YEAR_P: 'YEAR'; + +YES_P: 'YES'; + +ZONE: 'ZONE'; +// + +// non-reserved keywords (can not be function or type) + +// + +BETWEEN: 'BETWEEN'; + +BIGINT: 'BIGINT'; + +BIT: 'BIT'; + +BOOLEAN_P: 'BOOLEAN'; + +CHAR_P: 'CHAR'; + +CHARACTER: 'CHARACTER'; + +COALESCE: 'COALESCE'; + +DEC: 'DEC'; + +DECIMAL_P: 'DECIMAL'; + +EXISTS: 'EXISTS'; + +EXTRACT: 'EXTRACT'; + +FLOAT_P: 'FLOAT'; + +GREATEST: 'GREATEST'; + +INOUT: 'INOUT'; + +INT_P: 'INT'; + +INTEGER: 'INTEGER'; + +INTERVAL: 'INTERVAL'; + +LEAST: 'LEAST'; + +NATIONAL: 'NATIONAL'; + +NCHAR: 'NCHAR'; + +NONE: 'NONE'; + +NULLIF: 'NULLIF'; + +NUMERIC: 'NUMERIC'; + +OVERLAY: 'OVERLAY'; + +POSITION: 'POSITION'; + +PRECISION: 'PRECISION'; + +REAL: 'REAL'; + +ROW: 'ROW'; + +SETOF: 'SETOF'; + +SMALLINT: 'SMALLINT'; + +SUBSTRING: 'SUBSTRING'; + +TIME: 'TIME'; + +TIMESTAMP: 'TIMESTAMP'; + +TREAT: 'TREAT'; + +TRIM: 'TRIM'; + +VALUES: 'VALUES'; + +VARCHAR: 'VARCHAR'; + +XMLATTRIBUTES: 'XMLATTRIBUTES'; + +XMLCOMMENT: 'XMLCOMMENT'; + +XMLAGG: 'XMLAGG'; + +XML_IS_WELL_FORMED: 'XML_IS_WELL_FORMED'; + +XML_IS_WELL_FORMED_DOCUMENT: 'XML_IS_WELL_FORMED_DOCUMENT'; + +XML_IS_WELL_FORMED_CONTENT: 'XML_IS_WELL_FORMED_CONTENT'; + +XPATH: 'XPATH'; + +XPATH_EXISTS: 'XPATH_EXISTS'; + +XMLCONCAT: 'XMLCONCAT'; + +XMLELEMENT: 'XMLELEMENT'; + +XMLEXISTS: 'XMLEXISTS'; + +XMLFOREST: 'XMLFOREST'; + +XMLPARSE: 'XMLPARSE'; + +XMLPI: 'XMLPI'; + +XMLROOT: 'XMLROOT'; + +XMLSERIALIZE: 'XMLSERIALIZE'; +//MISSED + +CALL: 'CALL'; + +CURRENT_P: 'CURRENT'; + +ATTACH: 'ATTACH'; + +DETACH: 'DETACH'; + +EXPRESSION: 'EXPRESSION'; + +GENERATED: 'GENERATED'; + +LOGGED: 'LOGGED'; + +STORED: 'STORED'; + +INCLUDE: 'INCLUDE'; + +ROUTINE: 'ROUTINE'; + +TRANSFORM: 'TRANSFORM'; + +IMPORT_P: 'IMPORT'; + +POLICY: 'POLICY'; + +METHOD: 'METHOD'; + +REFERENCING: 'REFERENCING'; + +NEW: 'NEW'; + +OLD: 'OLD'; + +VALUE_P: 'VALUE'; + +SUBSCRIPTION: 'SUBSCRIPTION'; + +PUBLICATION: 'PUBLICATION'; + +OUT_P: 'OUT'; + +END_P: 'END'; + +ROUTINES: 'ROUTINES'; + +SCHEMAS: 'SCHEMAS'; + +PROCEDURES: 'PROCEDURES'; + +INPUT_P: 'INPUT'; + +SUPPORT: 'SUPPORT'; + +PARALLEL: 'PARALLEL'; + +SQL_P: 'SQL'; + +DEPENDS: 'DEPENDS'; + +OVERRIDING: 'OVERRIDING'; + +CONFLICT: 'CONFLICT'; + +SKIP_P: 'SKIP'; + +LOCKED: 'LOCKED'; + +TIES: 'TIES'; + +ROLLUP: 'ROLLUP'; + +CUBE: 'CUBE'; + +GROUPING: 'GROUPING'; + +SETS: 'SETS'; + +TABLESAMPLE: 'TABLESAMPLE'; + +ORDINALITY: 'ORDINALITY'; + +XMLTABLE: 'XMLTABLE'; + +COLUMNS: 'COLUMNS'; + +XMLNAMESPACES: 'XMLNAMESPACES'; + +ROWTYPE: 'ROWTYPE'; + +NORMALIZED: 'NORMALIZED'; + +WITHIN: 'WITHIN'; + +FILTER: 'FILTER'; + +GROUPS: 'GROUPS'; + +OTHERS: 'OTHERS'; + +NFC: 'NFC'; + +NFD: 'NFD'; + +NFKC: 'NFKC'; + +NFKD: 'NFKD'; + +UESCAPE: 'UESCAPE'; + +VIEWS: 'VIEWS'; + +NORMALIZE: 'NORMALIZE'; + +DUMP: 'DUMP'; + +ERROR: 'ERROR'; + +USE_VARIABLE: 'USE_VARIABLE'; + +USE_COLUMN: 'USE_COLUMN'; + +CONSTANT: 'CONSTANT'; + +PERFORM: 'PERFORM'; + +GET: 'GET'; + +DIAGNOSTICS: 'DIAGNOSTICS'; + +STACKED: 'STACKED'; + +ELSIF: 'ELSIF'; + +WHILE: 'WHILE'; + +FOREACH: 'FOREACH'; + +SLICE: 'SLICE'; + +EXIT: 'EXIT'; + +RETURN: 'RETURN'; + +RAISE: 'RAISE'; + +SQLSTATE: 'SQLSTATE'; + +DEBUG: 'DEBUG'; + +INFO: 'INFO'; + +NOTICE: 'NOTICE'; + +WARNING: 'WARNING'; + +EXCEPTION: 'EXCEPTION'; + +ASSERT: 'ASSERT'; + +LOOP: 'LOOP'; + +OPEN: 'OPEN'; + +FORMAT: 'FORMAT'; + + + + + +Identifier: IdentifierStartChar IdentifierChar*; + +fragment IdentifierStartChar options { + caseInsensitive = false; +}: // these are the valid identifier start characters below 0x7F + [a-zA-Z_] + | // these are the valid characters from 0x80 to 0xFF + [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF] + | // these are the letters above 0xFF which only need a single UTF-16 code unit + [\u0100-\uD7FF\uE000-\uFFFF] {this->CharIsLetter()}? + | // letters which require multiple UTF-16 code units + [\uD800-\uDBFF] [\uDC00-\uDFFF] {this->CheckIfUtf32Letter()}? +; + +fragment IdentifierChar: StrictIdentifierChar | '$'; + +fragment StrictIdentifierChar: IdentifierStartChar | [0-9]; +/* Quoted Identifiers + * + * These are divided into four separate tokens, allowing distinction of valid quoted identifiers from invalid quoted + * identifiers without sacrificing the ability of the lexer to reliably recover from lexical errors in the input. + */ + +QuotedIdentifier: UnterminatedQuotedIdentifier '"'; +// This is a quoted identifier which only contains valid characters but is not terminated + +UnterminatedQuotedIdentifier: '"' ('""' | ~ [\u0000"])*; +// This is a quoted identifier which is terminated but contains a \u0000 character + +InvalidQuotedIdentifier: InvalidUnterminatedQuotedIdentifier '"'; +// This is a quoted identifier which is unterminated and contains a \u0000 character + +InvalidUnterminatedQuotedIdentifier: '"' ('""' | ~ '"')*; +/* Unicode Quoted Identifiers + * + * These are divided into four separate tokens, allowing distinction of valid Unicode quoted identifiers from invalid + * Unicode quoted identifiers without sacrificing the ability of the lexer to reliably recover from lexical errors in + * the input. Note that escape sequences are never checked as part of this determination due to the ability of users + * to change the escape character with a UESCAPE clause following the Unicode quoted identifier. + * + * TODO: these rules assume "" is still a valid escape sequence within a Unicode quoted identifier. + */ + +UnicodeQuotedIdentifier: 'U' '&' QuotedIdentifier; +// This is a Unicode quoted identifier which only contains valid characters but is not terminated + +UnterminatedUnicodeQuotedIdentifier: 'U' '&' UnterminatedQuotedIdentifier; +// This is a Unicode quoted identifier which is terminated but contains a \u0000 character + +InvalidUnicodeQuotedIdentifier: 'U' '&' InvalidQuotedIdentifier; +// This is a Unicode quoted identifier which is unterminated and contains a \u0000 character + +InvalidUnterminatedUnicodeQuotedIdentifier: 'U' '&' InvalidUnterminatedQuotedIdentifier; +// + +// CONSTANTS (4.1.2) + +// + +// String Constants (4.1.2.1) + +StringConstant: UnterminatedStringConstant '\''; + +UnterminatedStringConstant: '\'' ('\'\'' | ~ '\'')*; +// String Constants with C-style Escapes (4.1.2.2) + +BeginEscapeStringConstant: 'E' '\'' -> more, pushMode (EscapeStringConstantMode); +// String Constants with Unicode Escapes (4.1.2.3) + +// + +// Note that escape sequences are never checked as part of this token due to the ability of users to change the escape + +// character with a UESCAPE clause following the Unicode string constant. + +// + +// TODO: these rules assume '' is still a valid escape sequence within a Unicode string constant. + +UnicodeEscapeStringConstant: UnterminatedUnicodeEscapeStringConstant '\''; + +UnterminatedUnicodeEscapeStringConstant: 'U' '&' UnterminatedStringConstant; +// Dollar-quoted String Constants (4.1.2.4) + +BeginDollarStringConstant: '$' Tag? '$' {this->PushTag();} -> pushMode (DollarQuotedStringMode); +/* "The tag, if any, of a dollar-quoted string follows the same rules as an + * unquoted identifier, except that it cannot contain a dollar sign." + */ + +fragment Tag: IdentifierStartChar StrictIdentifierChar*; +// Bit-strings Constants (4.1.2.5) + +BinaryStringConstant: UnterminatedBinaryStringConstant '\''; + +UnterminatedBinaryStringConstant: 'B' '\'' [01]*; + +InvalidBinaryStringConstant: InvalidUnterminatedBinaryStringConstant '\''; + +InvalidUnterminatedBinaryStringConstant: 'B' UnterminatedStringConstant; + +HexadecimalStringConstant: UnterminatedHexadecimalStringConstant '\''; + +UnterminatedHexadecimalStringConstant: 'X' '\'' [0-9A-F]*; + +InvalidHexadecimalStringConstant: InvalidUnterminatedHexadecimalStringConstant '\''; + +InvalidUnterminatedHexadecimalStringConstant: 'X' UnterminatedStringConstant; +// Numeric Constants (4.1.2.6) + +Integral: Digits; + +BinaryIntegral: '0b' Digits; + +OctalIntegral: '0o' Digits; + +HexadecimalIntegral: '0x' Digits; + +NumericFail: Digits '..' {this->HandleNumericFail();}; + +Numeric: + Digits '.' Digits? /*? replaced with + to solve problem with DOT_DOT .. but this surely must be rewriten */ ( + 'E' [+-]? Digits + )? + | '.' Digits ('E' [+-]? Digits)? + | Digits 'E' [+-]? Digits +; + +fragment Digits: [0-9]+; + +PLSQLVARIABLENAME: ':' [A-Z_] [A-Z_0-9$]*; + +PLSQLIDENTIFIER: ':"' ('\\' . | '""' | ~ ('"' | '\\'))* '"'; +// + +// WHITESPACE (4.1) + +// + +Whitespace: [ \t]+ -> channel (HIDDEN); + +Newline: ('\r' '\n'? | '\n') -> channel (HIDDEN); +// + +// COMMENTS (4.1.5) + +// + +LineComment: '--' ~ [\r\n]* -> channel (HIDDEN); + +BlockComment: + ('/*' ('/'* BlockComment | ~ [/*] | '/'+ ~ [/*] | '*'+ ~ [/*])* '*'* '*/') -> channel (HIDDEN) +; + +UnterminatedBlockComment: + '/*' ( + '/'* BlockComment + | // these characters are not part of special sequences in a block comment + ~ [/*] + | // handle / or * characters which are not part of /* or */ and do not appear at the end of the file + ('/'+ ~ [/*] | '*'+ ~ [/*]) + )* + // Handle the case of / or * characters at the end of the file, or a nested unterminated block comment + ('/'+ | '*'+ | '/'* UnterminatedBlockComment)? + // Optional assertion to make sure this rule is working as intended + {this->UnterminatedBlockCommentDebugAssert();} +; +// + +// META-COMMANDS + +// + +// http://www.postgresql.org/docs/9.3/static/app-psql.html + +MetaCommand: '\\' -> pushMode(META), more ; + +// + +// ERROR + +// + +// Any character which does not match one of the above rules will appear in the token stream as an ErrorCharacter token. + +// This ensures the lexer itself will never encounter a syntax error, so all error handling may be performed by the + +// parser. + +ErrorCharacter: .; + +mode EscapeStringConstantMode; +EscapeStringConstant: EscapeStringText '\'' -> mode (AfterEscapeStringConstantMode); + +UnterminatedEscapeStringConstant: + EscapeStringText + // Handle a final unmatched \ character appearing at the end of the file + '\\'? EOF +; + +fragment EscapeStringText options { caseInsensitive = false; }: + ( + '\'\'' + | '\\' ( + // two-digit hex escapes are still valid when treated as single-digit escapes + 'x' [0-9a-fA-F] + | 'u' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] + | 'U' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] + | // Any character other than the Unicode escapes can follow a backslash. Some have special meaning, + // but that doesn't affect the syntax. + ~ [xuU] + ) + | ~ ['\\] + )* +; + +InvalidEscapeStringConstant: InvalidEscapeStringText '\'' -> mode (AfterEscapeStringConstantMode); + +InvalidUnterminatedEscapeStringConstant: + InvalidEscapeStringText + // Handle a final unmatched \ character appearing at the end of the file + '\\'? EOF +; + +fragment InvalidEscapeStringText: ('\'\'' | '\\' . | ~ ['\\])*; + +mode AfterEscapeStringConstantMode; +AfterEscapeStringConstantMode_Whitespace: Whitespace -> type (Whitespace), channel (HIDDEN); + +AfterEscapeStringConstantMode_Newline: + Newline -> type (Newline), channel (HIDDEN), mode (AfterEscapeStringConstantWithNewlineMode) +; + +AfterEscapeStringConstantMode_NotContinued: + -> skip, popMode +; + +mode AfterEscapeStringConstantWithNewlineMode; +AfterEscapeStringConstantWithNewlineMode_Whitespace: + Whitespace -> type (Whitespace), channel (HIDDEN) +; + +AfterEscapeStringConstantWithNewlineMode_Newline: Newline -> type (Newline), channel (HIDDEN); + +AfterEscapeStringConstantWithNewlineMode_Continued: + '\'' -> more, mode (EscapeStringConstantMode) +; + +AfterEscapeStringConstantWithNewlineMode_NotContinued: + -> skip, popMode +; + +mode DollarQuotedStringMode; +DollarText: + ~ '$'+ + //| '$'([0-9])+ + | // this alternative improves the efficiency of handling $ characters within a dollar-quoted string which are + + // not part of the ending tag. + '$' ~ '$'* +; + +// NB: Next rule on two lines in order to make transformGrammar.py easy. +EndDollarStringConstant: ('$' Tag? '$') {this->IsTag()}? + {this->PopTag();} -> popMode; + +mode META; +MetaSemi : {this->IsSemiColon()}? ';' -> type(SEMI), popMode ; +MetaOther : ~[;\r\n\\"] .*? ('\\\\' | [\r\n]+) -> type(SEMI), popMode ; diff --git a/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.cpp b/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.cpp new file mode 100644 index 0000000..13f1dfd --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.cpp @@ -0,0 +1,81 @@ +#include "antlr4-runtime.h" +#include "PostgreSQLLexerBase.h" +#include "PostgreSQLLexer.h" +#include +#include +#include + +PostgreSQLLexerBase::PostgreSQLLexerBase(antlr4::CharStream * input) : antlr4::Lexer(input) +{ + _input = input; +} + +void PostgreSQLLexerBase::PushTag() +{ + tags.push(this->getText()); +} + +bool PostgreSQLLexerBase::IsTag() +{ + return this->getText() == tags.top(); +} + +void PostgreSQLLexerBase::PopTag() +{ + tags.pop(); +} + +void PostgreSQLLexerBase::UnterminatedBlockCommentDebugAssert() +{ +} + +bool PostgreSQLLexerBase::CheckLaMinus() +{ + return this->getInputStream()->LA(1) != '-'; +} + +bool PostgreSQLLexerBase::CheckLaStar() +{ + return this->getInputStream()->LA(1) != '*'; +} + +bool PostgreSQLLexerBase::CharIsLetter() +{ + return std::iswalpha(static_cast(this->getInputStream()->LA(-1))); +} + +void PostgreSQLLexerBase::HandleNumericFail() +{ + this->getInputStream()->seek(this->getInputStream()->index() - 2); + this->setType(stewkk::sql::codegen::PostgreSQLLexer::Integral); +} + +void PostgreSQLLexerBase::HandleLessLessGreaterGreater() +{ + if (this->getText() == "<<") this->setType(stewkk::sql::codegen::PostgreSQLLexer::LESS_LESS); + if (this->getText() == ">>") this->setType(stewkk::sql::codegen::PostgreSQLLexer::GREATER_GREATER); +} + + +char32_t surrogate_to_utf32(char16_t high, char16_t low) +{ + return (high << 10) + low - 0x35fdc00; +} + +int toCodePoint(int high, int low) +{ + return surrogate_to_utf32(high, low); +} + + +bool PostgreSQLLexerBase::CheckIfUtf32Letter() +{ + char high = static_cast(this->getInputStream()->LA(-2)); + char low = static_cast(this->getInputStream()->LA(-1)); + return std::iswalpha(toCodePoint(high, low)); +} + +bool PostgreSQLLexerBase::IsSemiColon() +{ + return ';' == (char)this->getInputStream()->LA(1); +} diff --git a/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.h b/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.h new file mode 100644 index 0000000..d55d507 --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLLexerBase.h @@ -0,0 +1,24 @@ +#pragma once +#include "antlr4-runtime.h" +#include +#include + +class PostgreSQLLexerBase : public antlr4::Lexer +{ + public: + PostgreSQLLexerBase(antlr4::CharStream * input); + void PushTag(); + bool IsTag(); + void PopTag(); + void UnterminatedBlockCommentDebugAssert(); + bool CheckLaMinus(); + bool CheckLaStar(); + bool CharIsLetter(); + void HandleNumericFail(); + void HandleLessLessGreaterGreater(); + bool CheckIfUtf32Letter(); + bool IsSemiColon(); + private: + std::stack tags; + +}; diff --git a/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 new file mode 100644 index 0000000..cb1b6bc --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 @@ -0,0 +1,5464 @@ +/* +PostgreSQL grammar. +The MIT License (MIT). +Copyright (c) 2021-2023, Oleksii Kovalov (Oleksii.Kovalov@outlook.com). +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +parser grammar PostgreSQLParser; + +options { + tokenVocab = PostgreSQLLexer; + superClass = PostgreSQLParserBase; +} + +@header {#include "PostgreSQLParserBase.h"} + +root + : stmtblock EOF + ; + +stmtblock + : stmtmulti + ; + +stmtmulti + : stmt? (SEMI stmt?)* + ; + +stmt + : altereventtrigstmt + | altercollationstmt + | alterdatabasestmt + | alterdatabasesetstmt + | alterdefaultprivilegesstmt + | alterdomainstmt + | alterenumstmt + | alterextensionstmt + | alterextensioncontentsstmt + | alterfdwstmt + | alterforeignserverstmt + | alterfunctionstmt + | altergroupstmt + | alterobjectdependsstmt + | alterobjectschemastmt + | alterownerstmt + | alteroperatorstmt + | altertypestmt + | alterpolicystmt + | alterseqstmt + | altersystemstmt + | altertablestmt + | altertblspcstmt + | altercompositetypestmt + | alterpublicationstmt + | alterrolesetstmt + | alterrolestmt + | altersubscriptionstmt + | alterstatsstmt + | altertsconfigurationstmt + | altertsdictionarystmt + | alterusermappingstmt + | analyzestmt + | callstmt + | checkpointstmt + | closeportalstmt + | clusterstmt + | commentstmt + | constraintssetstmt + | copystmt + | createamstmt + | createasstmt + | createassertionstmt + | createcaststmt + | createconversionstmt + | createdomainstmt + | createextensionstmt + | createfdwstmt + | createforeignserverstmt + | createforeigntablestmt + | createfunctionstmt + | creategroupstmt + | creatematviewstmt + | createopclassstmt + | createopfamilystmt + | createpublicationstmt + | alteropfamilystmt + | createpolicystmt + | createplangstmt + | createschemastmt + | createseqstmt + | createstmt + | createsubscriptionstmt + | createstatsstmt + | createtablespacestmt + | createtransformstmt + | createtrigstmt + | createeventtrigstmt + | createrolestmt + | createuserstmt + | createusermappingstmt + | createdbstmt + | deallocatestmt + | declarecursorstmt + | definestmt + | deletestmt + | discardstmt + | dostmt + | dropcaststmt + | dropopclassstmt + | dropopfamilystmt + | dropownedstmt + | dropstmt + | dropsubscriptionstmt + | droptablespacestmt + | droptransformstmt + | droprolestmt + | dropusermappingstmt + | dropdbstmt + | executestmt + | explainstmt + | fetchstmt + | grantstmt + | grantrolestmt + | importforeignschemastmt + | indexstmt + | insertstmt + | mergestmt + | listenstmt + | refreshmatviewstmt + | loadstmt + | lockstmt + | notifystmt + | preparestmt + | reassignownedstmt + | reindexstmt + | removeaggrstmt + | removefuncstmt + | removeoperstmt + | renamestmt + | revokestmt + | revokerolestmt + | rulestmt + | seclabelstmt + | selectstmt + | transactionstmt + | truncatestmt + | unlistenstmt + | updatestmt + | vacuumstmt + | variableresetstmt + | variablesetstmt + | variableshowstmt + | viewstmt + ; + +callstmt + : CALL func_application + ; + +createrolestmt + : CREATE ROLE roleid with_? optrolelist + ; + +with_ + : WITH + //| WITH_LA + + ; + +optrolelist + : createoptroleelem* + ; + +alteroptrolelist + : alteroptroleelem* + ; + +alteroptroleelem + : PASSWORD (sconst | NULL_P) + | (ENCRYPTED | UNENCRYPTED) PASSWORD sconst + | INHERIT + | CONNECTION LIMIT signediconst + | VALID UNTIL sconst + | USER role_list + | identifier + ; + +createoptroleelem + : alteroptroleelem + | SYSID iconst + | ADMIN role_list + | ROLE role_list + | IN_P (ROLE | GROUP_P) role_list + ; + +createuserstmt + : CREATE USER roleid with_? optrolelist + ; + +alterrolestmt + : ALTER (ROLE | USER) rolespec with_? alteroptrolelist + ; + +in_database_ + : + IN_P DATABASE name + ; + +alterrolesetstmt + : ALTER (ROLE | USER) ALL? rolespec in_database_? setresetclause + ; + +droprolestmt + : DROP (ROLE | USER | GROUP_P) (IF_P EXISTS)? role_list + ; + +creategroupstmt + : CREATE GROUP_P roleid with_? optrolelist + ; + +altergroupstmt + : ALTER GROUP_P rolespec add_drop USER role_list + ; + +add_drop + : ADD_P + | DROP + ; + +createschemastmt + : CREATE SCHEMA (IF_P NOT EXISTS)? (optschemaname? AUTHORIZATION rolespec | colid) optschemaeltlist + ; + +optschemaname + : colid + + ; + +optschemaeltlist + : schema_stmt* + ; + +schema_stmt + : createstmt + | indexstmt + | createseqstmt + | createtrigstmt + | grantstmt + | viewstmt + ; + +variablesetstmt + : SET (LOCAL | SESSION)? set_rest + ; + +set_rest + : TRANSACTION transaction_mode_list + | SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list + | set_rest_more + ; + +generic_set + : var_name (TO | EQUAL) (var_list | DEFAULT) + ; + +set_rest_more + : generic_set + | var_name FROM CURRENT_P + | TIME ZONE zone_value + | CATALOG sconst + | SCHEMA sconst + | NAMES encoding_? + | ROLE nonreservedword_or_sconst + | SESSION AUTHORIZATION nonreservedword_or_sconst + | XML_P OPTION document_or_content + | TRANSACTION SNAPSHOT sconst + ; + +var_name + : colid (DOT colid)* + ; + +var_list + : var_value (COMMA var_value)* + ; + +var_value + : boolean_or_string_ + | numericonly + ; + +iso_level + : READ (UNCOMMITTED | COMMITTED) + | REPEATABLE READ + | SERIALIZABLE + ; + +boolean_or_string_ + : TRUE_P + | FALSE_P + | ON + | nonreservedword_or_sconst + ; + +zone_value + : sconst + | identifier + | constinterval sconst interval_? + | constinterval OPEN_PAREN iconst CLOSE_PAREN sconst + | numericonly + | DEFAULT + | LOCAL + ; + +encoding_ + : sconst + | DEFAULT + + ; + +nonreservedword_or_sconst + : nonreservedword + | sconst + ; + +variableresetstmt + : RESET reset_rest + ; + +reset_rest + : generic_reset + | TIME ZONE + | TRANSACTION ISOLATION LEVEL + | SESSION AUTHORIZATION + ; + +generic_reset + : var_name + | ALL + ; + +setresetclause + : SET set_rest + | variableresetstmt + ; + +functionsetresetclause + : SET set_rest_more + | variableresetstmt + ; + +variableshowstmt + : SHOW (var_name | TIME ZONE | TRANSACTION ISOLATION LEVEL | SESSION AUTHORIZATION | ALL) + ; + +constraintssetstmt + : SET CONSTRAINTS constraints_set_list constraints_set_mode + ; + +constraints_set_list + : ALL + | qualified_name_list + ; + +constraints_set_mode + : DEFERRED + | IMMEDIATE + ; + +checkpointstmt + : CHECKPOINT + ; + +discardstmt + : DISCARD (ALL | TEMP | TEMPORARY | PLANS | SEQUENCES) + ; + +altertablestmt + : ALTER TABLE (IF_P EXISTS)? relation_expr (alter_table_cmds | partition_cmd) + | ALTER TABLE ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name nowait_? + | ALTER INDEX (IF_P EXISTS)? qualified_name (alter_table_cmds | index_partition_cmd) + | ALTER INDEX ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name nowait_? + | ALTER SEQUENCE (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER VIEW (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER MATERIALIZED VIEW (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER MATERIALIZED VIEW ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name nowait_? + | ALTER FOREIGN TABLE (IF_P EXISTS)? relation_expr alter_table_cmds + ; + +alter_table_cmds + : alter_table_cmd (COMMA alter_table_cmd)* + ; + +partition_cmd + : ATTACH PARTITION qualified_name partitionboundspec + | DETACH PARTITION qualified_name + ; + +index_partition_cmd + : ATTACH PARTITION qualified_name + ; + +alter_table_cmd + : ADD_P columnDef + | ADD_P IF_P NOT EXISTS columnDef + | ADD_P COLUMN columnDef + | ADD_P COLUMN IF_P NOT EXISTS columnDef + | ALTER column_? colid alter_column_default + | ALTER column_? colid DROP NOT NULL_P + | ALTER column_? colid SET NOT NULL_P + | ALTER column_? colid DROP EXPRESSION + | ALTER column_? colid DROP EXPRESSION IF_P EXISTS + | ALTER column_? colid SET STATISTICS signediconst + | ALTER column_? iconst SET STATISTICS signediconst + | ALTER column_? colid SET reloptions + | ALTER column_? colid RESET reloptions + | ALTER column_? colid SET STORAGE colid + | ALTER column_? colid ADD_P GENERATED generated_when AS IDENTITY_P optparenthesizedseqoptlist? + | ALTER column_? colid alter_identity_column_option_list + | ALTER column_? colid DROP IDENTITY_P + | ALTER column_? colid DROP IDENTITY_P IF_P EXISTS + | DROP column_? IF_P EXISTS colid drop_behavior_? + | DROP column_? colid drop_behavior_? + | ALTER column_? colid set_data_? TYPE_P typename collate_clause_? alter_using? + | ALTER column_? colid alter_generic_options + | ADD_P tableconstraint + | ALTER CONSTRAINT name constraintattributespec + | VALIDATE CONSTRAINT name + | DROP CONSTRAINT IF_P EXISTS name drop_behavior_? + | DROP CONSTRAINT name drop_behavior_? + | SET WITHOUT OIDS + | CLUSTER ON name + | SET WITHOUT CLUSTER + | SET LOGGED + | SET UNLOGGED + | ENABLE_P TRIGGER name + | ENABLE_P ALWAYS TRIGGER name + | ENABLE_P REPLICA TRIGGER name + | ENABLE_P TRIGGER ALL + | ENABLE_P TRIGGER USER + | DISABLE_P TRIGGER name + | DISABLE_P TRIGGER ALL + | DISABLE_P TRIGGER USER + | ENABLE_P RULE name + | ENABLE_P ALWAYS RULE name + | ENABLE_P REPLICA RULE name + | DISABLE_P RULE name + | INHERIT qualified_name + | NO INHERIT qualified_name + | OF any_name + | NOT OF + | OWNER TO rolespec + | SET TABLESPACE name + | SET reloptions + | RESET reloptions + | REPLICA IDENTITY_P replica_identity + | ENABLE_P ROW LEVEL SECURITY + | DISABLE_P ROW LEVEL SECURITY + | FORCE ROW LEVEL SECURITY + | NO FORCE ROW LEVEL SECURITY + | alter_generic_options + ; + +alter_column_default + : SET DEFAULT a_expr + | DROP DEFAULT + ; + +drop_behavior_ + : CASCADE + | RESTRICT + + ; + +collate_clause_ + : COLLATE any_name + + ; + +alter_using + : USING a_expr + + ; + +replica_identity + : NOTHING + | FULL + | DEFAULT + | USING INDEX name + ; + +reloptions + : OPEN_PAREN reloption_list CLOSE_PAREN + ; + +reloptions_ + : WITH reloptions + + ; + +reloption_list + : reloption_elem (COMMA reloption_elem)* + ; + +reloption_elem + : colLabel (EQUAL def_arg | DOT colLabel (EQUAL def_arg)?)? + ; + +alter_identity_column_option_list + : alter_identity_column_option+ + ; + +alter_identity_column_option + : RESTART (with_? numericonly)? + | SET (seqoptelem | GENERATED generated_when) + ; + +partitionboundspec + : FOR VALUES WITH OPEN_PAREN hash_partbound CLOSE_PAREN + | FOR VALUES IN_P OPEN_PAREN expr_list CLOSE_PAREN + | FOR VALUES FROM OPEN_PAREN expr_list CLOSE_PAREN TO OPEN_PAREN expr_list CLOSE_PAREN + | DEFAULT + ; + +hash_partbound_elem + : nonreservedword iconst + ; + +hash_partbound + : hash_partbound_elem (COMMA hash_partbound_elem)* + ; + +altercompositetypestmt + : ALTER TYPE_P any_name alter_type_cmds + ; + +alter_type_cmds + : alter_type_cmd (COMMA alter_type_cmd)* + ; + +alter_type_cmd + : ADD_P ATTRIBUTE tablefuncelement drop_behavior_? + | DROP ATTRIBUTE (IF_P EXISTS)? colid drop_behavior_? + | ALTER ATTRIBUTE colid set_data_? TYPE_P typename collate_clause_? drop_behavior_? + ; + +closeportalstmt + : CLOSE (cursor_name | ALL) + ; + +copystmt + : COPY binary_? qualified_name column_list_? copy_from program_? copy_file_name copy_delimiter? with_? copy_options where_clause? + | COPY OPEN_PAREN preparablestmt CLOSE_PAREN TO program_? copy_file_name with_? copy_options + ; + +copy_from + : FROM + | TO + ; + +program_ + : PROGRAM + + ; + +copy_file_name + : sconst + | STDIN + | STDOUT + ; + +copy_options + : copy_opt_list + | OPEN_PAREN copy_generic_opt_list CLOSE_PAREN + ; + +copy_opt_list + : copy_opt_item* + ; + +copy_opt_item + : BINARY + | FREEZE + | DELIMITER as_? sconst + | NULL_P as_? sconst + | CSV + | HEADER_P + | QUOTE as_? sconst + | ESCAPE as_? sconst + | FORCE QUOTE columnlist + | FORCE QUOTE STAR + | FORCE NOT NULL_P columnlist + | FORCE NULL_P columnlist + | ENCODING sconst + ; + +binary_ + : BINARY + + ; + +copy_delimiter + : using_? DELIMITERS sconst + + ; + +using_ + : USING + + ; + +copy_generic_opt_list + : copy_generic_opt_elem (COMMA copy_generic_opt_elem)* + ; + +copy_generic_opt_elem + : colLabel copy_generic_opt_arg? + ; + +copy_generic_opt_arg + : boolean_or_string_ + | numericonly + | STAR + | OPEN_PAREN copy_generic_opt_arg_list CLOSE_PAREN + + ; + +copy_generic_opt_arg_list + : copy_generic_opt_arg_list_item (COMMA copy_generic_opt_arg_list_item)* + ; + +copy_generic_opt_arg_list_item + : boolean_or_string_ + ; + +createstmt + : CREATE opttemp? TABLE (IF_P NOT EXISTS)? qualified_name ( + OPEN_PAREN opttableelementlist? CLOSE_PAREN optinherit? optpartitionspec? table_access_method_clause? optwith? oncommitoption? opttablespace? + | OF any_name opttypedtableelementlist? optpartitionspec? table_access_method_clause? optwith? oncommitoption? opttablespace? + | PARTITION OF qualified_name opttypedtableelementlist? partitionboundspec optpartitionspec? table_access_method_clause? optwith? oncommitoption? + opttablespace? + ) + ; + +opttemp + : TEMPORARY + | TEMP + | LOCAL (TEMPORARY | TEMP) + | GLOBAL (TEMPORARY | TEMP) + | UNLOGGED + + ; + +opttableelementlist + : tableelementlist + + ; + +opttypedtableelementlist + : OPEN_PAREN typedtableelementlist CLOSE_PAREN + + ; + +tableelementlist + : tableelement (COMMA tableelement)* + ; + +typedtableelementlist + : typedtableelement (COMMA typedtableelement)* + ; + +tableelement + : tableconstraint + | tablelikeclause + | columnDef + ; + +typedtableelement + : columnOptions + | tableconstraint + ; + +columnDef + : colid typename create_generic_options? colquallist + ; + +columnOptions + : colid (WITH OPTIONS)? colquallist + ; + +colquallist + : colconstraint* + ; + +colconstraint + : CONSTRAINT name colconstraintelem + | colconstraintelem + | constraintattr + | COLLATE any_name + ; + +colconstraintelem + : NOT NULL_P + | NULL_P + | UNIQUE definition_? optconstablespace? + | PRIMARY KEY definition_? optconstablespace? + | CHECK OPEN_PAREN a_expr CLOSE_PAREN no_inherit_? + | DEFAULT b_expr + | GENERATED generated_when AS ( + IDENTITY_P optparenthesizedseqoptlist? + | OPEN_PAREN a_expr CLOSE_PAREN STORED + ) + | REFERENCES qualified_name column_list_? key_match? key_actions? + ; + +generated_when + : ALWAYS + | BY DEFAULT + ; + +constraintattr + : DEFERRABLE + | NOT DEFERRABLE + | INITIALLY (DEFERRED | IMMEDIATE) + ; + +tablelikeclause + : LIKE qualified_name tablelikeoptionlist + ; + +tablelikeoptionlist + : ((INCLUDING | EXCLUDING) tablelikeoption)* + ; + +tablelikeoption + : COMMENTS + | CONSTRAINTS + | DEFAULTS + | IDENTITY_P + | GENERATED + | INDEXES + | STATISTICS + | STORAGE + | ALL + ; + +tableconstraint + : CONSTRAINT name constraintelem + | constraintelem + ; + +constraintelem + : CHECK OPEN_PAREN a_expr CLOSE_PAREN constraintattributespec + | UNIQUE ( + OPEN_PAREN columnlist CLOSE_PAREN c_include_? definition_? optconstablespace? constraintattributespec + | existingindex constraintattributespec + ) + | PRIMARY KEY ( + OPEN_PAREN columnlist CLOSE_PAREN c_include_? definition_? optconstablespace? constraintattributespec + | existingindex constraintattributespec + ) + | EXCLUDE access_method_clause? OPEN_PAREN exclusionconstraintlist CLOSE_PAREN c_include_? definition_? optconstablespace? exclusionwhereclause? + constraintattributespec + | FOREIGN KEY OPEN_PAREN columnlist CLOSE_PAREN REFERENCES qualified_name column_list_? key_match? key_actions? constraintattributespec + ; + +no_inherit_ + : NO INHERIT + + ; + +column_list_ + : OPEN_PAREN columnlist CLOSE_PAREN + + ; + +columnlist + : columnElem (COMMA columnElem)* + ; + +columnElem + : colid + ; + +c_include_ + : INCLUDE OPEN_PAREN columnlist CLOSE_PAREN + + ; + +key_match + : MATCH (FULL | PARTIAL | SIMPLE) + + ; + +exclusionconstraintlist + : exclusionconstraintelem (COMMA exclusionconstraintelem)* + ; + +exclusionconstraintelem + : index_elem WITH (any_operator | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN) + ; + +exclusionwhereclause + : WHERE OPEN_PAREN a_expr CLOSE_PAREN + + ; + +key_actions + : key_update + | key_delete + | key_update key_delete + | key_delete key_update + + ; + +key_update + : ON UPDATE key_action + ; + +key_delete + : ON DELETE_P key_action + ; + +key_action + : NO ACTION + | RESTRICT + | CASCADE + | SET (NULL_P | DEFAULT) + ; + +optinherit + : INHERITS OPEN_PAREN qualified_name_list CLOSE_PAREN + + ; + +optpartitionspec + : partitionspec + + ; + +partitionspec + : PARTITION BY colid OPEN_PAREN part_params CLOSE_PAREN + ; + +part_params + : part_elem (COMMA part_elem)* + ; + +part_elem + : colid collate_? class_? + | func_expr_windowless collate_? class_? + | OPEN_PAREN a_expr CLOSE_PAREN collate_? class_? + ; + +table_access_method_clause + : USING name + + ; + +optwith + : WITH reloptions + | WITHOUT OIDS + + ; + +oncommitoption + : ON COMMIT (DROP | DELETE_P ROWS | PRESERVE ROWS) + + ; + +opttablespace + : TABLESPACE name + + ; + +optconstablespace + : USING INDEX TABLESPACE name + + ; + +existingindex + : USING INDEX name + ; + +createstatsstmt + : CREATE STATISTICS (IF_P NOT EXISTS)? any_name name_list_? ON expr_list FROM from_list + ; + +alterstatsstmt + : ALTER STATISTICS (IF_P EXISTS)? any_name SET STATISTICS signediconst + ; + +createasstmt + : CREATE opttemp? TABLE (IF_P NOT EXISTS)? create_as_target AS selectstmt with_data_? + ; + +create_as_target + : qualified_name column_list_? table_access_method_clause? optwith? oncommitoption? opttablespace? + ; + +with_data_ + : WITH (DATA_P | NO DATA_P) + + ; + +creatematviewstmt + : CREATE optnolog? MATERIALIZED VIEW (IF_P NOT EXISTS)? create_mv_target AS selectstmt with_data_? + ; + +create_mv_target + : qualified_name column_list_? table_access_method_clause? reloptions_? opttablespace? + ; + +optnolog + : UNLOGGED + + ; + +refreshmatviewstmt + : REFRESH MATERIALIZED VIEW concurrently_? qualified_name with_data_? + ; + +createseqstmt + : CREATE opttemp? SEQUENCE (IF_P NOT EXISTS)? qualified_name optseqoptlist? + ; + +alterseqstmt + : ALTER SEQUENCE (IF_P EXISTS)? qualified_name seqoptlist + ; + +optseqoptlist + : seqoptlist + + ; + +optparenthesizedseqoptlist + : OPEN_PAREN seqoptlist CLOSE_PAREN + + ; + +seqoptlist + : seqoptelem+ + ; + +seqoptelem + : AS simpletypename + | CACHE numericonly + | CYCLE + | INCREMENT by_? numericonly + | MAXVALUE numericonly + | MINVALUE numericonly + | NO (MAXVALUE | MINVALUE | CYCLE) + | OWNED BY any_name + | SEQUENCE NAME_P any_name + | START with_? numericonly + | RESTART with_? numericonly? + ; + +by_ + : BY + + ; + +numericonly + : fconst + | PLUS fconst + | MINUS fconst + | signediconst + ; + +numericonly_list + : numericonly (COMMA numericonly)* + ; + +createplangstmt + : CREATE or_replace_? trusted_? procedural_? LANGUAGE name ( + HANDLER handler_name inline_handler_? validator_? + )? + ; + +trusted_ + : TRUSTED + + ; + +handler_name + : name attrs? + ; + +inline_handler_ + : INLINE_P handler_name + + ; + +validator_clause + : VALIDATOR handler_name + | NO VALIDATOR + ; + +validator_ + : validator_clause + + ; + +procedural_ + : PROCEDURAL + + ; + +createtablespacestmt + : CREATE TABLESPACE name opttablespaceowner? LOCATION sconst reloptions_? + ; + +opttablespaceowner + : OWNER rolespec + + ; + +droptablespacestmt + : DROP TABLESPACE (IF_P EXISTS)? name + ; + +createextensionstmt + : CREATE EXTENSION (IF_P NOT EXISTS)? name with_? create_extension_opt_list + ; + +create_extension_opt_list + : create_extension_opt_item* + ; + +create_extension_opt_item + : SCHEMA name + | VERSION_P nonreservedword_or_sconst + | FROM nonreservedword_or_sconst + | CASCADE + ; + +alterextensionstmt + : ALTER EXTENSION name UPDATE alter_extension_opt_list + ; + +alter_extension_opt_list + : alter_extension_opt_item* + ; + +alter_extension_opt_item + : TO nonreservedword_or_sconst + ; + +alterextensioncontentsstmt + : ALTER EXTENSION name add_drop object_type_name name + | ALTER EXTENSION name add_drop object_type_any_name any_name + | ALTER EXTENSION name add_drop AGGREGATE aggregate_with_argtypes + | ALTER EXTENSION name add_drop CAST OPEN_PAREN typename AS typename CLOSE_PAREN + | ALTER EXTENSION name add_drop DOMAIN_P typename + | ALTER EXTENSION name add_drop FUNCTION function_with_argtypes + | ALTER EXTENSION name add_drop OPERATOR operator_with_argtypes + | ALTER EXTENSION name add_drop OPERATOR CLASS any_name USING name + | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING name + | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes + | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes + | ALTER EXTENSION name add_drop TRANSFORM FOR typename LANGUAGE name + | ALTER EXTENSION name add_drop TYPE_P typename + ; + +createfdwstmt + : CREATE FOREIGN DATA_P WRAPPER name fdw_options_? create_generic_options? + ; + +fdw_option + : HANDLER handler_name + | NO HANDLER + | VALIDATOR handler_name + | NO VALIDATOR + ; + +fdw_options + : fdw_option+ + ; + +fdw_options_ + : fdw_options + + ; + +alterfdwstmt + : ALTER FOREIGN DATA_P WRAPPER name fdw_options_? alter_generic_options + | ALTER FOREIGN DATA_P WRAPPER name fdw_options + ; + +create_generic_options + : OPTIONS OPEN_PAREN generic_option_list CLOSE_PAREN + + ; + +generic_option_list + : generic_option_elem (COMMA generic_option_elem)* + ; + +alter_generic_options + : OPTIONS OPEN_PAREN alter_generic_option_list CLOSE_PAREN + ; + +alter_generic_option_list + : alter_generic_option_elem (COMMA alter_generic_option_elem)* + ; + +alter_generic_option_elem + : generic_option_elem + | SET generic_option_elem + | ADD_P generic_option_elem + | DROP generic_option_name + ; + +generic_option_elem + : generic_option_name generic_option_arg + ; + +generic_option_name + : colLabel + ; + +generic_option_arg + : sconst + ; + +createforeignserverstmt + : CREATE SERVER name type_? foreign_server_version_? FOREIGN DATA_P WRAPPER name create_generic_options? + | CREATE SERVER IF_P NOT EXISTS name type_? foreign_server_version_? FOREIGN DATA_P WRAPPER name create_generic_options? + ; + +type_ + : TYPE_P sconst + + ; + +foreign_server_version + : VERSION_P (sconst | NULL_P) + ; + +foreign_server_version_ + : foreign_server_version + + ; + +alterforeignserverstmt + : ALTER SERVER name (alter_generic_options | foreign_server_version alter_generic_options?) + ; + +createforeigntablestmt + : CREATE FOREIGN TABLE qualified_name OPEN_PAREN opttableelementlist? CLOSE_PAREN optinherit? SERVER name create_generic_options? + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name OPEN_PAREN opttableelementlist? CLOSE_PAREN optinherit? SERVER name create_generic_options? + | CREATE FOREIGN TABLE qualified_name PARTITION OF qualified_name opttypedtableelementlist? partitionboundspec SERVER name create_generic_options? + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name PARTITION OF qualified_name opttypedtableelementlist? partitionboundspec SERVER name + create_generic_options? + ; + +importforeignschemastmt + : IMPORT_P FOREIGN SCHEMA name import_qualification? FROM SERVER name INTO name create_generic_options? + ; + +import_qualification_type + : LIMIT TO + | EXCEPT + ; + +import_qualification + : import_qualification_type OPEN_PAREN relation_expr_list CLOSE_PAREN + + ; + +createusermappingstmt + : CREATE USER MAPPING FOR auth_ident SERVER name create_generic_options? + | CREATE USER MAPPING IF_P NOT EXISTS FOR auth_ident SERVER name create_generic_options? + ; + +auth_ident + : rolespec + | USER + ; + +dropusermappingstmt + : DROP USER MAPPING FOR auth_ident SERVER name + | DROP USER MAPPING IF_P EXISTS FOR auth_ident SERVER name + ; + +alterusermappingstmt + : ALTER USER MAPPING FOR auth_ident SERVER name alter_generic_options + ; + +createpolicystmt + : CREATE POLICY name ON qualified_name rowsecuritydefaultpermissive? rowsecuritydefaultforcmd? rowsecuritydefaulttorole? rowsecurityoptionalexpr? + rowsecurityoptionalwithcheck? + ; + +alterpolicystmt + : ALTER POLICY name ON qualified_name rowsecurityoptionaltorole? rowsecurityoptionalexpr? rowsecurityoptionalwithcheck? + ; + +rowsecurityoptionalexpr + : USING OPEN_PAREN a_expr CLOSE_PAREN + + ; + +rowsecurityoptionalwithcheck + : WITH CHECK OPEN_PAREN a_expr CLOSE_PAREN + + ; + +rowsecuritydefaulttorole + : TO role_list + + ; + +rowsecurityoptionaltorole + : TO role_list + + ; + +rowsecuritydefaultpermissive + : AS identifier + + ; + +rowsecuritydefaultforcmd + : FOR row_security_cmd + + ; + +row_security_cmd + : ALL + | SELECT + | INSERT + | UPDATE + | DELETE_P + ; + +createamstmt + : CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name + ; + +am_type + : INDEX + | TABLE + ; + +createtrigstmt + : CREATE TRIGGER name triggeractiontime triggerevents ON qualified_name triggerreferencing? triggerforspec? triggerwhen? EXECUTE + function_or_procedure func_name OPEN_PAREN triggerfuncargs CLOSE_PAREN + | CREATE CONSTRAINT TRIGGER name AFTER triggerevents ON qualified_name optconstrfromtable? constraintattributespec FOR EACH ROW triggerwhen? EXECUTE + function_or_procedure func_name OPEN_PAREN triggerfuncargs CLOSE_PAREN + ; + +triggeractiontime + : BEFORE + | AFTER + | INSTEAD OF + ; + +triggerevents + : triggeroneevent (OR triggeroneevent)* + ; + +triggeroneevent + : INSERT + | DELETE_P + | UPDATE + | UPDATE OF columnlist + | TRUNCATE + ; + +triggerreferencing + : REFERENCING triggertransitions + + ; + +triggertransitions + : triggertransition+ + ; + +triggertransition + : transitionoldornew transitionrowortable as_? transitionrelname + ; + +transitionoldornew + : NEW + | OLD + ; + +transitionrowortable + : TABLE + | ROW + ; + +transitionrelname + : colid + ; + +triggerforspec + : FOR triggerforopteach? triggerfortype + + ; + +triggerforopteach + : EACH + + ; + +triggerfortype + : ROW + | STATEMENT + ; + +triggerwhen + : WHEN OPEN_PAREN a_expr CLOSE_PAREN + + ; + +function_or_procedure + : FUNCTION + | PROCEDURE + ; + +triggerfuncargs + : (triggerfuncarg |) (COMMA triggerfuncarg)* + ; + +triggerfuncarg + : iconst + | fconst + | sconst + | colLabel + ; + +optconstrfromtable + : FROM qualified_name + + ; + +constraintattributespec + : constraintattributeElem* + ; + +constraintattributeElem + : NOT DEFERRABLE + | DEFERRABLE + | INITIALLY IMMEDIATE + | INITIALLY DEFERRED + | NOT VALID + | NO INHERIT + ; + +createeventtrigstmt + : CREATE EVENT TRIGGER name ON colLabel EXECUTE function_or_procedure func_name OPEN_PAREN CLOSE_PAREN + | CREATE EVENT TRIGGER name ON colLabel WHEN event_trigger_when_list EXECUTE function_or_procedure func_name OPEN_PAREN CLOSE_PAREN + ; + +event_trigger_when_list + : event_trigger_when_item (AND event_trigger_when_item)* + ; + +event_trigger_when_item + : colid IN_P OPEN_PAREN event_trigger_value_list CLOSE_PAREN + ; + +event_trigger_value_list + : sconst (COMMA sconst)* + ; + +altereventtrigstmt + : ALTER EVENT TRIGGER name enable_trigger + ; + +enable_trigger + : ENABLE_P + | ENABLE_P REPLICA + | ENABLE_P ALWAYS + | DISABLE_P + ; + +createassertionstmt + : CREATE ASSERTION any_name CHECK OPEN_PAREN a_expr CLOSE_PAREN constraintattributespec + ; + +definestmt + : CREATE or_replace_? AGGREGATE func_name aggr_args definition + | CREATE or_replace_? AGGREGATE func_name old_aggr_definition + | CREATE OPERATOR any_operator definition + | CREATE TYPE_P any_name definition + | CREATE TYPE_P any_name + | CREATE TYPE_P any_name AS OPEN_PAREN opttablefuncelementlist? CLOSE_PAREN + | CREATE TYPE_P any_name AS ENUM_P OPEN_PAREN enum_val_list_? CLOSE_PAREN + | CREATE TYPE_P any_name AS RANGE definition + | CREATE TEXT_P SEARCH PARSER any_name definition + | CREATE TEXT_P SEARCH DICTIONARY any_name definition + | CREATE TEXT_P SEARCH TEMPLATE any_name definition + | CREATE TEXT_P SEARCH CONFIGURATION any_name definition + | CREATE COLLATION any_name definition + | CREATE COLLATION IF_P NOT EXISTS any_name definition + | CREATE COLLATION any_name FROM any_name + | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name + ; + +definition + : OPEN_PAREN def_list CLOSE_PAREN + ; + +def_list + : def_elem (COMMA def_elem)* + ; + +def_elem + : colLabel (EQUAL def_arg)? + ; + +def_arg + : func_type + | reserved_keyword + | qual_all_op + | numericonly + | sconst + | NONE + ; + +old_aggr_definition + : OPEN_PAREN old_aggr_list CLOSE_PAREN + ; + +old_aggr_list + : old_aggr_elem (COMMA old_aggr_elem)* + ; + +old_aggr_elem + : identifier EQUAL def_arg + ; + +enum_val_list_ + : enum_val_list + + ; + +enum_val_list + : sconst (COMMA sconst)* + ; + +alterenumstmt + : ALTER TYPE_P any_name ADD_P VALUE_P if_not_exists_? sconst + | ALTER TYPE_P any_name ADD_P VALUE_P if_not_exists_? sconst BEFORE sconst + | ALTER TYPE_P any_name ADD_P VALUE_P if_not_exists_? sconst AFTER sconst + | ALTER TYPE_P any_name RENAME VALUE_P sconst TO sconst + ; + +if_not_exists_ + : IF_P NOT EXISTS + + ; + +createopclassstmt + : CREATE OPERATOR CLASS any_name default_? FOR TYPE_P typename USING name opfamily_? AS opclass_item_list + ; + +opclass_item_list + : opclass_item (COMMA opclass_item)* + ; + +opclass_item + : OPERATOR iconst any_operator opclass_purpose? recheck_? + | OPERATOR iconst operator_with_argtypes opclass_purpose? recheck_? + | FUNCTION iconst function_with_argtypes + | FUNCTION iconst OPEN_PAREN type_list CLOSE_PAREN function_with_argtypes + | STORAGE typename + ; + +default_ + : DEFAULT + + ; + +opfamily_ + : FAMILY any_name + + ; + +opclass_purpose + : FOR SEARCH + | FOR ORDER BY any_name + + ; + +recheck_ + : RECHECK + + ; + +createopfamilystmt + : CREATE OPERATOR FAMILY any_name USING name + ; + +alteropfamilystmt + : ALTER OPERATOR FAMILY any_name USING name ADD_P opclass_item_list + | ALTER OPERATOR FAMILY any_name USING name DROP opclass_drop_list + ; + +opclass_drop_list + : opclass_drop (COMMA opclass_drop)* + ; + +opclass_drop + : OPERATOR iconst OPEN_PAREN type_list CLOSE_PAREN + | FUNCTION iconst OPEN_PAREN type_list CLOSE_PAREN + ; + +dropopclassstmt + : DROP OPERATOR CLASS any_name USING name drop_behavior_? + | DROP OPERATOR CLASS IF_P EXISTS any_name USING name drop_behavior_? + ; + +dropopfamilystmt + : DROP OPERATOR FAMILY any_name USING name drop_behavior_? + | DROP OPERATOR FAMILY IF_P EXISTS any_name USING name drop_behavior_? + ; + +dropownedstmt + : DROP OWNED BY role_list drop_behavior_? + ; + +reassignownedstmt + : REASSIGN OWNED BY role_list TO rolespec + ; + +dropstmt + : DROP object_type_any_name IF_P EXISTS any_name_list_ drop_behavior_? + | DROP object_type_any_name any_name_list_ drop_behavior_? + | DROP drop_type_name IF_P EXISTS name_list drop_behavior_? + | DROP drop_type_name name_list drop_behavior_? + | DROP object_type_name_on_any_name name ON any_name drop_behavior_? + | DROP object_type_name_on_any_name IF_P EXISTS name ON any_name drop_behavior_? + | DROP TYPE_P type_name_list drop_behavior_? + | DROP TYPE_P IF_P EXISTS type_name_list drop_behavior_? + | DROP DOMAIN_P type_name_list drop_behavior_? + | DROP DOMAIN_P IF_P EXISTS type_name_list drop_behavior_? + | DROP INDEX CONCURRENTLY any_name_list_ drop_behavior_? + | DROP INDEX CONCURRENTLY IF_P EXISTS any_name_list_ drop_behavior_? + ; + +object_type_any_name + : TABLE + | SEQUENCE + | VIEW + | MATERIALIZED VIEW + | INDEX + | FOREIGN TABLE + | COLLATION + | CONVERSION_P + | STATISTICS + | TEXT_P SEARCH PARSER + | TEXT_P SEARCH DICTIONARY + | TEXT_P SEARCH TEMPLATE + | TEXT_P SEARCH CONFIGURATION + ; + +object_type_name + : drop_type_name + | DATABASE + | ROLE + | SUBSCRIPTION + | TABLESPACE + ; + +drop_type_name + : ACCESS METHOD + | EVENT TRIGGER + | EXTENSION + | FOREIGN DATA_P WRAPPER + | procedural_? LANGUAGE + | PUBLICATION + | SCHEMA + | SERVER + ; + +object_type_name_on_any_name + : POLICY + | RULE + | TRIGGER + ; + +any_name_list_ + : any_name (COMMA any_name)* + ; + +any_name + : colid attrs? + ; + +attrs + : (DOT attr_name)+ + ; + +type_name_list + : typename (COMMA typename)* + ; + +truncatestmt + : TRUNCATE table_? relation_expr_list restart_seqs_? drop_behavior_? + ; + +restart_seqs_ + : CONTINUE_P IDENTITY_P + | RESTART IDENTITY_P + + ; + +commentstmt + : COMMENT ON object_type_any_name any_name IS comment_text + | COMMENT ON COLUMN any_name IS comment_text + | COMMENT ON object_type_name name IS comment_text + | COMMENT ON TYPE_P typename IS comment_text + | COMMENT ON DOMAIN_P typename IS comment_text + | COMMENT ON AGGREGATE aggregate_with_argtypes IS comment_text + | COMMENT ON FUNCTION function_with_argtypes IS comment_text + | COMMENT ON OPERATOR operator_with_argtypes IS comment_text + | COMMENT ON CONSTRAINT name ON any_name IS comment_text + | COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text + | COMMENT ON object_type_name_on_any_name name ON any_name IS comment_text + | COMMENT ON PROCEDURE function_with_argtypes IS comment_text + | COMMENT ON ROUTINE function_with_argtypes IS comment_text + | COMMENT ON TRANSFORM FOR typename LANGUAGE name IS comment_text + | COMMENT ON OPERATOR CLASS any_name USING name IS comment_text + | COMMENT ON OPERATOR FAMILY any_name USING name IS comment_text + | COMMENT ON LARGE_P OBJECT_P numericonly IS comment_text + | COMMENT ON CAST OPEN_PAREN typename AS typename CLOSE_PAREN IS comment_text + ; + +comment_text + : sconst + | NULL_P + ; + +seclabelstmt + : SECURITY LABEL provider_? ON object_type_any_name any_name IS security_label + | SECURITY LABEL provider_? ON COLUMN any_name IS security_label + | SECURITY LABEL provider_? ON object_type_name name IS security_label + | SECURITY LABEL provider_? ON TYPE_P typename IS security_label + | SECURITY LABEL provider_? ON DOMAIN_P typename IS security_label + | SECURITY LABEL provider_? ON AGGREGATE aggregate_with_argtypes IS security_label + | SECURITY LABEL provider_? ON FUNCTION function_with_argtypes IS security_label + | SECURITY LABEL provider_? ON LARGE_P OBJECT_P numericonly IS security_label + | SECURITY LABEL provider_? ON PROCEDURE function_with_argtypes IS security_label + | SECURITY LABEL provider_? ON ROUTINE function_with_argtypes IS security_label + ; + +provider_ + : FOR nonreservedword_or_sconst + + ; + +security_label + : sconst + | NULL_P + ; + +fetchstmt + : FETCH fetch_args + | MOVE fetch_args + ; + +fetch_args + : cursor_name + | from_in cursor_name + | NEXT from_in_? cursor_name + | PRIOR from_in_? cursor_name + | FIRST_P from_in_? cursor_name + | LAST_P from_in_? cursor_name + | ABSOLUTE_P signediconst from_in_? cursor_name + | RELATIVE_P signediconst from_in_? cursor_name + | signediconst from_in_? cursor_name + | ALL from_in_? cursor_name + | FORWARD from_in_? cursor_name + | FORWARD signediconst from_in_? cursor_name + | FORWARD ALL from_in_? cursor_name + | BACKWARD from_in_? cursor_name + | BACKWARD signediconst from_in_? cursor_name + | BACKWARD ALL from_in_? cursor_name + ; + +from_in + : FROM + | IN_P + ; + +from_in_ + : from_in + + ; + +grantstmt + : GRANT privileges ON privilege_target TO grantee_list grant_grant_option_? + ; + +revokestmt + : REVOKE privileges ON privilege_target FROM grantee_list drop_behavior_? + | REVOKE GRANT OPTION FOR privileges ON privilege_target FROM grantee_list drop_behavior_? + ; + +privileges + : privilege_list + | ALL + | ALL PRIVILEGES + | ALL OPEN_PAREN columnlist CLOSE_PAREN + | ALL PRIVILEGES OPEN_PAREN columnlist CLOSE_PAREN + ; + +privilege_list + : privilege (COMMA privilege)* + ; + +privilege + : SELECT column_list_? + | REFERENCES column_list_? + | CREATE column_list_? + | colid column_list_? + ; + +privilege_target + : qualified_name_list + | TABLE qualified_name_list + | SEQUENCE qualified_name_list + | FOREIGN DATA_P WRAPPER name_list + | FOREIGN SERVER name_list + | FUNCTION function_with_argtypes_list + | PROCEDURE function_with_argtypes_list + | ROUTINE function_with_argtypes_list + | DATABASE name_list + | DOMAIN_P any_name_list_ + | LANGUAGE name_list + | LARGE_P OBJECT_P numericonly_list + | SCHEMA name_list + | TABLESPACE name_list + | TYPE_P any_name_list_ + | ALL TABLES IN_P SCHEMA name_list + | ALL SEQUENCES IN_P SCHEMA name_list + | ALL FUNCTIONS IN_P SCHEMA name_list + | ALL PROCEDURES IN_P SCHEMA name_list + | ALL ROUTINES IN_P SCHEMA name_list + ; + +grantee_list + : grantee (COMMA grantee)* + ; + +grantee + : rolespec + | GROUP_P rolespec + ; + +grant_grant_option_ + : WITH GRANT OPTION + + ; + +grantrolestmt + : GRANT privilege_list TO role_list grant_admin_option_? granted_by_? + ; + +revokerolestmt + : REVOKE privilege_list FROM role_list granted_by_? drop_behavior_? + | REVOKE ADMIN OPTION FOR privilege_list FROM role_list granted_by_? drop_behavior_? + ; + +grant_admin_option_ + : WITH ADMIN OPTION + + ; + +granted_by_ + : GRANTED BY rolespec + + ; + +alterdefaultprivilegesstmt + : ALTER DEFAULT PRIVILEGES defacloptionlist defaclaction + ; + +defacloptionlist + : defacloption* + ; + +defacloption + : IN_P SCHEMA name_list + | FOR ROLE role_list + | FOR USER role_list + ; + +defaclaction + : GRANT privileges ON defacl_privilege_target TO grantee_list grant_grant_option_? + | REVOKE privileges ON defacl_privilege_target FROM grantee_list drop_behavior_? + | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target FROM grantee_list drop_behavior_? + ; + +defacl_privilege_target + : TABLES + | FUNCTIONS + | ROUTINES + | SEQUENCES + | TYPES_P + | SCHEMAS + ; + +//create index + +indexstmt + : CREATE unique_? INDEX concurrently_? index_name_? ON relation_expr access_method_clause? OPEN_PAREN index_params CLOSE_PAREN include_? + reloptions_? opttablespace? where_clause? + | CREATE unique_? INDEX concurrently_? IF_P NOT EXISTS name ON relation_expr access_method_clause? OPEN_PAREN index_params CLOSE_PAREN + include_? reloptions_? opttablespace? where_clause? + ; + +unique_ + : UNIQUE + + ; + +single_name_ + : colid + ; + +concurrently_ + : CONCURRENTLY + + ; + +index_name_ + : name + + ; + +access_method_clause + : USING name + + ; + +index_params + : index_elem (COMMA index_elem)* + ; + +index_elem_options + : collate_? class_? asc_desc_? nulls_order_? + | collate_? any_name reloptions asc_desc_? nulls_order_? + ; + +index_elem + : colid index_elem_options + | func_expr_windowless index_elem_options + | OPEN_PAREN a_expr CLOSE_PAREN index_elem_options + ; + +include_ + : INCLUDE OPEN_PAREN index_including_params CLOSE_PAREN + + ; + +index_including_params + : index_elem (COMMA index_elem)* + ; + +collate_ + : COLLATE any_name + + ; + +class_ + : any_name + + ; + +asc_desc_ + : ASC + | DESC + + ; + +//TOD NULLS_LA was used + +nulls_order_ + : NULLS_P FIRST_P + | NULLS_P LAST_P + + ; + +createfunctionstmt + : CREATE or_replace_? (FUNCTION | PROCEDURE) func_name func_args_with_defaults ( + RETURNS (func_return | TABLE OPEN_PAREN table_func_column_list CLOSE_PAREN) + )? createfunc_opt_list + ; + +or_replace_ + : OR REPLACE + + ; + +func_args + : OPEN_PAREN func_args_list? CLOSE_PAREN + ; + +func_args_list + : func_arg (COMMA func_arg)* + ; + +function_with_argtypes_list + : function_with_argtypes (COMMA function_with_argtypes)* + ; + +function_with_argtypes + : func_name func_args + | type_func_name_keyword + | colid indirection? + ; + +func_args_with_defaults + : OPEN_PAREN func_args_with_defaults_list? CLOSE_PAREN + ; + +func_args_with_defaults_list + : func_arg_with_default (COMMA func_arg_with_default)* + ; + +func_arg + : arg_class param_name? func_type + | param_name arg_class? func_type + | func_type + ; + +arg_class + : IN_P OUT_P? + | OUT_P + | INOUT + | VARIADIC + ; + +param_name + : type_function_name + ; + +func_return + : func_type + ; + +func_type + : typename + | SETOF? type_function_name attrs PERCENT TYPE_P + ; + +func_arg_with_default + : func_arg ((DEFAULT | EQUAL) a_expr)? + ; + +aggr_arg + : func_arg + ; + +aggr_args + : OPEN_PAREN ( + STAR + | aggr_args_list + | ORDER BY aggr_args_list + | aggr_args_list ORDER BY aggr_args_list + ) CLOSE_PAREN + ; + +aggr_args_list + : aggr_arg (COMMA aggr_arg)* + ; + +aggregate_with_argtypes + : func_name aggr_args + ; + +aggregate_with_argtypes_list + : aggregate_with_argtypes (COMMA aggregate_with_argtypes)* + ; + +createfunc_opt_list + : createfunc_opt_item+ {this->ParseRoutineBody();} + // | createfunc_opt_list createfunc_opt_item + ; + +common_func_opt_item + : CALLED ON NULL_P INPUT_P + | RETURNS NULL_P ON NULL_P INPUT_P + | STRICT_P + | IMMUTABLE + | STABLE + | VOLATILE + | EXTERNAL SECURITY DEFINER + | EXTERNAL SECURITY INVOKER + | SECURITY DEFINER + | SECURITY INVOKER + | LEAKPROOF + | NOT LEAKPROOF + | COST numericonly + | ROWS numericonly + | SUPPORT any_name + | functionsetresetclause + | PARALLEL colid + ; + +createfunc_opt_item + : AS func_as + | LANGUAGE nonreservedword_or_sconst + | TRANSFORM transform_type_list + | WINDOW + | common_func_opt_item + ; + +//https://www.postgresql.org/docs/9.1/sql-createfunction.html + +// | AS 'definition' + +// | AS 'obj_file', 'link_symbol' + +func_as + : + /* |AS 'definition'*/ def = sconst + /*| AS 'obj_file', 'link_symbol'*/ + | sconst COMMA sconst + ; + +transform_type_list + : FOR TYPE_P typename (COMMA FOR TYPE_P typename)* + ; + +definition_ + : WITH definition + + ; + +table_func_column + : param_name func_type + ; + +table_func_column_list + : table_func_column (COMMA table_func_column)* + ; + +alterfunctionstmt + : ALTER (FUNCTION | PROCEDURE | ROUTINE) function_with_argtypes alterfunc_opt_list restrict_? + ; + +alterfunc_opt_list + : common_func_opt_item+ + ; + +restrict_ + : RESTRICT + + ; + +removefuncstmt + : DROP FUNCTION function_with_argtypes_list drop_behavior_? + | DROP FUNCTION IF_P EXISTS function_with_argtypes_list drop_behavior_? + | DROP PROCEDURE function_with_argtypes_list drop_behavior_? + | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list drop_behavior_? + | DROP ROUTINE function_with_argtypes_list drop_behavior_? + | DROP ROUTINE IF_P EXISTS function_with_argtypes_list drop_behavior_? + ; + +removeaggrstmt + : DROP AGGREGATE aggregate_with_argtypes_list drop_behavior_? + | DROP AGGREGATE IF_P EXISTS aggregate_with_argtypes_list drop_behavior_? + ; + +removeoperstmt + : DROP OPERATOR operator_with_argtypes_list drop_behavior_? + | DROP OPERATOR IF_P EXISTS operator_with_argtypes_list drop_behavior_? + ; + +oper_argtypes + : OPEN_PAREN typename CLOSE_PAREN + | OPEN_PAREN typename COMMA typename CLOSE_PAREN + | OPEN_PAREN NONE COMMA typename CLOSE_PAREN + | OPEN_PAREN typename COMMA NONE CLOSE_PAREN + ; + +any_operator + : (colid DOT)* all_op + ; + +operator_with_argtypes_list + : operator_with_argtypes (COMMA operator_with_argtypes)* + ; + +operator_with_argtypes + : any_operator oper_argtypes + ; + +dostmt + : DO dostmt_opt_list + ; + +dostmt_opt_list + : dostmt_opt_item+ + ; + +dostmt_opt_item + : sconst + | LANGUAGE nonreservedword_or_sconst + ; + +createcaststmt + : CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITH FUNCTION function_with_argtypes cast_context? + | CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITHOUT FUNCTION cast_context? + | CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITH INOUT cast_context? + ; + +cast_context + : AS IMPLICIT_P + | AS ASSIGNMENT + + ; + +dropcaststmt + : DROP CAST if_exists_? OPEN_PAREN typename AS typename CLOSE_PAREN drop_behavior_? + ; + +if_exists_ + : IF_P EXISTS + + ; + +createtransformstmt + : CREATE or_replace_? TRANSFORM FOR typename LANGUAGE name OPEN_PAREN transform_element_list CLOSE_PAREN + ; + +transform_element_list + : FROM SQL_P WITH FUNCTION function_with_argtypes COMMA TO SQL_P WITH FUNCTION function_with_argtypes + | TO SQL_P WITH FUNCTION function_with_argtypes COMMA FROM SQL_P WITH FUNCTION function_with_argtypes + | FROM SQL_P WITH FUNCTION function_with_argtypes + | TO SQL_P WITH FUNCTION function_with_argtypes + ; + +droptransformstmt + : DROP TRANSFORM if_exists_? FOR typename LANGUAGE name drop_behavior_? + ; + +reindexstmt + : REINDEX reindex_option_list? reindex_target_relation concurrently_? qualified_name + | REINDEX reindex_option_list? SCHEMA concurrently_? name + | REINDEX reindex_option_list? reindex_target_all concurrently_? single_name_? + ; + +reindex_target_relation + : INDEX + | TABLE + ; + +reindex_target_all + : SYSTEM_P + | DATABASE + ; + +reindex_option_list + : OPEN_PAREN utility_option_list CLOSE_PAREN + ; + +altertblspcstmt + : ALTER TABLESPACE name SET reloptions + | ALTER TABLESPACE name RESET reloptions + ; + +renamestmt + : ALTER AGGREGATE aggregate_with_argtypes RENAME TO name + | ALTER COLLATION any_name RENAME TO name + | ALTER CONVERSION_P any_name RENAME TO name + | ALTER DATABASE name RENAME TO name + | ALTER DOMAIN_P any_name RENAME TO name + | ALTER DOMAIN_P any_name RENAME CONSTRAINT name TO name + | ALTER FOREIGN DATA_P WRAPPER name RENAME TO name + | ALTER FUNCTION function_with_argtypes RENAME TO name + | ALTER GROUP_P roleid RENAME TO roleid + | ALTER procedural_? LANGUAGE name RENAME TO name + | ALTER OPERATOR CLASS any_name USING name RENAME TO name + | ALTER OPERATOR FAMILY any_name USING name RENAME TO name + | ALTER POLICY name ON qualified_name RENAME TO name + | ALTER POLICY IF_P EXISTS name ON qualified_name RENAME TO name + | ALTER PROCEDURE function_with_argtypes RENAME TO name + | ALTER PUBLICATION name RENAME TO name + | ALTER ROUTINE function_with_argtypes RENAME TO name + | ALTER SCHEMA name RENAME TO name + | ALTER SERVER name RENAME TO name + | ALTER SUBSCRIPTION name RENAME TO name + | ALTER TABLE relation_expr RENAME TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME TO name + | ALTER SEQUENCE qualified_name RENAME TO name + | ALTER SEQUENCE IF_P EXISTS qualified_name RENAME TO name + | ALTER VIEW qualified_name RENAME TO name + | ALTER VIEW IF_P EXISTS qualified_name RENAME TO name + | ALTER MATERIALIZED VIEW qualified_name RENAME TO name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME TO name + | ALTER INDEX qualified_name RENAME TO name + | ALTER INDEX IF_P EXISTS qualified_name RENAME TO name + | ALTER FOREIGN TABLE relation_expr RENAME TO name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME TO name + | ALTER TABLE relation_expr RENAME column_? name TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME column_? name TO name + | ALTER VIEW qualified_name RENAME column_? name TO name + | ALTER VIEW IF_P EXISTS qualified_name RENAME column_? name TO name + | ALTER MATERIALIZED VIEW qualified_name RENAME column_? name TO name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME column_? name TO name + | ALTER TABLE relation_expr RENAME CONSTRAINT name TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME CONSTRAINT name TO name + | ALTER FOREIGN TABLE relation_expr RENAME column_? name TO name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME column_? name TO name + | ALTER RULE name ON qualified_name RENAME TO name + | ALTER TRIGGER name ON qualified_name RENAME TO name + | ALTER EVENT TRIGGER name RENAME TO name + | ALTER ROLE roleid RENAME TO roleid + | ALTER USER roleid RENAME TO roleid + | ALTER TABLESPACE name RENAME TO name + | ALTER STATISTICS any_name RENAME TO name + | ALTER TEXT_P SEARCH PARSER any_name RENAME TO name + | ALTER TEXT_P SEARCH DICTIONARY any_name RENAME TO name + | ALTER TEXT_P SEARCH TEMPLATE any_name RENAME TO name + | ALTER TEXT_P SEARCH CONFIGURATION any_name RENAME TO name + | ALTER TYPE_P any_name RENAME TO name + | ALTER TYPE_P any_name RENAME ATTRIBUTE name TO name drop_behavior_? + ; + +column_ + : COLUMN + + ; + +set_data_ + : SET DATA_P + + ; + +alterobjectdependsstmt + : ALTER FUNCTION function_with_argtypes no_? DEPENDS ON EXTENSION name + | ALTER PROCEDURE function_with_argtypes no_? DEPENDS ON EXTENSION name + | ALTER ROUTINE function_with_argtypes no_? DEPENDS ON EXTENSION name + | ALTER TRIGGER name ON qualified_name no_? DEPENDS ON EXTENSION name + | ALTER MATERIALIZED VIEW qualified_name no_? DEPENDS ON EXTENSION name + | ALTER INDEX qualified_name no_? DEPENDS ON EXTENSION name + ; + +no_ + : NO + + ; + +alterobjectschemastmt + : ALTER AGGREGATE aggregate_with_argtypes SET SCHEMA name + | ALTER COLLATION any_name SET SCHEMA name + | ALTER CONVERSION_P any_name SET SCHEMA name + | ALTER DOMAIN_P any_name SET SCHEMA name + | ALTER EXTENSION name SET SCHEMA name + | ALTER FUNCTION function_with_argtypes SET SCHEMA name + | ALTER OPERATOR operator_with_argtypes SET SCHEMA name + | ALTER OPERATOR CLASS any_name USING name SET SCHEMA name + | ALTER OPERATOR FAMILY any_name USING name SET SCHEMA name + | ALTER PROCEDURE function_with_argtypes SET SCHEMA name + | ALTER ROUTINE function_with_argtypes SET SCHEMA name + | ALTER TABLE relation_expr SET SCHEMA name + | ALTER TABLE IF_P EXISTS relation_expr SET SCHEMA name + | ALTER STATISTICS any_name SET SCHEMA name + | ALTER TEXT_P SEARCH PARSER any_name SET SCHEMA name + | ALTER TEXT_P SEARCH DICTIONARY any_name SET SCHEMA name + | ALTER TEXT_P SEARCH TEMPLATE any_name SET SCHEMA name + | ALTER TEXT_P SEARCH CONFIGURATION any_name SET SCHEMA name + | ALTER SEQUENCE qualified_name SET SCHEMA name + | ALTER SEQUENCE IF_P EXISTS qualified_name SET SCHEMA name + | ALTER VIEW qualified_name SET SCHEMA name + | ALTER VIEW IF_P EXISTS qualified_name SET SCHEMA name + | ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name SET SCHEMA name + | ALTER FOREIGN TABLE relation_expr SET SCHEMA name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr SET SCHEMA name + | ALTER TYPE_P any_name SET SCHEMA name + ; + +alteroperatorstmt + : ALTER OPERATOR operator_with_argtypes SET OPEN_PAREN operator_def_list CLOSE_PAREN + ; + +operator_def_list + : operator_def_elem (COMMA operator_def_elem)* + ; + +operator_def_elem + : colLabel EQUAL NONE + | colLabel EQUAL operator_def_arg + ; + +operator_def_arg + : func_type + | reserved_keyword + | qual_all_op + | numericonly + | sconst + ; + +altertypestmt + : ALTER TYPE_P any_name SET OPEN_PAREN operator_def_list CLOSE_PAREN + ; + +alterownerstmt + : ALTER AGGREGATE aggregate_with_argtypes OWNER TO rolespec + | ALTER COLLATION any_name OWNER TO rolespec + | ALTER CONVERSION_P any_name OWNER TO rolespec + | ALTER DATABASE name OWNER TO rolespec + | ALTER DOMAIN_P any_name OWNER TO rolespec + | ALTER FUNCTION function_with_argtypes OWNER TO rolespec + | ALTER procedural_? LANGUAGE name OWNER TO rolespec + | ALTER LARGE_P OBJECT_P numericonly OWNER TO rolespec + | ALTER OPERATOR operator_with_argtypes OWNER TO rolespec + | ALTER OPERATOR CLASS any_name USING name OWNER TO rolespec + | ALTER OPERATOR FAMILY any_name USING name OWNER TO rolespec + | ALTER PROCEDURE function_with_argtypes OWNER TO rolespec + | ALTER ROUTINE function_with_argtypes OWNER TO rolespec + | ALTER SCHEMA name OWNER TO rolespec + | ALTER TYPE_P any_name OWNER TO rolespec + | ALTER TABLESPACE name OWNER TO rolespec + | ALTER STATISTICS any_name OWNER TO rolespec + | ALTER TEXT_P SEARCH DICTIONARY any_name OWNER TO rolespec + | ALTER TEXT_P SEARCH CONFIGURATION any_name OWNER TO rolespec + | ALTER FOREIGN DATA_P WRAPPER name OWNER TO rolespec + | ALTER SERVER name OWNER TO rolespec + | ALTER EVENT TRIGGER name OWNER TO rolespec + | ALTER PUBLICATION name OWNER TO rolespec + | ALTER SUBSCRIPTION name OWNER TO rolespec + ; + +createpublicationstmt + : CREATE PUBLICATION name publication_for_tables_? definition_? + ; + +publication_for_tables_ + : publication_for_tables + + ; + +publication_for_tables + : FOR TABLE relation_expr_list + | FOR ALL TABLES + ; + +alterpublicationstmt + : ALTER PUBLICATION name SET definition + | ALTER PUBLICATION name ADD_P TABLE relation_expr_list + | ALTER PUBLICATION name SET TABLE relation_expr_list + | ALTER PUBLICATION name DROP TABLE relation_expr_list + ; + +createsubscriptionstmt + : CREATE SUBSCRIPTION name CONNECTION sconst PUBLICATION publication_name_list definition_? + ; + +publication_name_list + : publication_name_item (COMMA publication_name_item)* + ; + +publication_name_item + : colLabel + ; + +altersubscriptionstmt + : ALTER SUBSCRIPTION name SET definition + | ALTER SUBSCRIPTION name CONNECTION sconst + | ALTER SUBSCRIPTION name REFRESH PUBLICATION definition_? + | ALTER SUBSCRIPTION name SET PUBLICATION publication_name_list definition_? + | ALTER SUBSCRIPTION name ENABLE_P + | ALTER SUBSCRIPTION name DISABLE_P + ; + +dropsubscriptionstmt + : DROP SUBSCRIPTION name drop_behavior_? + | DROP SUBSCRIPTION IF_P EXISTS name drop_behavior_? + ; + +rulestmt + : CREATE or_replace_? RULE name AS ON event TO qualified_name where_clause? DO instead_? ruleactionlist + ; + +ruleactionlist + : NOTHING + | ruleactionstmt + | OPEN_PAREN ruleactionmulti CLOSE_PAREN + ; + +ruleactionmulti + : ruleactionstmtOrEmpty? (SEMI ruleactionstmtOrEmpty?)* + ; + +ruleactionstmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + | notifystmt + ; + +ruleactionstmtOrEmpty + : ruleactionstmt + + ; + +event + : SELECT + | UPDATE + | DELETE_P + | INSERT + ; + +instead_ + : INSTEAD + | ALSO + + ; + +notifystmt + : NOTIFY colid notify_payload? + ; + +notify_payload + : COMMA sconst + + ; + +listenstmt + : LISTEN colid + ; + +unlistenstmt + : UNLISTEN colid + | UNLISTEN STAR + ; + +transactionstmt + : ABORT_P transaction_? transaction_chain_? + | BEGIN_P transaction_? transaction_mode_list_or_empty? + | START TRANSACTION transaction_mode_list_or_empty? + | COMMIT transaction_? transaction_chain_? + | END_P transaction_? transaction_chain_? + | ROLLBACK transaction_? transaction_chain_? + | SAVEPOINT colid + | RELEASE SAVEPOINT colid + | RELEASE colid + | ROLLBACK transaction_? TO SAVEPOINT colid + | ROLLBACK transaction_? TO colid + | PREPARE TRANSACTION sconst + | COMMIT PREPARED sconst + | ROLLBACK PREPARED sconst + ; + +transaction_ + : WORK + | TRANSACTION + + ; + +transaction_mode_item + : ISOLATION LEVEL iso_level + | READ ONLY + | READ WRITE + | DEFERRABLE + | NOT DEFERRABLE + ; + +transaction_mode_list + : transaction_mode_item (COMMA? transaction_mode_item)* + ; + +transaction_mode_list_or_empty + : transaction_mode_list + + ; + +transaction_chain_ + : AND NO? CHAIN + + ; + +viewstmt + : CREATE (OR REPLACE)? opttemp? ( + VIEW qualified_name column_list_? reloptions_? + | RECURSIVE VIEW qualified_name OPEN_PAREN columnlist CLOSE_PAREN reloptions_? + ) AS selectstmt check_option_? + ; + +check_option_ + : WITH (CASCADED | LOCAL)? CHECK OPTION + + ; + +loadstmt + : LOAD file_name + ; + +createdbstmt + : CREATE DATABASE name with_? createdb_opt_list? + ; + +createdb_opt_list + : createdb_opt_items + + ; + +createdb_opt_items + : createdb_opt_item+ + ; + +createdb_opt_item + : createdb_opt_name equal_? (signediconst | boolean_or_string_ | DEFAULT) + ; + +createdb_opt_name + : identifier + | CONNECTION LIMIT + | ENCODING + | LOCATION + | OWNER + | TABLESPACE + | TEMPLATE + ; + +equal_ + : EQUAL + + ; + +alterdatabasestmt + : ALTER DATABASE name (WITH createdb_opt_list? | createdb_opt_list? | SET TABLESPACE name) + ; + +alterdatabasesetstmt + : ALTER DATABASE name setresetclause + ; + +dropdbstmt + : DROP DATABASE (IF_P EXISTS)? name (with_? OPEN_PAREN drop_option_list CLOSE_PAREN)? + ; + +drop_option_list + : drop_option (COMMA drop_option)* + ; + +drop_option + : FORCE + ; + +altercollationstmt + : ALTER COLLATION any_name REFRESH VERSION_P + ; + +altersystemstmt + : ALTER SYSTEM_P (SET | RESET) generic_set + ; + +createdomainstmt + : CREATE DOMAIN_P any_name as_? typename colquallist + ; + +alterdomainstmt + : ALTER DOMAIN_P any_name ( + alter_column_default + | DROP NOT NULL_P + | SET NOT NULL_P + | ADD_P tableconstraint + | DROP CONSTRAINT (IF_P EXISTS)? name drop_behavior_? + | VALIDATE CONSTRAINT name + ) + ; + +as_ + : AS + + ; + +altertsdictionarystmt + : ALTER TEXT_P SEARCH DICTIONARY any_name definition + ; + +altertsconfigurationstmt + : ALTER TEXT_P SEARCH CONFIGURATION any_name ADD_P MAPPING FOR name_list any_with any_name_list_ + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list any_with any_name_list_ + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING REPLACE any_name any_with any_name + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list REPLACE any_name any_with any_name + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING FOR name_list + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING IF_P EXISTS FOR name_list + ; + +any_with + : WITH + //TODO + + // | WITH_LA + ; + +createconversionstmt + : CREATE default_? CONVERSION_P any_name FOR sconst TO sconst FROM any_name + ; + +clusterstmt + : CLUSTER verbose_? qualified_name cluster_index_specification? + | CLUSTER verbose_? + | CLUSTER verbose_? name ON qualified_name + ; + +cluster_index_specification + : USING name + + ; + +vacuumstmt + : VACUUM full_? freeze_? verbose_? analyze_? vacuum_relation_list_? + | VACUUM OPEN_PAREN vac_analyze_option_list CLOSE_PAREN vacuum_relation_list_? + ; + +analyzestmt + : analyze_keyword verbose_? vacuum_relation_list_? + | analyze_keyword OPEN_PAREN vac_analyze_option_list CLOSE_PAREN vacuum_relation_list_? + ; + +utility_option_list + : utility_option_elem ( ',' utility_option_elem)* + ; + +vac_analyze_option_list + : vac_analyze_option_elem (COMMA vac_analyze_option_elem)* + ; + +analyze_keyword + : ANALYZE + | ANALYSE + ; + +utility_option_elem + : utility_option_name utility_option_arg? + ; + +utility_option_name + : nonreservedword + | analyze_keyword + | FORMAT_LA + ; + +utility_option_arg + : boolean_or_string_ + | numericonly + ; + +vac_analyze_option_elem + : vac_analyze_option_name vac_analyze_option_arg? + ; + +vac_analyze_option_name + : nonreservedword + | analyze_keyword + ; + +vac_analyze_option_arg + : boolean_or_string_ + | numericonly + + ; + +analyze_ + : analyze_keyword + + ; + +verbose_ + : VERBOSE + + ; + +full_ + : FULL + + ; + +freeze_ + : FREEZE + + ; + +name_list_ + : OPEN_PAREN name_list CLOSE_PAREN + + ; + +vacuum_relation + : qualified_name name_list_? + ; + +vacuum_relation_list + : vacuum_relation (COMMA vacuum_relation)* + ; + +vacuum_relation_list_ + : vacuum_relation_list + + ; + +explainstmt + : EXPLAIN explainablestmt + | EXPLAIN analyze_keyword verbose_? explainablestmt + | EXPLAIN VERBOSE explainablestmt + | EXPLAIN OPEN_PAREN explain_option_list CLOSE_PAREN explainablestmt + ; + +explainablestmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + | declarecursorstmt + | createasstmt + | creatematviewstmt + | refreshmatviewstmt + | executestmt + ; + +explain_option_list + : explain_option_elem (COMMA explain_option_elem)* + ; + +explain_option_elem + : explain_option_name explain_option_arg? + ; + +explain_option_name + : nonreservedword + | analyze_keyword + ; + +explain_option_arg + : boolean_or_string_ + | numericonly + + ; + +preparestmt + : PREPARE name prep_type_clause? AS preparablestmt + ; + +prep_type_clause + : OPEN_PAREN type_list CLOSE_PAREN + + ; + +preparablestmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + ; + +executestmt + : EXECUTE name execute_param_clause? + | CREATE opttemp? TABLE create_as_target AS EXECUTE name execute_param_clause? with_data_? + | CREATE opttemp? TABLE IF_P NOT EXISTS create_as_target AS EXECUTE name execute_param_clause? with_data_? + ; + +execute_param_clause + : OPEN_PAREN expr_list CLOSE_PAREN + + ; + +deallocatestmt + : DEALLOCATE name + | DEALLOCATE PREPARE name + | DEALLOCATE ALL + | DEALLOCATE PREPARE ALL + ; + +insertstmt + : with_clause_? INSERT INTO insert_target insert_rest on_conflict_? returning_clause? + ; + +insert_target + : qualified_name (AS colid)? + ; + +insert_rest + : selectstmt + | OVERRIDING override_kind VALUE_P selectstmt + | OPEN_PAREN insert_column_list CLOSE_PAREN (OVERRIDING override_kind VALUE_P)? selectstmt + | DEFAULT VALUES + ; + +override_kind + : USER + | SYSTEM_P + ; + +insert_column_list + : insert_column_item (COMMA insert_column_item)* + ; + +insert_column_item + : colid opt_indirection + ; + +on_conflict_ + : ON CONFLICT conf_expr_? DO (UPDATE SET set_clause_list where_clause? | NOTHING) + + ; + +conf_expr_ + : OPEN_PAREN index_params CLOSE_PAREN where_clause? + | ON CONSTRAINT name + + ; + +returning_clause + : RETURNING target_list + + ; + +// https://www.postgresql.org/docs/current/sql-merge.html +mergestmt + : MERGE INTO? qualified_name alias_clause? USING (select_with_parens | qualified_name) alias_clause? ON a_expr ( + merge_insert_clause merge_update_clause? + | merge_update_clause merge_insert_clause? + ) merge_delete_clause? + ; + +merge_insert_clause + : WHEN NOT MATCHED (AND a_expr)? THEN? INSERT (OPEN_PAREN insert_column_list CLOSE_PAREN)? values_clause + ; + +merge_update_clause + : WHEN MATCHED (AND a_expr)? THEN? UPDATE SET set_clause_list + ; + +merge_delete_clause + : WHEN MATCHED THEN? DELETE_P + ; + +deletestmt + : with_clause_? DELETE_P FROM relation_expr_opt_alias using_clause? where_or_current_clause? returning_clause? + ; + +using_clause + : USING from_list + + ; + +lockstmt + : LOCK_P table_? relation_expr_list lock_? nowait_? + ; + +lock_ + : IN_P lock_type MODE + + ; + +lock_type + : ACCESS (SHARE | EXCLUSIVE) + | ROW (SHARE | EXCLUSIVE) + | SHARE (UPDATE EXCLUSIVE | ROW EXCLUSIVE)? + | EXCLUSIVE + ; + +nowait_ + : NOWAIT + + ; + +nowait_or_skip_ + : NOWAIT + | SKIP_P LOCKED + + ; + +updatestmt + : with_clause_? UPDATE relation_expr_opt_alias SET set_clause_list from_clause? where_or_current_clause? returning_clause? + ; + +set_clause_list + : set_clause (COMMA set_clause)* + ; + +set_clause + : set_target EQUAL a_expr + | OPEN_PAREN set_target_list CLOSE_PAREN EQUAL a_expr + ; + +set_target + : colid opt_indirection + ; + +set_target_list + : set_target (COMMA set_target)* + ; + +declarecursorstmt + : DECLARE cursor_name cursor_options CURSOR hold_? FOR selectstmt + ; + +cursor_name + : name + ; + +cursor_options + : (NO SCROLL | SCROLL | BINARY | INSENSITIVE)* + ; + +hold_ + : + WITH HOLD + | WITHOUT HOLD + ; + +/* +TODO: why select_with_parens alternative is needed at all? +i guess it because original byson grammar can choose selectstmt(2)->select_with_parens on only OPEN_PARENT/SELECT kewords at the begining of statement; +(select * from tab); +parse can go through selectstmt( )->select_no_parens(1)->select_clause(2)->select_with_parens(1)->select_no_parens(1)->select_clause(1)->simple_select +instead of selectstmt(1)->select_no_parens(1)->select_clause(2)->select_with_parens(1)->select_no_parens(1)->select_clause(1)->simple_select +all standard tests passed on both variants +*/ + +selectstmt + : select_no_parens + | select_with_parens + ; + +select_with_parens + : OPEN_PAREN select_no_parens CLOSE_PAREN + | OPEN_PAREN select_with_parens CLOSE_PAREN + ; + +select_no_parens + : select_clause sort_clause_? ( + for_locking_clause select_limit_? + | select_limit for_locking_clause_? + )? + | with_clause select_clause sort_clause_? ( + for_locking_clause select_limit_? + | select_limit for_locking_clause_? + )? + ; + +select_clause + : simple_select_intersect ((UNION | EXCEPT) all_or_distinct? simple_select_intersect)* + ; + +simple_select_intersect + : simple_select_pramary (INTERSECT all_or_distinct? simple_select_pramary)* + ; + +simple_select_pramary + : ( + SELECT + ( all_clause_? target_list_? + into_clause? from_clause? where_clause? + group_clause? having_clause? window_clause? + | distinct_clause target_list + into_clause? from_clause? where_clause? + group_clause? having_clause? window_clause? + ) + ) + | values_clause + | TABLE relation_expr + | select_with_parens + ; + +with_clause + : WITH RECURSIVE? cte_list + ; + +cte_list + : common_table_expr (COMMA common_table_expr)* + ; + +common_table_expr + : name name_list_? AS materialized_? OPEN_PAREN preparablestmt CLOSE_PAREN + ; + +materialized_ + : MATERIALIZED + | NOT MATERIALIZED + + ; + +with_clause_ + : with_clause + + ; + +into_clause + : INTO opttempTableName + ; + +strict_ + : + STRICT_P + ; + +opttempTableName + : (LOCAL | GLOBAL)? (TEMPORARY | TEMP) table_? qualified_name + | UNLOGGED table_? qualified_name + | TABLE qualified_name + | qualified_name + ; + +table_ + : TABLE + + ; + +all_or_distinct + : ALL + | DISTINCT + + ; + +distinct_clause + : DISTINCT (ON OPEN_PAREN expr_list CLOSE_PAREN)? + ; + +all_clause_ + : ALL + + ; + +sort_clause_ + : sort_clause + + ; + +sort_clause + : ORDER BY sortby_list + ; + +sortby_list + : sortby (COMMA sortby)* + ; + +sortby + : a_expr (USING qual_all_op | asc_desc_?) nulls_order_? + ; + +select_limit + : limit_clause offset_clause? + | offset_clause limit_clause? + ; + +select_limit_ + : select_limit + + ; + +limit_clause + : LIMIT select_limit_value (COMMA select_offset_value)? + | FETCH first_or_next ( + select_fetch_first_value row_or_rows (ONLY | WITH TIES) + | row_or_rows (ONLY | WITH TIES) + ) + ; + +offset_clause + : OFFSET (select_offset_value | select_fetch_first_value row_or_rows) + ; + +select_limit_value + : a_expr + | ALL + ; + +select_offset_value + : a_expr + ; + +select_fetch_first_value + : c_expr + | PLUS i_or_f_const + | MINUS i_or_f_const + ; + +i_or_f_const + : iconst + | fconst + ; + +row_or_rows + : ROW + | ROWS + ; + +first_or_next + : FIRST_P + | NEXT + ; + +group_clause + : GROUP_P BY group_by_list + + ; + +group_by_list + : group_by_item (COMMA group_by_item)* + ; + +group_by_item + : empty_grouping_set + | cube_clause + | rollup_clause + | grouping_sets_clause + | a_expr + ; + +empty_grouping_set + : OPEN_PAREN CLOSE_PAREN + ; + +rollup_clause + : ROLLUP OPEN_PAREN expr_list CLOSE_PAREN + ; + +cube_clause + : CUBE OPEN_PAREN expr_list CLOSE_PAREN + ; + +grouping_sets_clause + : GROUPING SETS OPEN_PAREN group_by_list CLOSE_PAREN + ; + +having_clause + : HAVING a_expr + + ; + +for_locking_clause + : for_locking_items + | FOR READ ONLY + ; + +for_locking_clause_ + : for_locking_clause + + ; + +for_locking_items + : for_locking_item+ + ; + +for_locking_item + : for_locking_strength locked_rels_list? nowait_or_skip_? + ; + +for_locking_strength + : FOR ((NO KEY)? UPDATE | KEY? SHARE) + ; + +locked_rels_list + : OF qualified_name_list + + ; + +values_clause + : VALUES OPEN_PAREN expr_list CLOSE_PAREN (COMMA OPEN_PAREN expr_list CLOSE_PAREN)* + ; + +from_clause + : FROM from_list + + ; + +from_list + : table_ref (COMMA table_ref)* + ; + +table_ref + : ( + relation_expr alias_clause? tablesample_clause? + | func_table func_alias_clause? + | xmltable alias_clause? + | select_with_parens alias_clause? + | LATERAL_P ( + xmltable alias_clause? + | func_table func_alias_clause? + | select_with_parens alias_clause? + ) + | OPEN_PAREN table_ref ( + CROSS JOIN table_ref + | NATURAL join_type? JOIN table_ref + | join_type? JOIN table_ref join_qual + )? CLOSE_PAREN alias_clause? + ) ( + CROSS JOIN table_ref + | NATURAL join_type? JOIN table_ref + | join_type? JOIN table_ref join_qual + )* + ; + +alias_clause + : AS? colid (OPEN_PAREN name_list CLOSE_PAREN)? + ; + +func_alias_clause + : alias_clause + | (AS colid? | colid) OPEN_PAREN tablefuncelementlist CLOSE_PAREN + + ; + +join_type + : (FULL | LEFT | RIGHT | INNER_P) OUTER_P? + ; + +join_qual + : USING OPEN_PAREN name_list CLOSE_PAREN + | ON a_expr + ; + +relation_expr + : qualified_name STAR? + | ONLY (qualified_name | OPEN_PAREN qualified_name CLOSE_PAREN) + ; + +relation_expr_list + : relation_expr (COMMA relation_expr)* + ; + +relation_expr_opt_alias + : relation_expr (AS? colid)? + ; + +tablesample_clause + : TABLESAMPLE func_name OPEN_PAREN expr_list CLOSE_PAREN repeatable_clause_? + ; + +repeatable_clause_ + : REPEATABLE OPEN_PAREN a_expr CLOSE_PAREN + + ; + +func_table + : func_expr_windowless ordinality_? + | ROWS FROM OPEN_PAREN rowsfrom_list CLOSE_PAREN ordinality_? + ; + +rowsfrom_item + : func_expr_windowless col_def_list_? + ; + +rowsfrom_list + : rowsfrom_item (COMMA rowsfrom_item)* + ; + +col_def_list_ + : AS OPEN_PAREN tablefuncelementlist CLOSE_PAREN + + ; + +//TODO WITH_LA was used + +ordinality_ + : WITH ORDINALITY + + ; + +where_clause + : WHERE a_expr + + ; + +where_or_current_clause + : WHERE (CURRENT_P OF cursor_name | a_expr) + + ; + +opttablefuncelementlist + : tablefuncelementlist + + ; + +tablefuncelementlist + : tablefuncelement (COMMA tablefuncelement)* + ; + +tablefuncelement + : colid typename collate_clause_? + ; + +xmltable + : XMLTABLE OPEN_PAREN ( + c_expr xmlexists_argument COLUMNS xmltable_column_list + | XMLNAMESPACES OPEN_PAREN xml_namespace_list CLOSE_PAREN COMMA c_expr xmlexists_argument COLUMNS xmltable_column_list + ) CLOSE_PAREN + ; + +xmltable_column_list + : xmltable_column_el (COMMA xmltable_column_el)* + ; + +xmltable_column_el + : colid (typename xmltable_column_option_list? | FOR ORDINALITY) + ; + +xmltable_column_option_list + : xmltable_column_option_el+ + ; + +xmltable_column_option_el + : DEFAULT a_expr + | identifier a_expr + | NOT NULL_P + | NULL_P + ; + +xml_namespace_list + : xml_namespace_el (COMMA xml_namespace_el)* + ; + +xml_namespace_el + : b_expr AS colLabel + | DEFAULT b_expr + ; + +typename + : SETOF? simpletypename + ( opt_array_bounds + | ARRAY (OPEN_BRACKET iconst CLOSE_BRACKET)? + ) + ; + +opt_array_bounds + : (OPEN_BRACKET iconst? CLOSE_BRACKET)* + ; + +simpletypename + : generictype + | numeric + | bit + | character + | constdatetime + | constinterval (interval_? | OPEN_PAREN iconst CLOSE_PAREN) + | jsonType + ; + +consttypename + : numeric + | constbit + | constcharacter + | constdatetime + | jsonType + ; + +generictype + : type_function_name attrs? type_modifiers_? + ; + +type_modifiers_ + : OPEN_PAREN expr_list CLOSE_PAREN + + ; + +numeric + : INT_P + | INTEGER + | SMALLINT + | BIGINT + | REAL + | FLOAT_P float_? + | DOUBLE_P PRECISION + | DECIMAL_P type_modifiers_? + | DEC type_modifiers_? + | NUMERIC type_modifiers_? + | BOOLEAN_P + ; + +float_ + : OPEN_PAREN iconst CLOSE_PAREN + + ; + +//todo: merge alts + +bit + : bitwithlength + | bitwithoutlength + ; + +constbit + : bitwithlength + | bitwithoutlength + ; + +bitwithlength + : BIT varying_? OPEN_PAREN expr_list CLOSE_PAREN + ; + +bitwithoutlength + : BIT varying_? + ; + +character + : character_c (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +constcharacter + : character_c (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +character_c + : (CHARACTER | CHAR_P | NCHAR) varying_? + | VARCHAR + | NATIONAL (CHARACTER | CHAR_P) varying_? + ; + +varying_ + : VARYING + + ; + +constdatetime + : (TIMESTAMP | TIME) (OPEN_PAREN iconst CLOSE_PAREN)? timezone_? + ; + +constinterval + : INTERVAL + ; + +//TODO with_la was used + +timezone_ + : WITH TIME ZONE + | WITHOUT TIME ZONE + + ; + +interval_ + : YEAR_P + | MONTH_P + | DAY_P + | HOUR_P + | MINUTE_P + | interval_second + | YEAR_P TO MONTH_P + | DAY_P TO (HOUR_P | MINUTE_P | interval_second) + | HOUR_P TO (MINUTE_P | interval_second) + | MINUTE_P TO interval_second + + ; + +interval_second + : SECOND_P (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +jsonType + : JSON + ; + +escape_ + : ESCAPE a_expr + + ; + +//precendence accroding to Table 4.2. Operator Precedence (highest to lowest) + +//https://www.postgresql.org/docs/12/sql-syntax-lexical.html#SQL-PRECEDENCE + +/* +original version of a_expr, for info + a_expr: c_expr + //:: left PostgreSQL-style typecast + | a_expr TYPECAST typename -- 1 + | a_expr COLLATE any_name -- 2 + | a_expr AT TIME ZONE a_expr-- 3 + //right unary plus, unary minus + | (PLUS| MINUS) a_expr -- 4 + //left exponentiation + | a_expr CARET a_expr -- 5 + //left multiplication, division, modulo + | a_expr (STAR | SLASH | PERCENT) a_expr -- 6 + //left addition, subtraction + | a_expr (PLUS | MINUS) a_expr -- 7 + //left all other native and user-defined operators + | a_expr qual_op a_expr -- 8 + | qual_op a_expr -- 9 + //range containment, set membership, string matching BETWEEN IN LIKE ILIKE SIMILAR + | a_expr NOT? (LIKE|ILIKE|SIMILAR TO|(BETWEEN SYMMETRIC?)) a_expr opt_escape -- 10 + //< > = <= >= <> comparison operators + | a_expr (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) a_expr -- 11 + //IS ISNULL NOTNULL IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM, etc + | a_expr IS NOT? + ( + NULL_P + |TRUE_P + |FALSE_P + |UNKNOWN + |DISTINCT FROM a_expr + |OF OPEN_PAREN type_list CLOSE_PAREN + |DOCUMENT_P + |unicode_normal_form? NORMALIZED + ) -- 12 + | a_expr (ISNULL|NOTNULL) -- 13 + | row OVERLAPS row -- 14 + //NOT right logical negation + | NOT a_expr -- 15 + //AND left logical conjunction + | a_expr AND a_expr -- 16 + //OR left logical disjunction + | a_expr OR a_expr -- 17 + | a_expr (LESS_LESS|GREATER_GREATER) a_expr -- 18 + | a_expr qual_op -- 19 + | a_expr NOT? IN_P in_expr -- 20 + | a_expr subquery_Op sub_type (select_with_parens|OPEN_PAREN a_expr CLOSE_PAREN) -- 21 + | UNIQUE select_with_parens -- 22 + | DEFAULT -- 23 +; +*/ + +a_expr + : a_expr_qual + ; + +/*23*/ + +/*moved to c_expr*/ + +/*22*/ + +/*moved to c_expr*/ + +/*19*/ + +a_expr_qual + : a_expr_lessless ({this->OnlyAcceptableOps()}? qual_op | ) + ; + +/*18*/ + +a_expr_lessless + : a_expr_or ((LESS_LESS | GREATER_GREATER) a_expr_or)* + ; + +/*17*/ + +a_expr_or + : a_expr_and (OR a_expr_and)* + ; + +/*16*/ + +a_expr_and + : a_expr_between (AND a_expr_between)* + ; + +/*21*/ + +a_expr_between + : a_expr_in (NOT? BETWEEN SYMMETRIC? a_expr_in AND a_expr_in)? + ; + +/*20*/ + +a_expr_in + : a_expr_unary_not (NOT? IN_P in_expr)? + ; + +/*15*/ + +a_expr_unary_not + : NOT? a_expr_isnull + ; + +/*14*/ + +/*moved to c_expr*/ + +/*13*/ + +a_expr_isnull + : a_expr_is_not (ISNULL | NOTNULL)? + ; + +/*12*/ + +a_expr_is_not + : a_expr_compare ( + IS NOT? ( + NULL_P + | TRUE_P + | FALSE_P + | UNKNOWN + | DISTINCT FROM a_expr + | OF OPEN_PAREN type_list CLOSE_PAREN + | DOCUMENT_P + | unicode_normal_form? NORMALIZED + ) + )? + ; + +/*11*/ + +a_expr_compare + : a_expr_like ( + (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) a_expr_like + | subquery_Op sub_type (select_with_parens | OPEN_PAREN a_expr CLOSE_PAREN) /*21*/ + )? + ; + +/*10*/ + +a_expr_like + : a_expr_qual_op (NOT? (LIKE | ILIKE | SIMILAR TO) a_expr_qual_op escape_?)? + ; + +/* 8*/ + +a_expr_qual_op + : a_expr_unary_qualop (qual_op a_expr_unary_qualop)* + ; + +/* 9*/ + +a_expr_unary_qualop + : qual_op? a_expr_add + ; + +/* 7*/ + +a_expr_add + : a_expr_mul ((MINUS | PLUS) a_expr_mul)* + ; + +/* 6*/ + +a_expr_mul + : a_expr_caret ((STAR | SLASH | PERCENT) a_expr_caret)* + ; + +/* 5*/ + +a_expr_caret + : a_expr_unary_sign (CARET a_expr_unary_sign)? + ; + +/* 4*/ + +a_expr_unary_sign + : (MINUS | PLUS)? a_expr_at_time_zone /* */ + ; + +/* 3*/ + +a_expr_at_time_zone + : a_expr_collate (AT TIME ZONE a_expr)? + ; + +/* 2*/ + +a_expr_collate + : a_expr_typecast (COLLATE any_name)? + ; + +/* 1*/ + +a_expr_typecast + : c_expr (TYPECAST typename)* + ; + +b_expr + : c_expr + | b_expr TYPECAST typename + //right unary plus, unary minus + | (PLUS | MINUS) b_expr + //^ left exponentiation + | b_expr CARET b_expr + //* / % left multiplication, division, modulo + | b_expr (STAR | SLASH | PERCENT) b_expr + //+ - left addition, subtraction + | b_expr (PLUS | MINUS) b_expr + //(any other operator) left all other native and user-defined operators + | b_expr qual_op b_expr + //< > = <= >= <> comparison operators + | b_expr (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) b_expr + | qual_op b_expr + | b_expr qual_op + //S ISNULL NOTNULL IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM, etc + | b_expr IS NOT? (DISTINCT FROM b_expr | OF OPEN_PAREN type_list CLOSE_PAREN | DOCUMENT_P) + ; + +c_expr + : EXISTS select_with_parens # c_expr_exists + | ARRAY (select_with_parens | array_expr) # c_expr_expr + | PARAM opt_indirection # c_expr_expr + | GROUPING OPEN_PAREN expr_list CLOSE_PAREN # c_expr_expr + | /*22*/ UNIQUE select_with_parens # c_expr_expr + | columnref # c_expr_expr + | aexprconst # c_expr_expr + | OPEN_PAREN a_expr_in_parens = a_expr CLOSE_PAREN opt_indirection # c_expr_expr + | case_expr # c_expr_case + | func_expr # c_expr_expr + | select_with_parens indirection? # c_expr_expr + | explicit_row # c_expr_expr + | implicit_row # c_expr_expr + | row OVERLAPS row /* 14*/ # c_expr_expr + | DEFAULT # c_expr_expr + ; + +plsqlvariablename + : PLSQLVARIABLENAME + ; + +func_application + : func_name OPEN_PAREN ( + func_arg_list (COMMA VARIADIC func_arg_expr)? sort_clause_? + | VARIADIC func_arg_expr sort_clause_? + | (ALL | DISTINCT) func_arg_list sort_clause_? + | STAR + | + ) CLOSE_PAREN + ; + +func_expr + : func_application within_group_clause? filter_clause? over_clause? + | func_expr_common_subexpr + ; + +func_expr_windowless + : func_application + | func_expr_common_subexpr + ; + +func_expr_common_subexpr + : COLLATION FOR OPEN_PAREN a_expr CLOSE_PAREN + | CURRENT_DATE + | CURRENT_TIME (OPEN_PAREN iconst CLOSE_PAREN)? + | CURRENT_TIMESTAMP (OPEN_PAREN iconst CLOSE_PAREN)? + | LOCALTIME (OPEN_PAREN iconst CLOSE_PAREN)? + | LOCALTIMESTAMP (OPEN_PAREN iconst CLOSE_PAREN)? + | CURRENT_ROLE + | CURRENT_USER + | SESSION_USER + | SYSTEM_USER + | USER + | CURRENT_CATALOG + | CURRENT_SCHEMA + | CAST OPEN_PAREN a_expr AS typename CLOSE_PAREN + | EXTRACT OPEN_PAREN extract_list? CLOSE_PAREN + | NORMALIZE OPEN_PAREN a_expr (COMMA unicode_normal_form)? CLOSE_PAREN + | OVERLAY OPEN_PAREN (overlay_list | func_arg_list? ) CLOSE_PAREN + | POSITION OPEN_PAREN position_list? CLOSE_PAREN + | SUBSTRING OPEN_PAREN (substr_list | func_arg_list?) CLOSE_PAREN + | TREAT OPEN_PAREN a_expr AS typename CLOSE_PAREN + | TRIM OPEN_PAREN (BOTH | LEADING | TRAILING)? trim_list CLOSE_PAREN + | NULLIF OPEN_PAREN a_expr COMMA a_expr CLOSE_PAREN + | COALESCE OPEN_PAREN expr_list CLOSE_PAREN + | GREATEST OPEN_PAREN expr_list CLOSE_PAREN + | LEAST OPEN_PAREN expr_list CLOSE_PAREN + | XMLCONCAT OPEN_PAREN expr_list CLOSE_PAREN + | XMLELEMENT OPEN_PAREN NAME_P colLabel (COMMA (xml_attributes | expr_list))? CLOSE_PAREN + | XMLEXISTS OPEN_PAREN c_expr xmlexists_argument CLOSE_PAREN + | XMLFOREST OPEN_PAREN xml_attribute_list CLOSE_PAREN + | XMLPARSE OPEN_PAREN document_or_content a_expr xml_whitespace_option? CLOSE_PAREN + | XMLPI OPEN_PAREN NAME_P colLabel (COMMA a_expr)? CLOSE_PAREN + | XMLROOT OPEN_PAREN XML_P a_expr COMMA xml_root_version xml_root_standalone_? CLOSE_PAREN + | XMLSERIALIZE OPEN_PAREN document_or_content a_expr AS simpletypename CLOSE_PAREN + | JSON_OBJECT OPEN_PAREN (func_arg_list + | json_name_and_value_list + json_object_constructor_null_clause? + json_key_uniqueness_constraint? + json_returning_clause? + | json_returning_clause? ) + CLOSE_PAREN + | JSON_ARRAY OPEN_PAREN (json_value_expr_list + json_array_constructor_null_clause? + json_returning_clause? + | select_no_parens + json_format_clause? + json_returning_clause? + | json_returning_clause? + ) + CLOSE_PAREN + | JSON '(' json_value_expr json_key_uniqueness_constraint? ')' + | JSON_SCALAR '(' a_expr ')' + | JSON_SERIALIZE '(' json_value_expr json_returning_clause? ')' + | MERGE_ACTION '(' ')' + | JSON_QUERY '(' + json_value_expr ',' a_expr json_passing_clause? + json_returning_clause? + json_wrapper_behavior + json_quotes_clause? + json_behavior_clause? + ')' + | JSON_EXISTS '(' + json_value_expr ',' a_expr json_passing_clause? + json_on_error_clause? + ')' + | JSON_VALUE '(' + json_value_expr ',' a_expr json_passing_clause? + json_returning_clause? + json_behavior_clause? + ')' + ; + +/* SQL/XML support */ + +xml_root_version + : VERSION_P a_expr + | VERSION_P NO VALUE_P + ; + +xml_root_standalone_ + : COMMA STANDALONE_P YES_P + | COMMA STANDALONE_P NO + | COMMA STANDALONE_P NO VALUE_P + ; + +xml_attributes + : XMLATTRIBUTES OPEN_PAREN xml_attribute_list CLOSE_PAREN + ; + +xml_attribute_list + : xml_attribute_el (COMMA xml_attribute_el)* + ; + +xml_attribute_el + : a_expr (AS colLabel)? + ; + +document_or_content + : DOCUMENT_P + | CONTENT_P + ; + +xml_whitespace_option + : PRESERVE WHITESPACE_P + | STRIP_P WHITESPACE_P + + ; + +xmlexists_argument + : PASSING c_expr + | PASSING c_expr xml_passing_mech + | PASSING xml_passing_mech c_expr + | PASSING xml_passing_mech c_expr xml_passing_mech + ; + +xml_passing_mech + : BY (REF | VALUE_P) + ; + +within_group_clause + : WITHIN GROUP_P OPEN_PAREN sort_clause CLOSE_PAREN + + ; + +filter_clause + : FILTER OPEN_PAREN WHERE a_expr CLOSE_PAREN + + ; + +window_clause + : WINDOW window_definition_list + + ; + +window_definition_list + : window_definition (COMMA window_definition)* + ; + +window_definition + : colid AS window_specification + ; + +over_clause + : OVER (window_specification | colid) + + ; + +window_specification + : OPEN_PAREN existing_window_name_? partition_clause_? sort_clause_? frame_clause_? CLOSE_PAREN + ; + +existing_window_name_ + : colid + + ; + +partition_clause_ + : PARTITION BY expr_list + + ; + +frame_clause_ + : RANGE frame_extent window_exclusion_clause_? + | ROWS frame_extent window_exclusion_clause_? + | GROUPS frame_extent window_exclusion_clause_? + + ; + +frame_extent + : frame_bound + | BETWEEN frame_bound AND frame_bound + ; + +frame_bound + : UNBOUNDED (PRECEDING | FOLLOWING) + | CURRENT_P ROW + | a_expr (PRECEDING | FOLLOWING) + ; + +window_exclusion_clause_ + : EXCLUDE (CURRENT_P ROW | GROUP_P | TIES | NO OTHERS) + + ; + +row + : ROW OPEN_PAREN expr_list? CLOSE_PAREN + | OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN + ; + +explicit_row + : ROW OPEN_PAREN expr_list? CLOSE_PAREN + ; + +/* +TODO: +for some reason v1 +implicit_row: OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN; +works better than v2 +implicit_row: OPEN_PAREN expr_list CLOSE_PAREN; +while looks like they are almost the same, except v2 requieres at least 2 items in list +while v1 allows single item in list +*/ + +implicit_row + : OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN + ; + +sub_type + : ANY + | SOME + | ALL + ; + +all_op + : Operator + | mathop + ; + +mathop + : PLUS + | MINUS + | STAR + | SLASH + | PERCENT + | CARET + | LT + | GT + | EQUAL + | LESS_EQUALS + | GREATER_EQUALS + | NOT_EQUALS + ; + +qual_op + : Operator + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + ; + +qual_all_op + : all_op + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + ; + +subquery_Op + : all_op + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + | LIKE + | NOT LIKE + | ILIKE + | NOT ILIKE + ; + +expr_list + : a_expr (COMMA a_expr)* + ; + +func_arg_list + : func_arg_expr (COMMA func_arg_expr)* + ; + +func_arg_expr + : a_expr + | param_name (COLON_EQUALS | EQUALS_GREATER) a_expr + ; + +type_list + : typename (COMMA typename)* + ; + +array_expr + : OPEN_BRACKET (expr_list | array_expr_list)? CLOSE_BRACKET + ; + +array_expr_list + : array_expr (COMMA array_expr)* + ; + +extract_list + : extract_arg FROM a_expr + + ; + +extract_arg + : identifier + | YEAR_P + | MONTH_P + | DAY_P + | HOUR_P + | MINUTE_P + | SECOND_P + | sconst + ; + +unicode_normal_form + : NFC + | NFD + | NFKC + | NFKD + ; + +overlay_list + : a_expr PLACING a_expr FROM a_expr (FOR a_expr)? + ; + +position_list + : b_expr IN_P b_expr + + ; + +substr_list + : a_expr FROM a_expr FOR a_expr + | a_expr FOR a_expr FROM a_expr + | a_expr FROM a_expr + | a_expr FOR a_expr + | a_expr SIMILAR a_expr ESCAPE a_expr + ; + +trim_list + : a_expr FROM expr_list + | FROM expr_list + | expr_list + ; + +in_expr + : select_with_parens # in_expr_select + | OPEN_PAREN expr_list CLOSE_PAREN # in_expr_list + ; + +case_expr + : CASE case_arg? when_clause_list case_default? END_P + ; + +when_clause_list + : when_clause+ + ; + +when_clause + : WHEN a_expr THEN a_expr + ; + +case_default + : ELSE a_expr + + ; + +case_arg + : a_expr + + ; + +columnref + : colid indirection? + ; + +indirection_el + : DOT (attr_name | STAR) + | OPEN_BRACKET (a_expr | slice_bound_? COLON slice_bound_?) CLOSE_BRACKET + ; + +slice_bound_ + : a_expr + + ; + +indirection + : indirection_el+ + ; + +opt_indirection + : indirection_el* + ; + +/* SQL/JSON support */ +json_passing_clause: + PASSING json_arguments + ; + +json_arguments: + json_argument + | json_arguments ',' json_argument + ; + +json_argument: + json_value_expr AS colLabel + ; + +/* ARRAY is a noise word */ +json_wrapper_behavior: + WITHOUT WRAPPER + | WITHOUT ARRAY WRAPPER + | WITH WRAPPER + | WITH ARRAY WRAPPER + | WITH CONDITIONAL ARRAY WRAPPER + | WITH UNCONDITIONAL ARRAY WRAPPER + | WITH CONDITIONAL WRAPPER + | WITH UNCONDITIONAL WRAPPER + | + ; + +json_behavior: + DEFAULT a_expr + | json_behavior_type + ; + +json_behavior_type: + ERROR + | NULL_P + | TRUE_P + | FALSE_P + | UNKNOWN + | EMPTY_P ARRAY + | EMPTY_P OBJECT_P + /* non-standard, for Oracle compatibility only */ + | EMPTY_P + ; + +json_behavior_clause: + json_behavior ON EMPTY_P + | json_behavior ON ERROR + | json_behavior ON EMPTY_P json_behavior ON ERROR + ; + +json_on_error_clause: + json_behavior ON ERROR + ; + +json_value_expr: + a_expr json_format_clause? + ; + +json_format_clause: + FORMAT_LA JSON ENCODING name + | FORMAT_LA JSON + ; + + +json_quotes_clause: + KEEP QUOTES ON SCALAR STRING_P + | KEEP QUOTES + | OMIT QUOTES ON SCALAR STRING_P + | OMIT QUOTES + ; + +json_returning_clause: + RETURNING typename json_format_clause? + ; + +/* + * We must assign the only-JSON production a precedence less than IDENT in + * order to favor shifting over reduction when JSON is followed by VALUE_P, + * OBJECT_P, or SCALAR. (ARRAY doesn't need that treatment, because it's a + * fully reserved word.) Because json_predicate_type_constraint is always + * followed by json_key_uniqueness_constraint_opt, we also need the only-JSON + * production to have precedence less than WITH and WITHOUT. UNBOUNDED isn't + * really related to this syntax, but it's a convenient choice because it + * already has a precedence less than IDENT for other reasons. + */ +json_predicate_type_constraint: + JSON + | JSON VALUE_P + | JSON ARRAY + | JSON OBJECT_P + | JSON SCALAR + ; + +/* + * KEYS is a noise word here. To avoid shift/reduce conflicts, assign the + * KEYS-less productions a precedence less than IDENT (i.e., less than KEYS). + * This prevents reducing them when the next token is KEYS. + */ +json_key_uniqueness_constraint: + WITH UNIQUE KEYS + | WITH UNIQUE + | WITHOUT UNIQUE KEYS + | WITHOUT UNIQUE + ; + +json_name_and_value_list: + json_name_and_value + | json_name_and_value_list ',' json_name_and_value + ; + +json_name_and_value: + c_expr VALUE_P json_value_expr + | + a_expr ':' json_value_expr + ; + +/* empty means false for objects, true for arrays */ +json_object_constructor_null_clause: + NULL_P ON NULL_P + | ABSENT ON NULL_P + ; + +json_array_constructor_null_clause: + NULL_P ON NULL_P + | ABSENT ON NULL_P + ; + +json_value_expr_list: + json_value_expr + | json_value_expr_list ',' json_value_expr + ; + +json_aggregate_func: + JSON_OBJECTAGG '(' + json_name_and_value + json_object_constructor_null_clause? + json_key_uniqueness_constraint? + json_returning_clause + ')' + | JSON_ARRAYAGG '(' + json_value_expr + json_array_aggregate_order_by_clause? + json_array_constructor_null_clause? + json_returning_clause + ')' + ; + +json_array_aggregate_order_by_clause: + ORDER BY sortby_list + ; + +/***************************************************************************** + * + * target list for SELECT + * + *****************************************************************************/ + +target_list_ + : target_list + + ; + +target_list + : target_el (COMMA target_el)* + ; + +target_el + : a_expr (AS colLabel | bareColLabel |) # target_label + | STAR # target_star + ; + +qualified_name_list + : qualified_name (COMMA qualified_name)* + ; + +qualified_name + : colid indirection? + ; + +name_list + : name (COMMA name)* + ; + +name + : colid + ; + +attr_name + : colLabel + ; + +file_name + : sconst + ; + +func_name + : type_function_name + | colid indirection + ; + +aexprconst + : iconst + | fconst + | sconst + | bconst + | xconst + | func_name (sconst | OPEN_PAREN func_arg_list sort_clause_? CLOSE_PAREN sconst) + | consttypename sconst + | constinterval (sconst interval_? | OPEN_PAREN iconst CLOSE_PAREN sconst) + | TRUE_P + | FALSE_P + | NULL_P + ; + +xconst + : HexadecimalStringConstant + ; + +bconst + : BinaryStringConstant + ; + +fconst + : Numeric + ; + +iconst + : Integral + | BinaryIntegral + | OctalIntegral + | HexadecimalIntegral + ; + +sconst + : anysconst uescape_? + ; + +anysconst + : StringConstant + | UnicodeEscapeStringConstant + | BeginDollarStringConstant DollarText* EndDollarStringConstant + | EscapeStringConstant + ; + +uescape_ + : UESCAPE anysconst + + ; + +signediconst + : iconst + | PLUS iconst + | MINUS iconst + ; + +roleid + : rolespec + ; + +rolespec + : nonreservedword + | CURRENT_USER + | SESSION_USER + ; + +role_list + : rolespec (COMMA rolespec)* + ; + +/* + * Name classification hierarchy. + * + * IDENT is the lexeme returned by the lexer for identifiers that match + * no known keyword. In most cases, we can accept certain keywords as + * names, not only IDENTs. We prefer to accept as many such keywords + * as possible to minimize the impact of "reserved words" on programmers. + * So, we divide names into several possible classes. The classification + * is chosen in part to make keywords acceptable as names wherever possible. + */ + +/* Column identifier --- names that can be column, table, etc names. + */ +colid + : identifier + | unreserved_keyword + | col_name_keyword + ; + +/* Type/function identifier --- names that can be type or function names. + */ +type_function_name + : identifier + | unreserved_keyword + | type_func_name_keyword + ; + +/* Any not-fully-reserved word --- these names can be, eg, role names. + */ +nonreservedword + : identifier + | unreserved_keyword + | col_name_keyword + | type_func_name_keyword + ; + +/* Column label --- allowed labels in "AS" clauses. + * This presently includes *all* Postgres keywords. + */ +colLabel + : identifier + | unreserved_keyword + | col_name_keyword + | type_func_name_keyword + | reserved_keyword + | EXIT //NB: not in gram.y official source. + ; + +/* Bare column label --- names that can be column labels without writing "AS". + * This classification is orthogonal to the other keyword categories. + */ +bareColLabel + : identifier + | bare_label_keyword + ; + +/* + * Keyword category lists. Generally, every keyword present in + * the Postgres grammar should appear in exactly one of these lists. + * + * Put a new keyword into the first list that it can go into without causing + * shift or reduce conflicts. The earlier lists define "less reserved" + * categories of keywords. + * + * Make sure that each keyword's category in kwlist.h matches where + * it is listed here. (Someday we may be able to generate these lists and + * kwlist.h's table from one source of truth.) + */ + +/* "Unreserved" keywords --- available for use as any kind of name. + */ +unreserved_keyword + : ABORT_P + | ABSENT + | ABSOLUTE_P + | ACCESS + | ACTION + | ADD_P + | ADMIN + | AFTER + | AGGREGATE + | ALSO + | ALTER + | ALWAYS + | ASENSITIVE + | ASSERTION + | ASSIGNMENT + | AT + | ATOMIC + | ATTACH + | ATTRIBUTE + | BACKWARD + | BEFORE + | BEGIN_P + | BREADTH + | BY + | CACHE + | CALL + | CALLED + | CASCADE + | CASCADED + | CATALOG + | CHAIN + | CHARACTERISTICS + | CHECKPOINT + | CLASS + | CLOSE + | CLUSTER + | COLUMNS + | COMMENT + | COMMENTS + | COMMIT + | COMMITTED + | COMPRESSION + | CONDITIONAL + | CONFIGURATION + | CONFLICT + | CONNECTION + | CONSTRAINTS + | CONTENT_P + | CONTINUE_P + | CONVERSION_P + | COPY + | COST + | CSV + | CUBE + | CURRENT_P + | CURSOR + | CYCLE + | DATA_P + | DATABASE + | DAY_P + | DEALLOCATE + | DECLARE + | DEFAULTS + | DEFERRED + | DEFINER + | DELETE_P + | DELIMITER + | DELIMITERS + | DEPENDS + | DEPTH + | DETACH + | DICTIONARY + | DISABLE_P + | DISCARD + | DOCUMENT_P + | DOMAIN_P + | DOUBLE_P + | DROP + | EACH + | EMPTY_P + | ENABLE_P + | ENCODING + | ENCRYPTED + | ENUM_P + | ERROR + | ESCAPE + | EVENT + | EXCLUDE + | EXCLUDING + | EXCLUSIVE + | EXECUTE + | EXPLAIN + | EXPRESSION + | EXTENSION + | EXTERNAL + | FAMILY + | FILTER + | FINALIZE + | FIRST_P + | FOLLOWING + | FORCE + | FORMAT + | FORWARD + | FUNCTION + | FUNCTIONS + | GENERATED + | GLOBAL + | GRANTED + | GROUPS + | HANDLER + | HEADER_P + | HOLD + | HOUR_P + | IDENTITY_P + | IF_P + | IMMEDIATE + | IMMUTABLE + | IMPLICIT_P + | IMPORT_P + | INCLUDE + | INCLUDING + | INCREMENT + | INDENT + | INDEX + | INDEXES + | INHERIT + | INHERITS + | INLINE_P + | INPUT_P + | INSENSITIVE + | INSERT + | INSTEAD + | INVOKER + | ISOLATION + | KEEP + | KEY + | KEYS + | LABEL + | LANGUAGE + | LARGE_P + | LAST_P + | LEAKPROOF + | LEVEL + | LISTEN + | LOAD + | LOCAL + | LOCATION + | LOCK_P + | LOCKED + | LOGGED + | MAPPING + | MATCH + | MATCHED + | MATERIALIZED + | MAXVALUE + | MERGE + | METHOD + | MINUTE_P + | MINVALUE + | MODE + | MONTH_P + | MOVE + | NAME_P + | NAMES + | NESTED + | NEW + | NEXT + | NFC + | NFD + | NFKC + | NFKD + | NO + | NORMALIZED + | NOTHING + | NOTIFY + | NOWAIT + | NULLS_P + | OBJECT_P + | OF + | OFF + | OIDS + | OLD + | OMIT + | OPERATOR + | OPTION + | OPTIONS + | ORDINALITY + | OTHERS + | OVER + | OVERRIDING + | OWNED + | OWNER + | PARALLEL + | PARAMETER + | PARSER + | PARTIAL + | PARTITION + | PASSING + | PASSWORD + | PATH + | PERIOD + | PLAN + | PLANS + | POLICY + | PRECEDING + | PREPARE + | PREPARED + | PRESERVE + | PRIOR + | PRIVILEGES + | PROCEDURAL + | PROCEDURE + | PROCEDURES + | PROGRAM + | PUBLICATION + | QUOTE + | QUOTES + | RANGE + | READ + | REASSIGN +// | RECHECK + | RECURSIVE + | REF + | REFERENCING + | REFRESH + | REINDEX + | RELATIVE_P + | RELEASE + | RENAME + | REPEATABLE + | REPLACE + | REPLICA + | RESET + | RESTART + | RESTRICT + | RETURN + | RETURNS + | REVOKE + | ROLE + | ROLLBACK + | ROLLUP + | ROUTINE + | ROUTINES + | ROWS + | RULE + | SAVEPOINT + | SCALAR + | SCHEMA + | SCHEMAS + | SCROLL + | SEARCH + | SECOND_P + | SECURITY + | SEQUENCE + | SEQUENCES + | SERIALIZABLE + | SERVER + | SESSION + | SET + | SETS + | SHARE + | SHOW + | SIMPLE + | SKIP_P + | SNAPSHOT + | SOURCE + | SQL_P + | STABLE + | STANDALONE_P + | START + | STATEMENT + | STATISTICS + | STDIN + | STDOUT + | STORAGE + | STORED + | STRICT_P + | STRING_P + | STRIP_P + | SUBSCRIPTION + | SUPPORT + | SYSID + | SYSTEM_P + | TABLES + | TABLESPACE + | TARGET + | TEMP + | TEMPLATE + | TEMPORARY + | TEXT_P + | TIES + | TRANSACTION + | TRANSFORM + | TRIGGER + | TRUNCATE + | TRUSTED + | TYPE_P + | TYPES_P + | UESCAPE + | UNBOUNDED + | UNCOMMITTED + | UNCONDITIONAL + | UNENCRYPTED + | UNKNOWN + | UNLISTEN + | UNLOGGED + | UNTIL + | UPDATE + | VACUUM + | VALID + | VALIDATE + | VALIDATOR + | VALUE_P + | VARYING + | VERSION_P + | VIEW + | VIEWS + | VOLATILE + | WHITESPACE_P + | WITHIN + | WITHOUT + | WORK + | WRAPPER + | WRITE + | XML_P + | YEAR_P + | YES_P + | ZONE + ; + +/* Column identifier --- keywords that can be column, table, etc names. + * + * Many of these keywords will in fact be recognized as type or function + * names too; but they have special productions for the purpose, and so + * can't be treated as "generic" type or function names. + * + * The type names appearing here are not usable as function names + * because they can be followed by '(' in typename productions, which + * looks too much like a function call for an LR(1) parser. + */ +col_name_keyword + : BETWEEN + | BIGINT + | BIT + | BOOLEAN_P + | CHAR_P + | character + | COALESCE + | DEC + | DECIMAL_P + | EXISTS + | EXTRACT + | FLOAT_P + | GREATEST + | GROUPING + | INOUT + | INT_P + | INTEGER + | INTERVAL + | JSON + | JSON_ARRAY + | JSON_ARRAYAGG + | JSON_EXISTS + | JSON_OBJECT + | JSON_OBJECTAGG + | JSON_QUERY + | JSON_SCALAR + | JSON_SERIALIZE + | JSON_TABLE + | JSON_VALUE + | LEAST + | MERGE_ACTION + | NATIONAL + | NCHAR + | NONE + | NORMALIZE + | NULLIF + | NUMERIC + | OUT_P + | OVERLAY + | POSITION + | PRECISION + | REAL + | ROW + | SETOF + | SMALLINT + | SUBSTRING + | TIME + | TIMESTAMP + | TREAT + | TRIM + | VALUES + | VARCHAR + | XMLATTRIBUTES + | XMLCONCAT + | XMLELEMENT + | XMLEXISTS + | XMLFOREST + | XMLNAMESPACES + | XMLPARSE + | XMLPI + | XMLROOT + | XMLSERIALIZE + | XMLTABLE + ; + +/* Type/function identifier --- keywords that can be type or function names. + * + * Most of these are keywords that are used as operators in expressions; + * in general such keywords can't be column names because they would be + * ambiguous with variables, but they are unambiguous as function identifiers. + * + * Do not include POSITION, SUBSTRING, etc here since they have explicit + * productions in a_expr to support the goofy SQL9x argument syntax. + * - thomas 2000-11-28 + */ +type_func_name_keyword + : AUTHORIZATION + | BINARY + | COLLATION + | CONCURRENTLY + | CROSS + | CURRENT_SCHEMA + | FREEZE + | FULL + | ILIKE + | INNER_P + | IS + | ISNULL + | JOIN + | LEFT + | LIKE + | NATURAL + | NOTNULL + | OUTER_P + | OVERLAPS + | RIGHT + | SIMILAR + | TABLESAMPLE + | VERBOSE + ; + +/* Reserved keyword --- these keywords are usable only as a ColLabel. + * + * Keywords appear here if they could not be distinguished from variable, + * type, or function names in some contexts. Don't put things here unless + * forced to. + */ +reserved_keyword + : ALL + | ANALYSE + | ANALYZE + | AND + | ANY + | ARRAY + | AS + | ASC + | ASYMMETRIC + | BOTH + | CASE + | CAST + | CHECK + | COLLATE + | COLUMN + | CONSTRAINT + | CREATE + | CURRENT_CATALOG + | CURRENT_DATE + | CURRENT_ROLE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CURRENT_USER + | DEFAULT + | DEFERRABLE + | DESC + | DISTINCT + | DO + | ELSE + | END_P + | EXCEPT + | FALSE_P + | FETCH + | FOR + | FOREIGN + | FROM + | GRANT + | GROUP_P + | HAVING + | IN_P + | INITIALLY + | INTERSECT + | INTO + | LATERAL_P + | LEADING + | LIMIT + | LOCALTIME + | LOCALTIMESTAMP + | NOT + | NULL_P + | OFFSET + | ON + | ONLY + | OR + | ORDER + | PLACING + | PRIMARY + | REFERENCES + | RETURNING + | SELECT + | SESSION_USER + | SOME + | SYMMETRIC + | SYSTEM_USER + | TABLE + | THEN + | TO + | TRAILING + | TRUE_P + | UNION + | UNIQUE + | USER + | USING + | VARIADIC + | WHEN + | WHERE + | WINDOW + | WITH + ; + +/* + * While all keywords can be used as column labels when preceded by AS, + * not all of them can be used as a "bare" column label without AS. + * Those that can be used as a bare label must be listed here, + * in addition to appearing in one of the category lists above. + * + * Always add a new keyword to this list if possible. Mark it BARE_LABEL + * in kwlist.h if it is included here, or AS_LABEL if it is not. + */ +bare_label_keyword + : ABORT_P + | ABSENT + | ABSOLUTE_P + | ACCESS + | ACTION + | ADD_P + | ADMIN + | AFTER + | AGGREGATE + | ALL + | ALSO + | ALTER + | ALWAYS + | ANALYSE + | ANALYZE + | AND + | ANY + | ASC + | ASENSITIVE + | ASSERTION + | ASSIGNMENT + | ASYMMETRIC + | AT + | ATOMIC + | ATTACH + | ATTRIBUTE + | AUTHORIZATION + | BACKWARD + | BEFORE + | BEGIN_P + | BETWEEN + | BIGINT + | BINARY + | BIT + | BOOLEAN_P + | BOTH + | BREADTH + | BY + | CACHE + | CALL + | CALLED + | CASCADE + | CASCADED + | CASE + | CAST + | CATALOG + | CHAIN + | CHARACTERISTICS + | CHECK + | CHECKPOINT + | CLASS + | CLOSE + | CLUSTER + | COALESCE + | COLLATE + | COLLATION + | COLUMN + | COLUMNS + | COMMENT + | COMMENTS + | COMMIT + | COMMITTED + | COMPRESSION + | CONCURRENTLY + | CONDITIONAL + | CONFIGURATION + | CONFLICT + | CONNECTION + | CONSTRAINT + | CONSTRAINTS + | CONTENT_P + | CONTINUE_P + | CONVERSION_P + | COPY + | COST + | CROSS + | CSV + | CUBE + | CURRENT_CATALOG + | CURRENT_DATE + | CURRENT_P + | CURRENT_ROLE + | CURRENT_SCHEMA + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CURRENT_USER + | CURSOR + | CYCLE + | DATA_P + | DATABASE + | DEALLOCATE + | DEC + | DECIMAL_P + | DECLARE + | DEFAULT + | DEFAULTS + | DEFERRABLE + | DEFERRED + | DEFINER + | DELETE_P + | DELIMITER + | DELIMITERS + | DEPENDS + | DEPTH + | DESC + | DETACH + | DICTIONARY + | DISABLE_P + | DISCARD + | DISTINCT + | DO + | DOCUMENT_P + | DOMAIN_P + | DOUBLE_P + | DROP + | EACH + | ELSE + | EMPTY_P + | ENABLE_P + | ENCODING + | ENCRYPTED + | END_P + | ENUM_P + | ERROR + | ESCAPE + | EVENT + | EXCLUDE + | EXCLUDING + | EXCLUSIVE + | EXECUTE + | EXISTS + | EXPLAIN + | EXPRESSION + | EXTENSION + | EXTERNAL + | EXTRACT + | FALSE_P + | FAMILY + | FINALIZE + | FIRST_P + | FLOAT_P + | FOLLOWING + | FORCE + | FOREIGN + | FORMAT + | FORWARD + | FREEZE + | FULL + | FUNCTION + | FUNCTIONS + | GENERATED + | GLOBAL + | GRANTED + | GREATEST + | GROUPING + | GROUPS + | HANDLER + | HEADER_P + | HOLD + | IDENTITY_P + | IF_P + | ILIKE + | IMMEDIATE + | IMMUTABLE + | IMPLICIT_P + | IMPORT_P + | IN_P + | INCLUDE + | INCLUDING + | INCREMENT + | INDENT + | INDEX + | INDEXES + | INHERIT + | INHERITS + | INITIALLY + | INLINE_P + | INNER_P + | INOUT + | INPUT_P + | INSENSITIVE + | INSERT + | INSTEAD + | INT_P + | INTEGER + | INTERVAL + | INVOKER + | IS + | ISOLATION + | JOIN + | JSON + | JSON_ARRAY + | JSON_ARRAYAGG + | JSON_EXISTS + | JSON_OBJECT + | JSON_OBJECTAGG + | JSON_QUERY + | JSON_SCALAR + | JSON_SERIALIZE + | JSON_TABLE + | JSON_VALUE + | KEEP + | KEY + | KEYS + | LABEL + | LANGUAGE + | LARGE_P + | LAST_P + | LATERAL_P + | LEADING + | LEAKPROOF + | LEAST + | LEFT + | LEVEL + | LIKE + | LISTEN + | LOAD + | LOCAL + | LOCALTIME + | LOCALTIMESTAMP + | LOCATION + | LOCK_P + | LOCKED + | LOGGED + | MAPPING + | MATCH + | MATCHED + | MATERIALIZED + | MAXVALUE + | MERGE + | MERGE_ACTION + | METHOD + | MINVALUE + | MODE + | MOVE + | NAME_P + | NAMES + | NATIONAL + | NATURAL + | NCHAR + | NESTED + | NEW + | NEXT + | NFC + | NFD + | NFKC + | NFKD + | NO + | NONE + | NORMALIZE + | NORMALIZED + | NOT + | NOTHING + | NOTIFY + | NOWAIT + | NULL_P + | NULLIF + | NULLS_P + | NUMERIC + | OBJECT_P + | OF + | OFF + | OIDS + | OLD + | OMIT + | ONLY + | OPERATOR + | OPTION + | OPTIONS + | OR + | ORDINALITY + | OTHERS + | OUT_P + | OUTER_P + | OVERLAY + | OVERRIDING + | OWNED + | OWNER + | PARALLEL + | PARAMETER + | PARSER + | PARTIAL + | PARTITION + | PASSING + | PASSWORD + | PATH + | PERIOD + | PLACING + | PLAN + | PLANS + | POLICY + | POSITION + | PRECEDING + | PREPARE + | PREPARED + | PRESERVE + | PRIMARY + | PRIOR + | PRIVILEGES + | PROCEDURAL + | PROCEDURE + | PROCEDURES + | PROGRAM + | PUBLICATION + | QUOTE + | QUOTES + | RANGE + | READ + | REAL + | REASSIGN + | RECURSIVE + | REF + | REFERENCES + | REFERENCING + | REFRESH + | REINDEX + | RELATIVE_P + | RELEASE + | RENAME + | REPEATABLE + | REPLACE + | REPLICA + | RESET + | RESTART + | RESTRICT + | RETURN + | RETURNS + | REVOKE + | RIGHT + | ROLE + | ROLLBACK + | ROLLUP + | ROUTINE + | ROUTINES + | ROW + | ROWS + | RULE + | SAVEPOINT + | SCALAR + | SCHEMA + | SCHEMAS + | SCROLL + | SEARCH + | SECURITY + | SELECT + | SEQUENCE + | SEQUENCES + | SERIALIZABLE + | SERVER + | SESSION + | SESSION_USER + | SET + | SETOF + | SETS + | SHARE + | SHOW + | SIMILAR + | SIMPLE + | SKIP_P + | SMALLINT + | SNAPSHOT + | SOME + | SOURCE + | SQL_P + | STABLE + | STANDALONE_P + | START + | STATEMENT + | STATISTICS + | STDIN + | STDOUT + | STORAGE + | STORED + | STRICT_P + | STRING_P + | STRIP_P + | SUBSCRIPTION + | SUBSTRING + | SUPPORT + | SYMMETRIC + | SYSID + | SYSTEM_P + | SYSTEM_USER + | TABLE + | TABLES + | TABLESAMPLE + | TABLESPACE + | TARGET + | TEMP + | TEMPLATE + | TEMPORARY + | TEXT_P + | THEN + | TIES + | TIME + | TIMESTAMP + | TRAILING + | TRANSACTION + | TRANSFORM + | TREAT + | TRIGGER + | TRIM + | TRUE_P + | TRUNCATE + | TRUSTED + | TYPE_P + | TYPES_P + | UESCAPE + | UNBOUNDED + | UNCOMMITTED + | UNCONDITIONAL + | UNENCRYPTED + | UNIQUE + | UNKNOWN + | UNLISTEN + | UNLOGGED + | UNTIL + | UPDATE + | USER + | USING + | VACUUM + | VALID + | VALIDATE + | VALIDATOR + | VALUE_P + | VALUES + | VARCHAR + | VARIADIC + | VERBOSE + | VERSION_P + | VIEW + | VIEWS + | VOLATILE + | WHEN + | WHITESPACE_P + | WORK + | WRAPPER + | WRITE + | XML_P + | XMLATTRIBUTES + | XMLCONCAT + | XMLELEMENT + | XMLEXISTS + | XMLFOREST + | XMLNAMESPACES + | XMLPARSE + | XMLPI + | XMLROOT + | XMLSERIALIZE + | XMLTABLE + | YES_P + | ZONE + ; + + +any_identifier + : colid + ; + +identifier + : Identifier uescape_? + | QuotedIdentifier + | UnicodeQuotedIdentifier + | PLSQLVARIABLENAME + ; diff --git a/src/stewkk/sql/logic/parser/PostgreSQLParserBase.cpp b/src/stewkk/sql/logic/parser/PostgreSQLParserBase.cpp new file mode 100644 index 0000000..599def2 --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLParserBase.cpp @@ -0,0 +1,17 @@ +#include "PostgreSQLParser.h" + +using namespace antlr4; + +void PostgreSQLParserBase::ParseRoutineBody() +{ +} + +bool PostgreSQLParserBase::OnlyAcceptableOps() +{ + auto c = ((CommonTokenStream*)this->getInputStream())->LT(1); + auto text = c->getText(); + return text == "!" || text == "!!" + || text == "!=-" // Code for specific example. + ; +} + diff --git a/src/stewkk/sql/logic/parser/PostgreSQLParserBase.h b/src/stewkk/sql/logic/parser/PostgreSQLParserBase.h new file mode 100644 index 0000000..921293c --- /dev/null +++ b/src/stewkk/sql/logic/parser/PostgreSQLParserBase.h @@ -0,0 +1,10 @@ +#pragma once + +#include "antlr4-runtime.h" + +class PostgreSQLParserBase : public antlr4::Parser { +public: + PostgreSQLParserBase(antlr4::TokenStream *input) : Parser(input) { } + void ParseRoutineBody(); + bool OnlyAcceptableOps(); +}; diff --git a/src/stewkk/sql/main.cpp b/src/stewkk/sql/main.cpp index 91e5881..87e84d1 100644 --- a/src/stewkk/sql/main.cpp +++ b/src/stewkk/sql/main.cpp @@ -2,38 +2,15 @@ #include -#include -#include -#include - -#include +#include +#include +#include using namespace stewkk::sql::codegen; -using namespace stewkk::sql::logic; using namespace antlr4; int main() { - std::ifstream f("example.txt"); - - ANTLRInputStream input(f); - TLexer lexer(&input); - CommonTokenStream tokens(&lexer); - - tokens.fill(); - for (auto token : tokens.getTokens()) { - std::cout << token->toString() << std::endl; - } - - TParser parser(&tokens); - tree::ParseTree* tree = parser.main(); - - std::cout << tree->toStringTree(&parser) << std::endl << std::endl; - - Visitor visitor; - visitor.visit(tree); - - auto ir = visitor.GetIr(); - ir->print(llvm::errs(), nullptr); + std::cout << "Hello\n"; return 0; } From 806c6b3bfa8834306d3bb2a39a3e153756b331b5 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 30 Oct 2025 17:06:56 +0300 Subject: [PATCH 03/43] Upgrade C++ standard --- src/stewkk/sql/CMakeLists.txt | 4 ++-- test/CMakeLists.txt | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 46a70e0..821d0b5 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -18,9 +18,9 @@ target_include_directories(sql PRIVATE $ $ ) -target_compile_features(sql PRIVATE cxx_std_20) +target_compile_features(sql PRIVATE cxx_std_23) set_target_properties(sql PROPERTIES - CXX_STANDART 20 + CXX_STANDART 23 CXX_STANDART_REQUIRED YES CXX_EXTENSIONS YES ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc0fbfc..c194dab 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,6 +5,12 @@ add_executable(unittests) target_sources(unittests PRIVATE ${PROJECT_SOURCE_DIR}/src/stewkk/sql/some_test.cpp ) +target_compile_features(unittests PRIVATE cxx_std_23) +set_target_properties(unittests PROPERTIES + CXX_STANDART 23 + CXX_STANDART_REQUIRED YES + CXX_EXTENSIONS YES +) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_libraries(unittests PRIVATE gmock_main) From 105b9866d4f479fbf8dd386c38438322ffe4575d Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 30 Oct 2025 17:35:39 +0300 Subject: [PATCH 04/43] Add build with sanitizers --- .gitignore | 1 + CMakeLists.txt | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.gitignore b/.gitignore index bd2af54..d63dfcd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /build/ /CMakeFiles/ **/codegen/ +/build-sanitizers/ diff --git a/CMakeLists.txt b/CMakeLists.txt index abe5f38..f0671f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,25 @@ project( list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}") +option(SANITIZE_ADDRESS "Enable AddressSanitizer" OFF) +option(SANITIZE_UNDEFINED "Enable UndefinedBehaviorSanitizer" OFF) +option(SANITIZE_THREADS "Enable ThreadSanitizer" OFF) + +if (SANITIZE_ADDRESS) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +if (SANITIZE_UNDEFINED) + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) +endif() + +if (SANITIZE_THREADS) + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) +endif() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) From fdcd8436a3cd956f4019a9a53f9673edf0f877cb Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 1 Nov 2025 11:27:53 +0300 Subject: [PATCH 05/43] Add library target --- include/stewkk/sql/logic/parser/parser.hpp | 5 +++ src/stewkk/sql/CMakeLists.txt | 44 +++++++++++++------ src/stewkk/sql/logic/parser/parser.cpp | 11 +++++ .../parser/parser_test.cpp} | 6 ++- src/stewkk/sql/main.cpp | 9 ---- test/CMakeLists.txt | 10 ++++- 6 files changed, 59 insertions(+), 26 deletions(-) create mode 100644 include/stewkk/sql/logic/parser/parser.hpp create mode 100644 src/stewkk/sql/logic/parser/parser.cpp rename src/stewkk/sql/{some_test.cpp => logic/parser/parser_test.cpp} (74%) diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp new file mode 100644 index 0000000..814fb68 --- /dev/null +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace stewkk::sql { + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 821d0b5..89f1dc4 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -3,8 +3,7 @@ include(SetupLLVM) add_definitions(-DANTLR4CPP_STATIC) -add_executable(sql - main.cpp +add_library(libsql logic/parser/codegen/PostgreSQLLexer.cpp logic/parser/codegen/PostgreSQLParser.cpp logic/parser/codegen/PostgreSQLParserBaseListener.cpp @@ -13,10 +12,35 @@ add_executable(sql logic/parser/codegen/PostgreSQLParserVisitor.cpp logic/parser/PostgreSQLLexerBase.cpp logic/parser/PostgreSQLParserBase.cpp + logic/parser/parser.cpp +) +add_library(stewkk::libsql ALIAS libsql) +target_compile_features(libsql PUBLIC cxx_std_23) +set_target_properties(libsql PROPERTIES + CXX_STANDART 23 + CXX_STANDART_REQUIRED YES + CXX_EXTENSIONS YES ) -target_include_directories(sql PRIVATE - $ - $ +set_target_properties(libsql PROPERTIES OUTPUT_NAME stewkk_sql) +target_include_directories( + libsql PUBLIC $ + $ + ${ANTLR4_INCLUDE_DIRS} + ${LLVM_INCLUDE_DIRS} + $ + $ + $ +) +target_compile_options(libsql PRIVATE ${BASE_COMPILE_FLAGS}) +target_link_options(libsql PRIVATE ${BASE_LINK_FLAGS}) +llvm_map_components_to_libnames(llvm_libs support core irreader) +target_link_libraries(libsql PRIVATE + antlr4_static + ${llvm_libs} +) + +add_executable(sql + main.cpp ) target_compile_features(sql PRIVATE cxx_std_23) set_target_properties(sql PROPERTIES @@ -24,13 +48,7 @@ set_target_properties(sql PROPERTIES CXX_STANDART_REQUIRED YES CXX_EXTENSIONS YES ) - target_compile_options(sql PRIVATE ${BASE_COMPILE_FLAGS}) -target_include_directories(sql PRIVATE - ${ANTLR4_INCLUDE_DIRS} - ${LLVM_INCLUDE_DIRS} - $ -) -llvm_map_components_to_libnames(llvm_libs support core irreader) -target_link_libraries(sql PRIVATE antlr4_static ${llvm_libs} +target_link_libraries(sql PRIVATE + libsql ) diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp new file mode 100644 index 0000000..0583fa1 --- /dev/null +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -0,0 +1,11 @@ +#include + +#include + +#include +#include +#include + +namespace stewkk::sql { + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/some_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp similarity index 74% rename from src/stewkk/sql/some_test.cpp rename to src/stewkk/sql/logic/parser/parser_test.cpp index c518b23..ba74797 100644 --- a/src/stewkk/sql/some_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -1,5 +1,7 @@ #include +#include + using ::testing::Eq; using ::testing::Optional; @@ -8,8 +10,8 @@ using std::string_view_literals::operator""sv; namespace stewkk::sql { -TEST(ExampleTest, APlusB) { - ASSERT_THAT(2*2, Eq(4)); +TEST(ParserTest, APlusB) { + } } // namespace stewkk::sql diff --git a/src/stewkk/sql/main.cpp b/src/stewkk/sql/main.cpp index 87e84d1..4d3a5ae 100644 --- a/src/stewkk/sql/main.cpp +++ b/src/stewkk/sql/main.cpp @@ -1,14 +1,5 @@ #include -#include - -#include -#include -#include - -using namespace stewkk::sql::codegen; -using namespace antlr4; - int main() { std::cout << "Hello\n"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c194dab..cc0b4e1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,7 @@ include(GoogleTest) add_executable(unittests) target_sources(unittests PRIVATE - ${PROJECT_SOURCE_DIR}/src/stewkk/sql/some_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/parser/parser_test.cpp ) target_compile_features(unittests PRIVATE cxx_std_23) set_target_properties(unittests PROPERTIES @@ -11,7 +11,13 @@ set_target_properties(unittests PROPERTIES CXX_STANDART_REQUIRED YES CXX_EXTENSIONS YES ) +target_include_directories( + unittests PUBLIC $ + $ + ${ANTLR4_INCLUDE_DIRS} + ${LLVM_INCLUDE_DIRS} +) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) -target_link_libraries(unittests PRIVATE gmock_main) +target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static) gtest_discover_tests(unittests) From 4c901fb02fef1879a99f741b0f8a9db109fb44d9 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 12 Nov 2025 22:11:49 +0300 Subject: [PATCH 06/43] Impl getting ast from antlr --- include/stewkk/sql/logic/parser/parser.hpp | 4 ++++ src/stewkk/sql/logic/parser/parser.cpp | 17 +++++++++++++++++ src/stewkk/sql/logic/parser/parser_test.cpp | 10 ++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index 814fb68..6f1df01 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -1,5 +1,9 @@ #pragma once +#include + namespace stewkk::sql { +void GetAST(std::istream& in); + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 0583fa1..b1f35e7 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -8,4 +8,21 @@ namespace stewkk::sql { +void GetAST(std::istream& in) { + antlr4::ANTLRInputStream antlr_input(in); + codegen::PostgreSQLLexer lexer(&antlr_input); + + antlr4::CommonTokenStream tokens(&lexer); + tokens.fill(); + + for (auto token : tokens.getTokens()) { + std::cout << token->toString() << std::endl; + } + + codegen::PostgreSQLParser parser(&tokens); + antlr4::tree::ParseTree* tree = parser.root(); + + std::cout << tree->toStringTree(&parser) << std::endl << std::endl; +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index ba74797..98f1da2 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -1,17 +1,19 @@ #include +#include + #include using ::testing::Eq; -using ::testing::Optional; - -using std::string_literals::operator""s; -using std::string_view_literals::operator""sv; namespace stewkk::sql { TEST(ParserTest, APlusB) { + std::stringstream s{"CREATE TABLE hobbies_r (\nname text,\nperson text\n);"}; + + GetAST(s); + ASSERT_THAT(2, Eq(3)); } } // namespace stewkk::sql From 8c8645616c599714c1e057e7a40790e1573bd200 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 13 Nov 2025 01:05:10 +0300 Subject: [PATCH 07/43] Impl parsing simple select statement --- flake.nix | 1 + include/stewkk/sql/logic/parser/parser.hpp | 10 +++- src/stewkk/sql/logic/parser/parser.cpp | 66 ++++++++++++++++++++- src/stewkk/sql/logic/parser/parser_test.cpp | 9 +-- 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index f14e4ed..6389826 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ cmake zlib zlib.dev + gdb ]; nativeBuildInputs = [ diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index 6f1df01..ff1ab93 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -1,9 +1,17 @@ #pragma once #include +#include namespace stewkk::sql { -void GetAST(std::istream& in); +struct Table { + std::string name; + + auto operator<=>(const Table& other) const = default; +}; +using Operator = std::variant; + +Operator GetAST(std::istream& in); } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index b1f35e7..5ffbd70 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -8,7 +8,60 @@ namespace stewkk::sql { -void GetAST(std::istream& in) { +namespace { + +class Visitor : public codegen::PostgreSQLParserBaseVisitor { + public: + explicit Visitor(codegen::PostgreSQLParser* parser); + virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; + virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; + virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; + + private: + codegen::PostgreSQLParser* parser_; +}; + +Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} + +std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { + return visitStmtblock(ctx->stmtblock()); +} + +std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) { + return visitStmt(ctx->stmt()[0]); +} + +std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { + std::cout << ctx->toStringTree(parser_, true) << std::endl; + auto target = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->target_list_() + ->target_list() + ->target_el()[0] + ->getText(); + auto from_ident = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->from_clause() + ->from_list() + ->table_ref()[0] + ->relation_expr() + ->qualified_name() + ->colid() + ->identifier() + ->getText(); + + std::cout << target << ' ' << from_ident << std::endl; + + return Operator{Table{from_ident}}; +} + +} // namespace + +Operator GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); codegen::PostgreSQLLexer lexer(&antlr_input); @@ -22,7 +75,16 @@ void GetAST(std::istream& in) { codegen::PostgreSQLParser parser(&tokens); antlr4::tree::ParseTree* tree = parser.root(); - std::cout << tree->toStringTree(&parser) << std::endl << std::endl; + std::cout << tree->toStringTree(&parser, true) << std::endl << std::endl; + + Visitor visitor(&parser); + auto res = visitor.visit(tree); + + if (Operator* op = std::any_cast(&res)) { + return *op; + } + + return Table{"NOOP"}; } } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 98f1da2..fb07ee0 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -5,15 +5,16 @@ #include using ::testing::Eq; +using ::testing::VariantWith; namespace stewkk::sql { -TEST(ParserTest, APlusB) { - std::stringstream s{"CREATE TABLE hobbies_r (\nname text,\nperson text\n);"}; +TEST(ParserTest, SelectAllFromSingleTable) { + std::stringstream s{"SELECT * FROM users;"}; - GetAST(s); + Operator got = GetAST(s); - ASSERT_THAT(2, Eq(3)); + ASSERT_THAT(got, VariantWith
(Table{"users"})); } } // namespace stewkk::sql From a6829ac05be7ad97eeded541138976b90498bc19 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Fri, 14 Nov 2025 23:00:06 +0300 Subject: [PATCH 08/43] Support select statement with single column output --- include/stewkk/sql/logic/parser/parser.hpp | 15 ++++++++++++++- src/stewkk/sql/logic/parser/parser.cpp | 18 ++++++++++++++++-- src/stewkk/sql/logic/parser/parser_test.cpp | 10 ++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index ff1ab93..78c8cc6 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -1,16 +1,29 @@ #pragma once #include +#include #include +#include namespace stewkk::sql { +struct Table; +struct Projection; + +using Operator = std::variant; + struct Table { std::string name; auto operator<=>(const Table& other) const = default; }; -using Operator = std::variant
; + +struct Projection { + std::vector attributes; + std::shared_ptr source; + + bool operator==(const Projection& other) const; +}; Operator GetAST(std::istream& in); diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 5ffbd70..fdcd9ca 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -16,11 +16,17 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; + virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; private: codegen::PostgreSQLParser* parser_; }; +std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { + std::cout << std::format("visiting {}, number of children {}\n", node->toString(), node->children.size()); + return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); +} + Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { @@ -56,11 +62,19 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* std::cout << target << ' ' << from_ident << std::endl; - return Operator{Table{from_ident}}; + auto table_operator = Operator{Table{from_ident}}; + if (target == "*") { + return table_operator; + } + return Operator{Projection{{target}, std::make_shared(table_operator)}}; } } // namespace +bool Projection::operator==(const Projection& other) const { + return attributes == other.attributes && *source == *other.source; +} + Operator GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); codegen::PostgreSQLLexer lexer(&antlr_input); @@ -81,7 +95,7 @@ Operator GetAST(std::istream& in) { auto res = visitor.visit(tree); if (Operator* op = std::any_cast(&res)) { - return *op; + return std::move(*op); } return Table{"NOOP"}; diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index fb07ee0..a6fddcb 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -17,4 +17,14 @@ TEST(ParserTest, SelectAllFromSingleTable) { ASSERT_THAT(got, VariantWith
(Table{"users"})); } +TEST(ParserTest, SelectColumnFromSingleTable) { + std::stringstream s{"SELECT id FROM users;"}; + + Operator got = GetAST(s); + + ASSERT_THAT(got, + VariantWith(Projection{std::vector{"id"}, + std::make_shared(Table{"users"})})); +} + } // namespace stewkk::sql From c366e9f0607cf6df4a376e1570a110343119a8b9 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Fri, 14 Nov 2025 23:14:18 +0300 Subject: [PATCH 09/43] Support multiple columns projection in select stmt --- src/stewkk/sql/logic/parser/parser.cpp | 16 +++++++++------- src/stewkk/sql/logic/parser/parser_test.cpp | 12 +++++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index fdcd9ca..7ed32df 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include @@ -39,14 +41,16 @@ std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ct std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { std::cout << ctx->toStringTree(parser_, true) << std::endl; - auto target = ctx->select_no_parens() + auto targets = ctx->select_no_parens() ->select_clause() ->simple_select_intersect()[0] ->simple_select_pramary()[0] ->target_list_() ->target_list() - ->target_el()[0] - ->getText(); + ->target_el() | std::ranges::views::transform([] (const auto& target_node) { + return target_node->getText(); + }) | std::ranges::to(); + auto from_ident = ctx->select_no_parens() ->select_clause() ->simple_select_intersect()[0] @@ -60,13 +64,11 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ->identifier() ->getText(); - std::cout << target << ' ' << from_ident << std::endl; - auto table_operator = Operator{Table{from_ident}}; - if (target == "*") { + if (targets == std::vector{{"*"}}) { return table_operator; } - return Operator{Projection{{target}, std::make_shared(table_operator)}}; + return Operator{Projection{targets, std::make_shared(table_operator)}}; } } // namespace diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index a6fddcb..39006f5 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -17,7 +17,7 @@ TEST(ParserTest, SelectAllFromSingleTable) { ASSERT_THAT(got, VariantWith
(Table{"users"})); } -TEST(ParserTest, SelectColumnFromSingleTable) { +TEST(ParserTest, SelectSingleColumnFromSingleTable) { std::stringstream s{"SELECT id FROM users;"}; Operator got = GetAST(s); @@ -27,4 +27,14 @@ TEST(ParserTest, SelectColumnFromSingleTable) { std::make_shared(Table{"users"})})); } +TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { + std::stringstream s{"SELECT id, email, phone FROM users;"}; + + Operator got = GetAST(s); + + ASSERT_THAT(got, + VariantWith(Projection{std::vector{"id", "email", "phone"}, + std::make_shared(Table{"users"})})); +} + } // namespace stewkk::sql From 88f896b6e48c90054891857aebb89f1c2e42f3c2 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 19 Nov 2025 00:08:45 +0300 Subject: [PATCH 10/43] Support where clause --- include/stewkk/sql/logic/parser/parser.hpp | 36 ++++++++- src/stewkk/sql/logic/parser/parser.cpp | 90 ++++++++++++++++++++- src/stewkk/sql/logic/parser/parser_test.cpp | 43 +++++++++- test/static/parser/expected.dot | 5 ++ 4 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 test/static/parser/expected.dot diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index 78c8cc6..48e9afe 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -7,10 +7,15 @@ namespace stewkk::sql { +// TODO: Attribute should be struct {std::string table, std::string attribute} +using Attribute = std::string; +using IntConst = std::int64_t; + struct Table; struct Projection; +struct Filter; -using Operator = std::variant; +using Operator = std::variant; struct Table { std::string name; @@ -19,12 +24,39 @@ struct Table { }; struct Projection { - std::vector attributes; + std::vector attributes; std::shared_ptr source; bool operator==(const Projection& other) const; }; +struct BinaryExpression; + +using Expression = std::variant; + +enum class BinaryOp { + kGt, +}; + +std::string ToString(BinaryOp binop); + +struct BinaryExpression { + std::shared_ptr lhs; + BinaryOp binop; + std::shared_ptr rhs; + + bool operator==(const BinaryExpression& other) const; +}; + +struct Filter { + Expression expr; + std::shared_ptr source; + + bool operator==(const Filter& other) const; +}; + Operator GetAST(std::istream& in); +std::string GetDotRepresentation(const Operator& op); + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 7ed32df..5cffbcf 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -12,6 +12,18 @@ namespace stewkk::sql { namespace { +class ExprVisitor : public codegen::PostgreSQLParserBaseVisitor { + virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; +}; + +std::any ExprVisitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { + auto lhs = ctx->a_expr_like(0); + auto rhs = ctx->a_expr_like(1); + return Expression{ + BinaryExpression{std::make_shared(Attribute{lhs->getText()}), BinaryOp::kGt, + std::make_shared(IntConst{std::stoi(rhs->getText())})}}; +} + class Visitor : public codegen::PostgreSQLParserBaseVisitor { public: explicit Visitor(codegen::PostgreSQLParser* parser); @@ -64,11 +76,27 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ->identifier() ->getText(); - auto table_operator = Operator{Table{from_ident}}; - if (targets == std::vector{{"*"}}) { - return table_operator; + auto where_clause = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->where_clause(); + + Operator result{Table{from_ident}}; + + if (where_clause) { + ExprVisitor expr_visitor; + auto where_expr_any = expr_visitor.visit(where_clause->a_expr()); + if (Expression* where_expr = std::any_cast(&where_expr_any)) { + result = Filter{*where_expr, std::make_shared(result)}; + } + } + + if (targets != std::vector{{"*"}}) { + result = Projection{targets, std::make_shared(result)}; } - return Operator{Projection{targets, std::make_shared(table_operator)}}; + + return result; } } // namespace @@ -77,6 +105,14 @@ bool Projection::operator==(const Projection& other) const { return attributes == other.attributes && *source == *other.source; } +bool Filter::operator==(const Filter& other) const { + return expr == other.expr && *source == *other.source; +} + +bool BinaryExpression::operator==(const BinaryExpression& other) const { + return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; +} + Operator GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); codegen::PostgreSQLLexer lexer(&antlr_input); @@ -103,4 +139,50 @@ Operator GetAST(std::istream& in) { return Table{"NOOP"}; } +std::string GetDotRepresentation(const Expression& expr) { + struct DotFormatter { + std::string operator()(const BinaryExpression& expr) { + return std::format("{} {} {}", std::visit(DotFormatter{}, *expr.lhs), ToString(expr.binop), + std::visit(DotFormatter{}, *expr.rhs)); + } + std::string operator()(const Attribute& expr) { + return expr; + } + std::string operator()(const IntConst& expr) { + return std::to_string(expr); + } + }; + return std::visit(DotFormatter{}, expr); +} + +std::string GetDotRepresentation(const Operator& op) { + struct DotFormatter { + std::pair operator()(const Projection& op) { + auto attrs + = op.attributes | std::ranges::views::join_with(',') | std::ranges::to(); + auto node = std::format("\"π {}\"", attrs); + auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); + return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; + } + std::pair operator()(const Filter& op) { + auto node = std::format("\"σ {}\"", GetDotRepresentation(op.expr)); + auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); + return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; + } + std::pair operator()(const Table& op) { + auto node = std::format("\"{}\"", op.name); + return {node, node}; + } + }; + auto [_, code] = std::visit(DotFormatter{}, op); + return std::format("digraph G {{ rankdir=BT; {} }}\n", code); +} + +std::string ToString(BinaryOp binop) { + switch (binop) { + case BinaryOp::kGt: + return ">"; + } +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 39006f5..1dfc947 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include @@ -9,6 +11,15 @@ using ::testing::VariantWith; namespace stewkk::sql { +const static std::string kProjectDir = std::getenv("PWD"); + +std::string ReadFromFile(std::filesystem::path path) { + std::ifstream f{path}; + std::ostringstream stream; + stream << f.rdbuf(); + return stream.str(); +} + TEST(ParserTest, SelectAllFromSingleTable) { std::stringstream s{"SELECT * FROM users;"}; @@ -18,23 +29,47 @@ TEST(ParserTest, SelectAllFromSingleTable) { } TEST(ParserTest, SelectSingleColumnFromSingleTable) { - std::stringstream s{"SELECT id FROM users;"}; + std::stringstream s{"SELECT users.id FROM users;"}; Operator got = GetAST(s); ASSERT_THAT(got, - VariantWith(Projection{std::vector{"id"}, + VariantWith(Projection{std::vector{"users.id"}, std::make_shared(Table{"users"})})); } TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { - std::stringstream s{"SELECT id, email, phone FROM users;"}; + std::stringstream s{"SELECT users.id, users.email, users.phone FROM users;"}; Operator got = GetAST(s); ASSERT_THAT(got, - VariantWith(Projection{std::vector{"id", "email", "phone"}, + VariantWith(Projection{std::vector{"users.id", "users.email", "users.phone"}, std::make_shared(Table{"users"})})); } +TEST(ParserTest, SelectWithWhereClause) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; + + Operator got = GetAST(s); + + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{"users.id"}, + std::make_shared( + Filter{Expression{BinaryExpression{ + std::make_shared(Attribute{"users.age"}), + BinaryOp::kGt, std::make_shared(IntConst{18})}}, + std::make_shared(Table{"users"})})})); +} + +TEST(ParserTest, GetDotRepresentation) { + std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; + Operator op = GetAST(s); + auto expected = ReadFromFile(kProjectDir+"/test/static/parser/expected.dot"); + + auto got = GetDotRepresentation(op); + + ASSERT_THAT(got, Eq(expected)); +} + } // namespace stewkk::sql diff --git a/test/static/parser/expected.dot b/test/static/parser/expected.dot new file mode 100644 index 0000000..614fdce --- /dev/null +++ b/test/static/parser/expected.dot @@ -0,0 +1,5 @@ +digraph G { rankdir=BT; "π users.id" +"σ users.age > 18" -> "π users.id" +"σ users.age > 18" +"users" -> "σ users.age > 18" +"users" } From cc3366c4e5bb7a4551c5250fe5b5cdde65c4d882 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 20 Nov 2025 18:53:25 +0300 Subject: [PATCH 11/43] Change attributes list to be pair (table, column) --- include/stewkk/sql/logic/parser/parser.hpp | 11 +++- src/stewkk/sql/logic/parser/parser.cpp | 67 ++++++++++++++------- src/stewkk/sql/logic/parser/parser_test.cpp | 64 ++++++++++---------- 3 files changed, 86 insertions(+), 56 deletions(-) diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index 48e9afe..4fbe0af 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -7,8 +7,15 @@ namespace stewkk::sql { -// TODO: Attribute should be struct {std::string table, std::string attribute} -using Attribute = std::string; +struct Attribute { + std::string table; + std::string name; + + auto operator<=>(const Attribute& other) const = default; +}; + +std::string ToString(const Attribute& attr); + using IntConst = std::int64_t; struct Table; diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 5cffbcf..95b16c7 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -12,24 +12,14 @@ namespace stewkk::sql { namespace { -class ExprVisitor : public codegen::PostgreSQLParserBaseVisitor { - virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; -}; - -std::any ExprVisitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { - auto lhs = ctx->a_expr_like(0); - auto rhs = ctx->a_expr_like(1); - return Expression{ - BinaryExpression{std::make_shared(Attribute{lhs->getText()}), BinaryOp::kGt, - std::make_shared(IntConst{std::stoi(rhs->getText())})}}; -} - class Visitor : public codegen::PostgreSQLParserBaseVisitor { public: explicit Visitor(codegen::PostgreSQLParser* parser); virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; + virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; + virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; private: @@ -41,6 +31,28 @@ std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); } +std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { + auto table = ctx->colid()->getText(); + auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); + std::cout << "visitColumnref " << table << ' ' << column << std::endl; + return Attribute{std::move(table), std::move(column)}; +} + +std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { + auto lhs = ctx->a_expr_like(0); + auto rhs = ctx->a_expr_like(1); + if (!lhs || !rhs) { + return visitChildren(ctx); + } + auto lhs_any = visit(lhs); + if (Attribute* attr = std::any_cast(&lhs_any)) { + return Expression{ + BinaryExpression{std::make_shared(*attr), BinaryOp::kGt, + std::make_shared(IntConst{std::stoi(rhs->getText())})}}; + } + std::unreachable(); +} + Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { @@ -53,15 +65,22 @@ std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ct std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { std::cout << ctx->toStringTree(parser_, true) << std::endl; - auto targets = ctx->select_no_parens() + auto target_list = ctx->select_no_parens() ->select_clause() ->simple_select_intersect()[0] ->simple_select_pramary()[0] ->target_list_() - ->target_list() - ->target_el() | std::ranges::views::transform([] (const auto& target_node) { - return target_node->getText(); + ->target_list(); + std::vector targets; + if (!(target_list->target_el().size() == 1 && target_list->target_el(0)->getText() == "*")) { + targets = target_list->target_el() | std::ranges::views::transform([this] (const auto& target_node) { + auto expr_any = visit(target_node); + if (Attribute* attr = std::any_cast(&expr_any)) { + return *attr; + } + std::unreachable(); }) | std::ranges::to(); + } auto from_ident = ctx->select_no_parens() ->select_clause() @@ -85,14 +104,13 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* Operator result{Table{from_ident}}; if (where_clause) { - ExprVisitor expr_visitor; - auto where_expr_any = expr_visitor.visit(where_clause->a_expr()); + auto where_expr_any = visit(where_clause->a_expr()); if (Expression* where_expr = std::any_cast(&where_expr_any)) { result = Filter{*where_expr, std::make_shared(result)}; } } - if (targets != std::vector{{"*"}}) { + if (!targets.empty()) { result = Projection{targets, std::make_shared(result)}; } @@ -146,7 +164,7 @@ std::string GetDotRepresentation(const Expression& expr) { std::visit(DotFormatter{}, *expr.rhs)); } std::string operator()(const Attribute& expr) { - return expr; + return ToString(expr); } std::string operator()(const IntConst& expr) { return std::to_string(expr); @@ -158,8 +176,9 @@ std::string GetDotRepresentation(const Expression& expr) { std::string GetDotRepresentation(const Operator& op) { struct DotFormatter { std::pair operator()(const Projection& op) { - auto attrs - = op.attributes | std::ranges::views::join_with(',') | std::ranges::to(); + auto attrs = op.attributes + | std::views::transform([](const Attribute& attr) { return ToString(attr); }) + | std::views::join_with(',') | std::ranges::to(); auto node = std::format("\"π {}\"", attrs); auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; @@ -185,4 +204,8 @@ std::string ToString(BinaryOp binop) { } } +std::string ToString(const Attribute& attr) { + return std::format("{}.{}", attr.table, attr.name); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 1dfc947..1915906 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -1,8 +1,8 @@ #include -#include #include #include +#include #include @@ -14,62 +14,62 @@ namespace stewkk::sql { const static std::string kProjectDir = std::getenv("PWD"); std::string ReadFromFile(std::filesystem::path path) { - std::ifstream f{path}; - std::ostringstream stream; - stream << f.rdbuf(); - return stream.str(); + std::ifstream f{path}; + std::ostringstream stream; + stream << f.rdbuf(); + return stream.str(); } TEST(ParserTest, SelectAllFromSingleTable) { - std::stringstream s{"SELECT * FROM users;"}; + std::stringstream s{"SELECT * FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s); - ASSERT_THAT(got, VariantWith
(Table{"users"})); + ASSERT_THAT(got, VariantWith
(Table{"users"})); } TEST(ParserTest, SelectSingleColumnFromSingleTable) { - std::stringstream s{"SELECT users.id FROM users;"}; + std::stringstream s{"SELECT users.id FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s); - ASSERT_THAT(got, - VariantWith(Projection{std::vector{"users.id"}, - std::make_shared(Table{"users"})})); + ASSERT_THAT(got, VariantWith(Projection{std::vector{{"users", "id"}}, + std::make_shared(Table{"users"})})); } TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { - std::stringstream s{"SELECT users.id, users.email, users.phone FROM users;"}; + std::stringstream s{"SELECT users.id, users.email, users.phone FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s); - ASSERT_THAT(got, - VariantWith(Projection{std::vector{"users.id", "users.email", "users.phone"}, - std::make_shared(Table{"users"})})); + ASSERT_THAT(got, + VariantWith(Projection{ + std::vector{{"users", "id"}, {"users", "email"}, {"users", "phone"}}, + std::make_shared(Table{"users"})})); } TEST(ParserTest, SelectWithWhereClause) { - std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; + std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator got = GetAST(s); + Operator got = GetAST(s); - ASSERT_THAT(got, VariantWith(Projection{ - std::vector{"users.id"}, - std::make_shared( - Filter{Expression{BinaryExpression{ - std::make_shared(Attribute{"users.age"}), - BinaryOp::kGt, std::make_shared(IntConst{18})}}, - std::make_shared(Table{"users"})})})); + ASSERT_THAT(got, VariantWith(Projection{ + std::vector{{"users", "id"}}, + std::make_shared( + Filter{Expression{BinaryExpression{ + std::make_shared(Attribute{"users", "age"}), + BinaryOp::kGt, std::make_shared(IntConst{18})}}, + std::make_shared(Table{"users"})})})); } TEST(ParserTest, GetDotRepresentation) { - std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator op = GetAST(s); - auto expected = ReadFromFile(kProjectDir+"/test/static/parser/expected.dot"); + std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; + Operator op = GetAST(s); + auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected.dot"); - auto got = GetDotRepresentation(op); + auto got = GetDotRepresentation(op); - ASSERT_THAT(got, Eq(expected)); + ASSERT_THAT(got, Eq(expected)); } } // namespace stewkk::sql From 2b624951aa32fb4b2bd7577ca80df11ea4387d30 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 26 Nov 2025 16:18:51 +0300 Subject: [PATCH 12/43] Refactor --- include/stewkk/sql/logic/parser/parser.hpp | 60 +------- .../models/parser/relational_algebra_ast.hpp | 65 +++++++++ src/stewkk/sql/CMakeLists.txt | 2 + src/stewkk/sql/logic/parser/parser.cpp | 135 +----------------- src/stewkk/sql/logic/parser/visitor.cpp | 100 +++++++++++++ src/stewkk/sql/logic/parser/visitor.hpp | 22 +++ .../models/parser/relational_algebra_ast.cpp | 28 ++++ 7 files changed, 221 insertions(+), 191 deletions(-) create mode 100644 include/stewkk/sql/models/parser/relational_algebra_ast.hpp create mode 100644 src/stewkk/sql/logic/parser/visitor.cpp create mode 100644 src/stewkk/sql/logic/parser/visitor.hpp create mode 100644 src/stewkk/sql/models/parser/relational_algebra_ast.cpp diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index 4fbe0af..a22342f 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -1,66 +1,10 @@ #pragma once #include -#include -#include -#include -namespace stewkk::sql { - -struct Attribute { - std::string table; - std::string name; - - auto operator<=>(const Attribute& other) const = default; -}; - -std::string ToString(const Attribute& attr); - -using IntConst = std::int64_t; - -struct Table; -struct Projection; -struct Filter; - -using Operator = std::variant; - -struct Table { - std::string name; - - auto operator<=>(const Table& other) const = default; -}; - -struct Projection { - std::vector attributes; - std::shared_ptr source; +#include - bool operator==(const Projection& other) const; -}; - -struct BinaryExpression; - -using Expression = std::variant; - -enum class BinaryOp { - kGt, -}; - -std::string ToString(BinaryOp binop); - -struct BinaryExpression { - std::shared_ptr lhs; - BinaryOp binop; - std::shared_ptr rhs; - - bool operator==(const BinaryExpression& other) const; -}; - -struct Filter { - Expression expr; - std::shared_ptr source; - - bool operator==(const Filter& other) const; -}; +namespace stewkk::sql { Operator GetAST(std::istream& in); diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp new file mode 100644 index 0000000..c3302cf --- /dev/null +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include + +namespace stewkk::sql { + +struct Attribute { + std::string table; + std::string name; + + auto operator<=>(const Attribute& other) const = default; +}; + +std::string ToString(const Attribute& attr); + +using IntConst = std::int64_t; + +struct Table; +struct Projection; +struct Filter; + +using Operator = std::variant; + +struct Table { + std::string name; + + auto operator<=>(const Table& other) const = default; +}; + +struct Projection { + std::vector attributes; + std::shared_ptr source; + + bool operator==(const Projection& other) const; +}; + +struct BinaryExpression; + +using Expression = std::variant; + +enum class BinaryOp { + kGt, +}; + +std::string ToString(BinaryOp binop); + +struct BinaryExpression { + std::shared_ptr lhs; + BinaryOp binop; + std::shared_ptr rhs; + + bool operator==(const BinaryExpression& other) const; +}; + +struct Filter { + Expression expr; + std::shared_ptr source; + + bool operator==(const Filter& other) const; +}; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 89f1dc4..b1764ff 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(libsql logic/parser/PostgreSQLLexerBase.cpp logic/parser/PostgreSQLParserBase.cpp logic/parser/parser.cpp + logic/parser/visitor.cpp + models/parser/relational_algebra_ast.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 95b16c7..7c9030d 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -6,130 +6,10 @@ #include #include -#include -namespace stewkk::sql { - -namespace { - -class Visitor : public codegen::PostgreSQLParserBaseVisitor { - public: - explicit Visitor(codegen::PostgreSQLParser* parser); - virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; - virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; - virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; - virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; - virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; - virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; - - private: - codegen::PostgreSQLParser* parser_; -}; - -std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { - std::cout << std::format("visiting {}, number of children {}\n", node->toString(), node->children.size()); - return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); -} - -std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { - auto table = ctx->colid()->getText(); - auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); - std::cout << "visitColumnref " << table << ' ' << column << std::endl; - return Attribute{std::move(table), std::move(column)}; -} - -std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { - auto lhs = ctx->a_expr_like(0); - auto rhs = ctx->a_expr_like(1); - if (!lhs || !rhs) { - return visitChildren(ctx); - } - auto lhs_any = visit(lhs); - if (Attribute* attr = std::any_cast(&lhs_any)) { - return Expression{ - BinaryExpression{std::make_shared(*attr), BinaryOp::kGt, - std::make_shared(IntConst{std::stoi(rhs->getText())})}}; - } - std::unreachable(); -} +#include -Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} - -std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { - return visitStmtblock(ctx->stmtblock()); -} - -std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) { - return visitStmt(ctx->stmt()[0]); -} - -std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { - std::cout << ctx->toStringTree(parser_, true) << std::endl; - auto target_list = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->target_list_() - ->target_list(); - std::vector targets; - if (!(target_list->target_el().size() == 1 && target_list->target_el(0)->getText() == "*")) { - targets = target_list->target_el() | std::ranges::views::transform([this] (const auto& target_node) { - auto expr_any = visit(target_node); - if (Attribute* attr = std::any_cast(&expr_any)) { - return *attr; - } - std::unreachable(); - }) | std::ranges::to(); - } - - auto from_ident = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->from_clause() - ->from_list() - ->table_ref()[0] - ->relation_expr() - ->qualified_name() - ->colid() - ->identifier() - ->getText(); - - auto where_clause = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->where_clause(); - - Operator result{Table{from_ident}}; - - if (where_clause) { - auto where_expr_any = visit(where_clause->a_expr()); - if (Expression* where_expr = std::any_cast(&where_expr_any)) { - result = Filter{*where_expr, std::make_shared(result)}; - } - } - - if (!targets.empty()) { - result = Projection{targets, std::make_shared(result)}; - } - - return result; -} - -} // namespace - -bool Projection::operator==(const Projection& other) const { - return attributes == other.attributes && *source == *other.source; -} - -bool Filter::operator==(const Filter& other) const { - return expr == other.expr && *source == *other.source; -} - -bool BinaryExpression::operator==(const BinaryExpression& other) const { - return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; -} +namespace stewkk::sql { Operator GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); @@ -197,15 +77,4 @@ std::string GetDotRepresentation(const Operator& op) { return std::format("digraph G {{ rankdir=BT; {} }}\n", code); } -std::string ToString(BinaryOp binop) { - switch (binop) { - case BinaryOp::kGt: - return ">"; - } -} - -std::string ToString(const Attribute& attr) { - return std::format("{}.{}", attr.table, attr.name); -} - } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp new file mode 100644 index 0000000..a2936f0 --- /dev/null +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -0,0 +1,100 @@ +#include + +#include + +#include + +namespace stewkk::sql { + +std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { + std::cout << std::format("visiting {}, number of children {}\n", node->toString(), node->children.size()); + return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); +} + +std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { + auto table = ctx->colid()->getText(); + auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); + std::cout << "visitColumnref " << table << ' ' << column << std::endl; + return Attribute{std::move(table), std::move(column)}; +} + +std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { + auto lhs = ctx->a_expr_like(0); + auto rhs = ctx->a_expr_like(1); + if (!lhs || !rhs) { + return visitChildren(ctx); + } + auto lhs_any = visit(lhs); + if (Attribute* attr = std::any_cast(&lhs_any)) { + return Expression{ + BinaryExpression{std::make_shared(*attr), BinaryOp::kGt, + std::make_shared(IntConst{std::stoi(rhs->getText())})}}; + } + std::unreachable(); +} + +Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} + +std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { + return visitStmtblock(ctx->stmtblock()); +} + +std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) { + return visitStmt(ctx->stmt()[0]); +} + +std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { + std::cout << ctx->toStringTree(parser_, true) << std::endl; + auto target_list = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->target_list_() + ->target_list(); + std::vector targets; + if (!(target_list->target_el().size() == 1 && target_list->target_el(0)->getText() == "*")) { + targets = target_list->target_el() | std::ranges::views::transform([this] (const auto& target_node) { + auto expr_any = visit(target_node); + if (Attribute* attr = std::any_cast(&expr_any)) { + return *attr; + } + std::unreachable(); + }) | std::ranges::to(); + } + + auto from_ident = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->from_clause() + ->from_list() + ->table_ref()[0] + ->relation_expr() + ->qualified_name() + ->colid() + ->identifier() + ->getText(); + + auto where_clause = ctx->select_no_parens() + ->select_clause() + ->simple_select_intersect()[0] + ->simple_select_pramary()[0] + ->where_clause(); + + Operator result{Table{from_ident}}; + + if (where_clause) { + auto where_expr_any = visit(where_clause->a_expr()); + if (Expression* where_expr = std::any_cast(&where_expr_any)) { + result = Filter{*where_expr, std::make_shared(result)}; + } + } + + if (!targets.empty()) { + result = Projection{targets, std::make_shared(result)}; + } + + return result; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp new file mode 100644 index 0000000..343265d --- /dev/null +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace stewkk::sql { + +class Visitor : public codegen::PostgreSQLParserBaseVisitor { + public: + explicit Visitor(codegen::PostgreSQLParser* parser); + virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; + virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; + virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; + virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; + virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; + virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; + + private: + codegen::PostgreSQLParser* parser_; +}; + + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp new file mode 100644 index 0000000..8db6bde --- /dev/null +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -0,0 +1,28 @@ +#include + +namespace stewkk::sql { + +bool Projection::operator==(const Projection& other) const { + return attributes == other.attributes && *source == *other.source; +} + +bool Filter::operator==(const Filter& other) const { + return expr == other.expr && *source == *other.source; +} + +bool BinaryExpression::operator==(const BinaryExpression& other) const { + return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; +} + +std::string ToString(BinaryOp binop) { + switch (binop) { + case BinaryOp::kGt: + return ">"; + } +} + +std::string ToString(const Attribute& attr) { + return std::format("{}.{}", attr.table, attr.name); +} + +} // namespace stewkk::sql From 93565fa46a93a87259a575062b0c2647170aa775 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 26 Nov 2025 16:29:18 +0300 Subject: [PATCH 13/43] Add basic error handling --- include/stewkk/sql/logic/parser/parser.hpp | 3 +- include/stewkk/sql/logic/result/error.hpp | 35 ++++++++++++++++++++ include/stewkk/sql/logic/result/result.hpp | 28 ++++++++++++++++ src/stewkk/sql/CMakeLists.txt | 2 ++ src/stewkk/sql/logic/parser/parser.cpp | 6 +++- src/stewkk/sql/logic/parser/parser_test.cpp | 29 ++++++++++++++--- src/stewkk/sql/logic/parser/visitor.cpp | 4 +++ src/stewkk/sql/logic/result/error.cpp | 36 +++++++++++++++++++++ src/stewkk/sql/logic/result/result.cpp | 7 ++++ 9 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 include/stewkk/sql/logic/result/error.hpp create mode 100644 include/stewkk/sql/logic/result/result.hpp create mode 100644 src/stewkk/sql/logic/result/error.cpp create mode 100644 src/stewkk/sql/logic/result/result.cpp diff --git a/include/stewkk/sql/logic/parser/parser.hpp b/include/stewkk/sql/logic/parser/parser.hpp index a22342f..cf99d18 100644 --- a/include/stewkk/sql/logic/parser/parser.hpp +++ b/include/stewkk/sql/logic/parser/parser.hpp @@ -3,10 +3,11 @@ #include #include +#include namespace stewkk::sql { -Operator GetAST(std::istream& in); +Result GetAST(std::istream& in); std::string GetDotRepresentation(const Operator& op); diff --git a/include/stewkk/sql/logic/result/error.hpp b/include/stewkk/sql/logic/result/error.hpp new file mode 100644 index 0000000..1350ac9 --- /dev/null +++ b/include/stewkk/sql/logic/result/error.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +namespace stewkk::sql { + +enum class ErrorType { + kUnknown, + kNotFound, + kNotMaster, + kNotConnected, +}; + +class Error : public std::exception { +private: + struct ErrorData { + ErrorType type; + std::string message; + }; + +public: + Error(ErrorType type, std::string message); + std::string What() const; + virtual const char* what() const noexcept override; + Error& Wrap(ErrorType type, std::string message); + bool Wraps(ErrorType type) const; + +private: + std::vector wrapped_; + std::optional what_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/result/result.hpp b/include/stewkk/sql/logic/result/result.hpp new file mode 100644 index 0000000..f2c070f --- /dev/null +++ b/include/stewkk/sql/logic/result/result.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +namespace stewkk::sql { + +struct EmptyResult {}; + +template using Result = std::expected; + +template auto Ok(T&& v = {}) { return Result(std::forward(v)); } + +template +std::unexpected MakeError(const std::format_string fmt, Args&&... args) { + return std::unexpected(Error{ErrorType, std::format(fmt, std::forward(args)...)}); +} + +template +std::unexpected WrapError(Result&& result, const std::format_string fmt, Args&&... args) { + return std::unexpected(result.error().Wrap(ErrorType, std::format(fmt, std::forward(args)...))); +} + +std::string What(const Error& error); + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index b1764ff..ebfe788 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -15,6 +15,8 @@ add_library(libsql logic/parser/parser.cpp logic/parser/visitor.cpp models/parser/relational_algebra_ast.cpp + logic/result/error.cpp + logic/result/result.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 7c9030d..18078ab 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -11,7 +11,7 @@ namespace stewkk::sql { -Operator GetAST(std::istream& in) { +Result GetAST(std::istream& in) { antlr4::ANTLRInputStream antlr_input(in); codegen::PostgreSQLLexer lexer(&antlr_input); @@ -25,6 +25,10 @@ Operator GetAST(std::istream& in) { codegen::PostgreSQLParser parser(&tokens); antlr4::tree::ParseTree* tree = parser.root(); + if (parser.getNumberOfSyntaxErrors() != 0) { + return MakeError("ill-formed query"); + } + std::cout << tree->toStringTree(&parser, true) << std::endl << std::endl; Visitor visitor(&parser); diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 1915906..8365a48 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -23,7 +23,7 @@ std::string ReadFromFile(std::filesystem::path path) { TEST(ParserTest, SelectAllFromSingleTable) { std::stringstream s{"SELECT * FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s).value(); ASSERT_THAT(got, VariantWith
(Table{"users"})); } @@ -31,7 +31,7 @@ TEST(ParserTest, SelectAllFromSingleTable) { TEST(ParserTest, SelectSingleColumnFromSingleTable) { std::stringstream s{"SELECT users.id FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s).value(); ASSERT_THAT(got, VariantWith(Projection{std::vector{{"users", "id"}}, std::make_shared(Table{"users"})})); @@ -40,7 +40,7 @@ TEST(ParserTest, SelectSingleColumnFromSingleTable) { TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { std::stringstream s{"SELECT users.id, users.email, users.phone FROM users;"}; - Operator got = GetAST(s); + Operator got = GetAST(s).value(); ASSERT_THAT(got, VariantWith(Projection{ @@ -51,7 +51,7 @@ TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { TEST(ParserTest, SelectWithWhereClause) { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator got = GetAST(s); + Operator got = GetAST(s).value(); ASSERT_THAT(got, VariantWith(Projection{ std::vector{{"users", "id"}}, @@ -64,7 +64,7 @@ TEST(ParserTest, SelectWithWhereClause) { TEST(ParserTest, GetDotRepresentation) { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 18;"}; - Operator op = GetAST(s); + Operator op = GetAST(s).value(); auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected.dot"); auto got = GetDotRepresentation(op); @@ -72,4 +72,23 @@ TEST(ParserTest, GetDotRepresentation) { ASSERT_THAT(got, Eq(expected)); } +TEST(ParserTest, DISABLED_SelectWithBooleanExpression) { + std::stringstream s{"SELECT TRUE AND NULL OR FALSE AND NOT NULL;"}; + + Operator got = GetAST(s).value(); +} + +TEST(ParserTest, SyntaxError) { + std::stringstream s{"xxx;"}; + + auto got = GetAST(s).error().What(); + + ASSERT_THAT(got, Eq("ill-formed query")); +} + +/* +** ORDER BY +** full support of a_expr + */ + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index a2936f0..852194a 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -40,6 +40,10 @@ std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { } std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) { + auto stmt = ctx->stmt(); + if (stmt.empty()) { + return {}; + } return visitStmt(ctx->stmt()[0]); } diff --git a/src/stewkk/sql/logic/result/error.cpp b/src/stewkk/sql/logic/result/error.cpp new file mode 100644 index 0000000..d4002d7 --- /dev/null +++ b/src/stewkk/sql/logic/result/error.cpp @@ -0,0 +1,36 @@ +#include + +#include +#include + +namespace stewkk::sql { + +namespace { +const static std::string kWhat = "no data"; +} + +Error::Error(ErrorType type, std::string message) + : wrapped_({ErrorData{std::move(type), std::move(message)}}) {} + +std::string Error::What() const { + std::ostringstream res; + res << wrapped_.back().message; + auto view_without_first = wrapped_ | std::views::reverse | std::views::drop(1); + for (const auto& el : view_without_first) { + res << ": " << el.message; + } + return res.str(); +} + +Error& Error::Wrap(ErrorType type, std::string message) { + wrapped_.emplace_back(std::move(type), std::move(message)); + return *this; +} + +bool Error::Wraps(ErrorType type) const { + return std::ranges::find(wrapped_, type, &ErrorData::type) != wrapped_.end(); +} + +const char* Error::what() const noexcept { return kWhat.c_str(); } + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/result/result.cpp b/src/stewkk/sql/logic/result/result.cpp new file mode 100644 index 0000000..af48f32 --- /dev/null +++ b/src/stewkk/sql/logic/result/result.cpp @@ -0,0 +1,7 @@ +#include + +namespace stewkk::sql { + +std::string What(const Error& error) { return error.What(); } + +} // namespace stewkk::sql From 51eac1360b4dd1b0c0f09c0e4c40dc8d01dee6eb Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 26 Nov 2025 20:00:53 +0300 Subject: [PATCH 14/43] Improve error handling --- include/stewkk/sql/logic/result/error.hpp | 5 ++--- src/stewkk/sql/logic/parser/parser.cpp | 10 ++++++++-- src/stewkk/sql/logic/parser/parser_test.cpp | 15 +++++++++++++-- src/stewkk/sql/logic/parser/visitor.cpp | 8 +++++--- src/stewkk/sql/logic/parser/visitor.hpp | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/include/stewkk/sql/logic/result/error.hpp b/include/stewkk/sql/logic/result/error.hpp index 1350ac9..6ee6ec7 100644 --- a/include/stewkk/sql/logic/result/error.hpp +++ b/include/stewkk/sql/logic/result/error.hpp @@ -8,9 +8,8 @@ namespace stewkk::sql { enum class ErrorType { kUnknown, - kNotFound, - kNotMaster, - kNotConnected, + kSyntaxError, + kQueryNotSupported, }; class Error : public std::exception { diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 18078ab..28c07fe 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -26,13 +26,19 @@ Result GetAST(std::istream& in) { antlr4::tree::ParseTree* tree = parser.root(); if (parser.getNumberOfSyntaxErrors() != 0) { - return MakeError("ill-formed query"); + return MakeError("syntax error"); } std::cout << tree->toStringTree(&parser, true) << std::endl << std::endl; Visitor visitor(&parser); - auto res = visitor.visit(tree); + + std::any res; + try { + res = visitor.visit(tree); + } catch (const Error& ex) { + return std::unexpected(ex); + } if (Operator* op = std::any_cast(&res)) { return std::move(*op); diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 8365a48..13cfd85 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -7,6 +7,7 @@ #include using ::testing::Eq; +using ::testing::IsTrue; using ::testing::VariantWith; namespace stewkk::sql { @@ -81,9 +82,19 @@ TEST(ParserTest, DISABLED_SelectWithBooleanExpression) { TEST(ParserTest, SyntaxError) { std::stringstream s{"xxx;"}; - auto got = GetAST(s).error().What(); + auto got = GetAST(s).error(); - ASSERT_THAT(got, Eq("ill-formed query")); + ASSERT_THAT(got.What(), Eq("syntax error")); + ASSERT_THAT(got.Wraps(ErrorType::kSyntaxError), IsTrue()); +} + +TEST(ParserTest, NotSupportedError) { + std::stringstream s{"INSERT INTO users (id) VALUES (1);"}; + + auto got = GetAST(s).error(); + + ASSERT_THAT(got.What(), Eq("INSERT is not supported")); + ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); } /* diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 852194a..7d71928 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -3,18 +3,17 @@ #include #include +#include namespace stewkk::sql { std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { - std::cout << std::format("visiting {}, number of children {}\n", node->toString(), node->children.size()); return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); } std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { auto table = ctx->colid()->getText(); auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); - std::cout << "visitColumnref " << table << ' ' << column << std::endl; return Attribute{std::move(table), std::move(column)}; } @@ -48,7 +47,6 @@ std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ct } std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { - std::cout << ctx->toStringTree(parser_, true) << std::endl; auto target_list = ctx->select_no_parens() ->select_clause() ->simple_select_intersect()[0] @@ -101,4 +99,8 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* return result; } +std::any Visitor::visitInsertstmt(codegen::PostgreSQLParser::InsertstmtContext *ctx) { + throw Error{ErrorType::kQueryNotSupported, "INSERT is not supported"}; +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 343265d..02b14e3 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -12,6 +12,7 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; + virtual std::any visitInsertstmt(codegen::PostgreSQLParser::InsertstmtContext *ctx) override; virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; private: From d8e1e4f7ccb114b6670fb5a65d3c8d76b748ee22 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 26 Nov 2025 20:25:11 +0300 Subject: [PATCH 15/43] Report unsupported statements --- src/stewkk/sql/logic/parser/parser_test.cpp | 3 ++- src/stewkk/sql/logic/parser/visitor.cpp | 30 ++++++++++++++++++--- src/stewkk/sql/logic/parser/visitor.hpp | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 13cfd85..822d941 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -93,13 +93,14 @@ TEST(ParserTest, NotSupportedError) { auto got = GetAST(s).error(); - ASSERT_THAT(got.What(), Eq("INSERT is not supported")); + ASSERT_THAT(got.What(), Eq("insertstmt is currently unsupported")); ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); } /* ** ORDER BY ** full support of a_expr +** aggregations: SELECT kind, sum(len) AS total FROM films GROUP BY kind; */ } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 7d71928..2d5395e 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -7,6 +7,32 @@ namespace stewkk::sql { +std::any Visitor::visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) { + if (!ctx->children.empty() && !ctx->selectstmt()) { + auto& rule_names = parser_->getRuleNames(); + std::vector unsupported_rules; + for (auto child : ctx->children) { + if (!child) { + continue; + } + if (antlr4::RuleContext* rule_context = dynamic_cast(child)) { + size_t rule_index = rule_context->getRuleIndex(); + unsupported_rules.emplace_back(rule_names[rule_index]); + } + } + std::string rules_list + = unsupported_rules | std::views::join_with(',') | std::ranges::to(); + if (rules_list.empty()) { + throw Error{ErrorType::kQueryNotSupported, "query is not supported"}; + } + // NOTE: concatenation of errors could be useful here + throw Error{ErrorType::kQueryNotSupported, + std::format("{} {} currently unsupported", std::move(rules_list), + unsupported_rules.size() == 1 ? "is" : "are")}; + } + return visitChildren(ctx); +} + std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); } @@ -99,8 +125,4 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* return result; } -std::any Visitor::visitInsertstmt(codegen::PostgreSQLParser::InsertstmtContext *ctx) { - throw Error{ErrorType::kQueryNotSupported, "INSERT is not supported"}; -} - } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 02b14e3..00f5731 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -8,11 +8,11 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { public: explicit Visitor(codegen::PostgreSQLParser* parser); virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; + virtual std::any visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) override; virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; - virtual std::any visitInsertstmt(codegen::PostgreSQLParser::InsertstmtContext *ctx) override; virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; private: From 550fb6b05d3a81a6e2df2f9d092f3c151bc5788a Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Wed, 26 Nov 2025 20:29:41 +0300 Subject: [PATCH 16/43] Add empty query test --- src/stewkk/sql/logic/parser/parser.cpp | 2 +- src/stewkk/sql/logic/parser/parser_test.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 28c07fe..0b97475 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -44,7 +44,7 @@ Result GetAST(std::istream& in) { return std::move(*op); } - return Table{"NOOP"}; + return Table{"_EMPTY_TABLE_"}; } std::string GetDotRepresentation(const Expression& expr) { diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 822d941..ca9c918 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -97,6 +97,14 @@ TEST(ParserTest, NotSupportedError) { ASSERT_THAT(got.Wraps(ErrorType::kQueryNotSupported), IsTrue()); } +TEST(ParserTest, EmptyQuery) { + std::stringstream s{""}; + + Operator got = GetAST(s).value(); + + ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); +} + /* ** ORDER BY ** full support of a_expr From 1d0f3632e3abb2eb6ec69437a28714a04effbaa5 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 27 Nov 2025 16:32:24 +0300 Subject: [PATCH 17/43] Implement proper handling of selectstmt --- .../models/parser/relational_algebra_ast.hpp | 3 + src/stewkk/sql/logic/parser/parser.cpp | 2 +- src/stewkk/sql/logic/parser/parser_test.cpp | 16 ++ src/stewkk/sql/logic/parser/visitor.cpp | 143 +++++++++++++----- src/stewkk/sql/logic/parser/visitor.hpp | 11 +- 5 files changed, 134 insertions(+), 41 deletions(-) diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index c3302cf..746ae36 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -7,6 +7,8 @@ namespace stewkk::sql { +constexpr static std::string kEmptyTableName = "_EMPTY_TABLE_"; + struct Attribute { std::string table; std::string name; @@ -32,6 +34,7 @@ struct Table { struct Projection { std::vector attributes; + // TODO: type erasure pattern???? std::any??? std::shared_ptr source; bool operator==(const Projection& other) const; diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 0b97475..960c7f9 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -44,7 +44,7 @@ Result GetAST(std::istream& in) { return std::move(*op); } - return Table{"_EMPTY_TABLE_"}; + return Table{kEmptyTableName}; } std::string GetDotRepresentation(const Expression& expr) { diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index ca9c918..fe335f2 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -105,6 +105,22 @@ TEST(ParserTest, EmptyQuery) { ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); } +TEST(ParserTest, SelectWithParens) { + std::stringstream s{"((SELECT * FROM users));"}; + + Operator got = GetAST(s).value(); + + ASSERT_THAT(got, VariantWith
(Table{"users"})); +} + +TEST(ParserTest, EmptySelect) { + std::stringstream s{"SELECT;"}; + + Operator got = GetAST(s).value(); + + ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); +} + /* ** ORDER BY ** full support of a_expr diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 2d5395e..14d4144 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -33,8 +33,20 @@ std::any Visitor::visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) { return visitChildren(ctx); } -std::any Visitor::visitChildren(antlr4::tree::ParseTree *node) { - return codegen::PostgreSQLParserBaseVisitor::visitChildren(node); +std::any Visitor::visit(antlr4::tree::ParseTree *tree) { + std::string rule_name; + int indentation = 0; + auto& rule_names = parser_->getRuleNames(); + if (antlr4::RuleContext *rule_context = dynamic_cast(tree)) { + size_t rule_index = rule_context->getRuleIndex(); + rule_name = rule_names[rule_index]; + indentation = rule_context->depth()-1; + } + std::string indentation_str(indentation*4, ' '); + std::cout << std::format("{}visiting rule: {}\n", indentation_str, rule_name); + auto tmp = codegen::PostgreSQLParserBaseVisitor::visit(tree); + std::cout << std::format("{}exiting rule: {}\n", indentation_str, rule_name); + return tmp; } std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { @@ -61,7 +73,7 @@ std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareC Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} std::any Visitor::visitRoot(codegen::PostgreSQLParser::RootContext *ctx) { - return visitStmtblock(ctx->stmtblock()); + return visit(ctx->stmtblock()); } std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) { @@ -69,33 +81,38 @@ std::any Visitor::visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ct if (stmt.empty()) { return {}; } - return visitStmt(ctx->stmt()[0]); + return visit(ctx->stmt()[0]); +} + +std::any Visitor::visitSimple_select_intersect(codegen::PostgreSQLParser::Simple_select_intersectContext *ctx) { + // TODO: INTERSECT (with ALL or DISTINCT) + return visit(ctx->simple_select_pramary().front()); } -std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) { - auto target_list = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->target_list_() - ->target_list(); +std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext *ctx) { std::vector targets; - if (!(target_list->target_el().size() == 1 && target_list->target_el(0)->getText() == "*")) { - targets = target_list->target_el() | std::ranges::views::transform([this] (const auto& target_node) { - auto expr_any = visit(target_node); - if (Attribute* attr = std::any_cast(&expr_any)) { - return *attr; - } - std::unreachable(); - }) | std::ranges::to(); + if (!(ctx->target_el().size() == 1 && ctx->target_el(0)->getText() == "*")) { + targets = ctx->target_el() + | std::ranges::views::transform([this](const auto &target_node) { + auto expr_any = visit(target_node); + if (Attribute *attr = std::any_cast(&expr_any)) { + return *attr; + } + // TODO: support arbitrary expressions here + std::unreachable(); + }) + | std::ranges::to(); } - auto from_ident = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->from_clause() - ->from_list() + if (!targets.empty()) { + return Projection{targets, nullptr}; + } + + return {}; +} + +std::any Visitor::visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) { + auto from_ident = ctx->from_list() ->table_ref()[0] ->relation_expr() ->qualified_name() @@ -103,26 +120,76 @@ std::any Visitor::visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ->identifier() ->getText(); - auto where_clause = ctx->select_no_parens() - ->select_clause() - ->simple_select_intersect()[0] - ->simple_select_pramary()[0] - ->where_clause(); + return Table{from_ident}; +} + +std::any Visitor::visitSimple_select_pramary( + codegen::PostgreSQLParser::Simple_select_pramaryContext *ctx) { + if (ctx->select_with_parens()) { + return visit(ctx->select_with_parens()); + } + if (!ctx->SELECT()) { + throw Error{ErrorType::kQueryNotSupported, "VALUES and TABLE clauses are not supported"}; + } + if (ctx->distinct_clause()) { + // TODO: support distinct clause + throw Error{ErrorType::kQueryNotSupported, "DISTINCT clause is not supported"}; + } - Operator result{Table{from_ident}}; + // NOTE: all_clause_ is ignored + // TODO: support group_clause, having_clause - if (where_clause) { - auto where_expr_any = visit(where_clause->a_expr()); - if (Expression* where_expr = std::any_cast(&where_expr_any)) { - result = Filter{*where_expr, std::make_shared(result)}; - } + Operator result = Table{kEmptyTableName}; + + if (ctx->from_clause()) { + result = std::any_cast
(visit(ctx->from_clause())); } - if (!targets.empty()) { - result = Projection{targets, std::make_shared(result)}; + if (ctx->where_clause()) { + auto filter_op = std::any_cast(visit(ctx->where_clause())); + filter_op.source = std::make_shared(std::move(result)); + result = std::move(filter_op); + } + + if (ctx->target_list_()) { + auto target_list_opt = visit(ctx->target_list_()); + if (target_list_opt.has_value()) { + auto projection_op = std::any_cast(target_list_opt); + projection_op.source = std::make_shared(std::move(result)); + result = std::move(projection_op); + } } return result; } +std::any Visitor::visitWhere_clause(codegen::PostgreSQLParser::Where_clauseContext *ctx) { + auto where_expr = std::any_cast(visit(ctx->a_expr())); + return Filter{where_expr, nullptr}; +} + +std::any Visitor::visitSelect_clause(codegen::PostgreSQLParser::Select_clauseContext *ctx) { + // TODO: UNION and EXCEPT (with ALL or DISTINCT) + return visit(ctx->simple_select_intersect().front()); +} + +std::any Visitor::visitSelect_with_parens(codegen::PostgreSQLParser::Select_with_parensContext *ctx) { + if (ctx->select_with_parens()) { + return visit(ctx->select_with_parens()); + } + return visit(ctx->select_no_parens()); +} + +std::any Visitor::visitSelect_no_parens(codegen::PostgreSQLParser::Select_no_parensContext *ctx) { + if (ctx->with_clause()) { + // TODO + } + + auto select_clause = std::any_cast(visit(ctx->select_clause())); + + // TODO: remaining clauses + + return select_clause; +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 00f5731..7887508 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -7,13 +7,20 @@ namespace stewkk::sql { class Visitor : public codegen::PostgreSQLParserBaseVisitor { public: explicit Visitor(codegen::PostgreSQLParser* parser); + virtual std::any visit(antlr4::tree::ParseTree *tree) override; virtual std::any visitRoot(codegen::PostgreSQLParser::RootContext* ctx) override; virtual std::any visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) override; virtual std::any visitStmtmulti(codegen::PostgreSQLParser::StmtmultiContext* ctx) override; - virtual std::any visitSelectstmt(codegen::PostgreSQLParser::SelectstmtContext* ctx) override; + virtual std::any visitSelect_no_parens(codegen::PostgreSQLParser::Select_no_parensContext *ctx) override; virtual std::any visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) override; virtual std::any visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) override; - virtual std::any visitChildren(antlr4::tree::ParseTree *node) override; + virtual std::any visitSelect_clause(codegen::PostgreSQLParser::Select_clauseContext *ctx) override; + virtual std::any visitSimple_select_intersect(codegen::PostgreSQLParser::Simple_select_intersectContext *ctx) override; + virtual std::any visitSimple_select_pramary(codegen::PostgreSQLParser::Simple_select_pramaryContext *ctx) override; + virtual std::any visitTarget_list(codegen::PostgreSQLParser::Target_listContext *ctx) override; + virtual std::any visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) override; + virtual std::any visitWhere_clause(codegen::PostgreSQLParser::Where_clauseContext *ctx) override; + virtual std::any visitSelect_with_parens(codegen::PostgreSQLParser::Select_with_parensContext *ctx) override; private: codegen::PostgreSQLParser* parser_; From 6492b8b7ed1a6cc766917f9238bee5499e9756a3 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 4 Dec 2025 00:25:49 +0300 Subject: [PATCH 18/43] Implement arbitrary expressions parsing --- include/stewkk/sql/logic/result/error.hpp | 1 + .../models/parser/relational_algebra_ast.hpp | 45 ++- src/stewkk/sql/logic/parser/parser.cpp | 12 +- src/stewkk/sql/logic/parser/parser_test.cpp | 35 +- src/stewkk/sql/logic/parser/visitor.cpp | 353 +++++++++++++++++- src/stewkk/sql/logic/parser/visitor.hpp | 35 ++ .../models/parser/relational_algebra_ast.cpp | 65 +++- test/static/parser/expected_arithmetical.dot | 3 + 8 files changed, 518 insertions(+), 31 deletions(-) create mode 100644 test/static/parser/expected_arithmetical.dot diff --git a/include/stewkk/sql/logic/result/error.hpp b/include/stewkk/sql/logic/result/error.hpp index 6ee6ec7..b3d4bc4 100644 --- a/include/stewkk/sql/logic/result/error.hpp +++ b/include/stewkk/sql/logic/result/error.hpp @@ -9,6 +9,7 @@ namespace stewkk::sql { enum class ErrorType { kUnknown, kSyntaxError, + kConversionError, kQueryNotSupported, }; diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index 746ae36..684cc6a 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -32,20 +32,40 @@ struct Table { auto operator<=>(const Table& other) const = default; }; +struct BinaryExpression; +struct UnaryExpression; + +enum class Literal { + kNull, + kTrue, + kFalse, + kUnknown, +}; + +std::string ToString(Literal literal); + +using Expression = std::variant; + +std::string ToString(const Expression& expr); + struct Projection { - std::vector attributes; - // TODO: type erasure pattern???? std::any??? + std::vector expressions; std::shared_ptr source; bool operator==(const Projection& other) const; }; -struct BinaryExpression; - -using Expression = std::variant; - enum class BinaryOp { kGt, + kEq, + kOr, + kAnd, + kPlus, + kMinus, + kMul, + kDiv, + kMod, + kPow, }; std::string ToString(BinaryOp binop); @@ -58,6 +78,19 @@ struct BinaryExpression { bool operator==(const BinaryExpression& other) const; }; +enum class UnaryOp { + kNot, +}; + +std::string ToString(UnaryOp op); + +struct UnaryExpression { + UnaryOp op; + std::shared_ptr child; + + bool operator==(const UnaryExpression& other) const; +}; + struct Filter { Expression expr; std::shared_ptr source; diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 960c7f9..005c32d 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -53,12 +53,18 @@ std::string GetDotRepresentation(const Expression& expr) { return std::format("{} {} {}", std::visit(DotFormatter{}, *expr.lhs), ToString(expr.binop), std::visit(DotFormatter{}, *expr.rhs)); } + std::string operator()(const UnaryExpression& expr) { + return std::format("({} {})",ToString(expr.op), std::visit(DotFormatter{}, *expr.child)); + } std::string operator()(const Attribute& expr) { return ToString(expr); } std::string operator()(const IntConst& expr) { return std::to_string(expr); } + std::string operator()(const Literal& expr) { + return ToString(expr); + } }; return std::visit(DotFormatter{}, expr); } @@ -66,10 +72,10 @@ std::string GetDotRepresentation(const Expression& expr) { std::string GetDotRepresentation(const Operator& op) { struct DotFormatter { std::pair operator()(const Projection& op) { - auto attrs = op.attributes - | std::views::transform([](const Attribute& attr) { return ToString(attr); }) + auto exprs = op.expressions + | std::views::transform([](const Expression& expr) { return ToString(expr); }) | std::views::join_with(',') | std::ranges::to(); - auto node = std::format("\"π {}\"", attrs); + auto node = std::format("\"π {}\"", exprs); auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; } diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index fe335f2..c0e784c 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -34,7 +34,7 @@ TEST(ParserTest, SelectSingleColumnFromSingleTable) { Operator got = GetAST(s).value(); - ASSERT_THAT(got, VariantWith(Projection{std::vector{{"users", "id"}}, + ASSERT_THAT(got, VariantWith(Projection{std::vector{{Attribute{"users", "id"}}}, std::make_shared(Table{"users"})})); } @@ -45,7 +45,7 @@ TEST(ParserTest, SelectMultipleColumnsFromSingleTable) { ASSERT_THAT(got, VariantWith(Projection{ - std::vector{{"users", "id"}, {"users", "email"}, {"users", "phone"}}, + std::vector{Attribute{"users", "id"}, Attribute{"users", "email"}, Attribute{"users", "phone"}}, std::make_shared(Table{"users"})})); } @@ -55,7 +55,7 @@ TEST(ParserTest, SelectWithWhereClause) { Operator got = GetAST(s).value(); ASSERT_THAT(got, VariantWith(Projection{ - std::vector{{"users", "id"}}, + std::vector{Attribute{"users", "id"}}, std::make_shared( Filter{Expression{BinaryExpression{ std::make_shared(Attribute{"users", "age"}), @@ -79,6 +79,35 @@ TEST(ParserTest, DISABLED_SelectWithBooleanExpression) { Operator got = GetAST(s).value(); } +TEST(ParserTest, SelectWithArithmeticalOperations) { + std::stringstream s{"SELECT 1+2-3;"}; + + Operator got = GetAST(s).value(); + + ASSERT_THAT( + got, VariantWith(Projection{{BinaryExpression{ + std::make_shared(BinaryExpression{ + std::make_shared(IntConst{1}), + BinaryOp::kPlus, + std::make_shared(IntConst{2}), + }), + BinaryOp::kMinus, + std::make_shared(IntConst{3}), + }}, + std::make_shared(Table{"_EMPTY_TABLE_"})})); +} + +TEST(ParserTest, GetDotRepresentationOfArithmeticalExpression) { + std::stringstream s{"SELECT 1+2-3+4+5-6;"}; + + Operator op = GetAST(s).value(); + auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_arithmetical.dot"); + + auto got = GetDotRepresentation(op); + + ASSERT_THAT(got, Eq(expected)); +} + TEST(ParserTest, SyntaxError) { std::stringstream s{"xxx;"}; diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 14d4144..f385bce 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -50,9 +50,13 @@ std::any Visitor::visit(antlr4::tree::ParseTree *tree) { } std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ctx) { + // NOTE: column reference has form: database_name.schema_name.table_name.column_name auto table = ctx->colid()->getText(); - auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); - return Attribute{std::move(table), std::move(column)}; + if (ctx->indirection()) { + auto column = ctx->indirection()->indirection_el(0)->attr_name()->getText(); + return Expression{Attribute{std::move(table), std::move(column)}}; + } + return Expression{Attribute{"", std::move(table)}}; } std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { @@ -89,23 +93,28 @@ std::any Visitor::visitSimple_select_intersect(codegen::PostgreSQLParser::Simple return visit(ctx->simple_select_pramary().front()); } -std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext *ctx) { - std::vector targets; - if (!(ctx->target_el().size() == 1 && ctx->target_el(0)->getText() == "*")) { - targets = ctx->target_el() - | std::ranges::views::transform([this](const auto &target_node) { - auto expr_any = visit(target_node); - if (Attribute *attr = std::any_cast(&expr_any)) { - return *attr; - } - // TODO: support arbitrary expressions here - std::unreachable(); - }) - | std::ranges::to(); - } +std::any Visitor::visitTarget_label(codegen::PostgreSQLParser::Target_labelContext *ctx) { + // TODO: AS (rename operation) + return visit(ctx->a_expr()); +} + +std::any Visitor::visitTarget_star(codegen::PostgreSQLParser::Target_starContext* ctx) { + return {}; +} + +// TODO: make this functions return Expression, and make operator that sets child expression +std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext* ctx) { + const auto& targets = ctx->target_el(); + auto target_expressions + = targets | std::views::transform([this](const auto& target) { return visit(target); }) + | std::views::filter( + [](const std::any& expr) { return expr.has_value(); }) + | std::views::transform( + [](std::any expr) { return std::any_cast(expr); }) + | std::ranges::to(); - if (!targets.empty()) { - return Projection{targets, nullptr}; + if (!target_expressions.empty()) { + return Projection{std::move(target_expressions), nullptr}; } return {}; @@ -192,4 +201,312 @@ std::any Visitor::visitSelect_no_parens(codegen::PostgreSQLParser::Select_no_par return select_clause; } +std::any Visitor::visitA_expr_qual(codegen::PostgreSQLParser::A_expr_qualContext *ctx) { + if (ctx->qual_op()) { + throw Error{ErrorType::kQueryNotSupported, "qualified operators are not supported"}; + } + return visit(ctx->a_expr_lessless()); +} + +std::any Visitor::visitA_expr_lessless(codegen::PostgreSQLParser::A_expr_lesslessContext *ctx) { + if (ctx->a_expr_or().size() > 1) { + throw Error{ErrorType::kQueryNotSupported, "<< and >> operators are not supported"}; + } + return visit(ctx->a_expr_or(0)); +} + +std::any Visitor::visitA_expr_or(codegen::PostgreSQLParser::A_expr_orContext *ctx) { + const auto& exprs = ctx->a_expr_and(); + auto result = std::any_cast(visit(exprs.front())); + for (size_t i = 1; i < exprs.size(); i++) { + auto tmp = std::any_cast(visit(exprs[i])); + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kOr, + std::make_shared(std::move(tmp))}; + } + return result; +} + +std::any Visitor::visitA_expr_and(codegen::PostgreSQLParser::A_expr_andContext *ctx) { + const auto& exprs = ctx->a_expr_between(); + auto result = std::any_cast(visit(exprs.front())); + for (size_t i = 1; i < exprs.size(); i++) { + auto tmp = std::any_cast(visit(exprs[i])); + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kAnd, + std::make_shared(std::move(tmp))}; + } + return result; +} + +std::any Visitor::visitA_expr_between(codegen::PostgreSQLParser::A_expr_betweenContext *ctx) { + if (ctx->BETWEEN()) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "BETWEEN clause is not supported"}; + } + return visit(ctx->a_expr_in(0)); +} + +std::any Visitor::visitA_expr_in(codegen::PostgreSQLParser::A_expr_inContext *ctx) { + if (ctx->IN_P()) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "IN clause is not supported"}; + } + return visit(ctx->a_expr_unary_not()); +} + +std::any Visitor::visitA_expr_unary_not(codegen::PostgreSQLParser::A_expr_unary_notContext *ctx) { + auto result = std::any_cast(visit(ctx->a_expr_isnull())); + if (ctx->NOT()) { + result = UnaryExpression{UnaryOp::kNot, std::make_shared(std::move(result))}; + } + return result; +} + +std::any Visitor::visitA_expr_isnull(codegen::PostgreSQLParser::A_expr_isnullContext *ctx) { + auto result = std::any_cast(visit(ctx->a_expr_is_not())); + if (ctx->ISNULL()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + } + if (ctx->NOTNULL()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + result = UnaryExpression{UnaryOp::kNot, std::make_shared(std::move(result))}; + } + return result; +} + +std::any Visitor::visitA_expr_is_not(codegen::PostgreSQLParser::A_expr_is_notContext *ctx) { + auto result = std::any_cast(visit(ctx->a_expr_compare())); + if (!ctx->IS()) { + return result; + } + + if (ctx->NULL_P()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kNull)}; + } else if (ctx->TRUE_P()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kTrue)}; + } else if (ctx->FALSE_P()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kFalse)}; + } else if (ctx->UNKNOWN()) { + result = BinaryExpression{std::make_shared(std::move(result)), BinaryOp::kEq, std::make_shared(Literal::kUnknown)}; + } else if (ctx->DISTINCT()) { + throw Error{ErrorType::kQueryNotSupported, "IS DISTINCT FROM clause is not supported"}; + } else if (ctx->OF()) { + throw Error{ErrorType::kQueryNotSupported, "IS OF (type_list) clause is not supported"}; + } else if (ctx->DOCUMENT_P()) { + throw Error{ErrorType::kQueryNotSupported, "IS DOCUMENT clause is not supported"}; + } else if (ctx->NORMALIZED()) { + throw Error{ErrorType::kQueryNotSupported, "IS NORMALIZED clause is not supported"}; + } + + if (ctx->NOT()) { + result = UnaryExpression{UnaryOp::kNot, std::make_shared(std::move(result))}; + } + return result; +} + +std::any Visitor::visitA_expr_like(codegen::PostgreSQLParser::A_expr_likeContext *ctx) { + const auto& exprs = ctx->a_expr_qual_op(); + if (exprs.size() > 1) { + // NOTE: may want to implement + throw Error{ErrorType::kQueryNotSupported, "LIKE clause is not supported"}; + } + return visit(exprs.front()); +} + +std::any Visitor::visitA_expr_qual_op(codegen::PostgreSQLParser::A_expr_qual_opContext *ctx) { + const auto& exprs = ctx->a_expr_unary_qualop(); + if (exprs.size() > 1) { + // NOTE: may want to implement + throw Error{ErrorType::kQueryNotSupported, "qual_op is not supported"}; + } + return visit(exprs.front()); +} + +std::any Visitor::visitA_expr_unary_qualop(codegen::PostgreSQLParser::A_expr_unary_qualopContext *ctx) { + if (ctx->qual_op()) { + // NOTE: may want to implement + throw Error{ErrorType::kQueryNotSupported, "unary_qual_op is not supported"}; + } + return visit(ctx->a_expr_add()); +} + +std::any Visitor::visitA_expr_add(codegen::PostgreSQLParser::A_expr_addContext *ctx) { + const auto& exprs = ctx->a_expr_mul(); + auto result = std::any_cast(visit(exprs.front())); + auto expr_it = exprs.cbegin()+1; + + const auto& children = ctx->children; + auto op_it = children.cbegin()+1; + + while (expr_it != exprs.cend()) { + auto op = (*op_it)->getText(); + auto rhs = std::any_cast(visit(*expr_it)); + if (op == "+") { + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kPlus, + std::make_shared(std::move(rhs)), + }; + } else if (op == "-") { + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kMinus, + std::make_shared(std::move(rhs)), + }; + } + op_it += 2; + expr_it++; + } + return result; +} + +std::any Visitor::visitA_expr_mul(codegen::PostgreSQLParser::A_expr_mulContext *ctx) { + const auto& exprs = ctx->a_expr_caret(); + auto result = std::any_cast(visit(exprs.front())); + auto expr_it = exprs.cbegin()+1; + + const auto& children = ctx->children; + auto op_it = children.cbegin()+1; + + while (expr_it != exprs.cend()) { + auto op = (*op_it)->getText(); + auto rhs = std::any_cast(visit(*expr_it)); + if (op == "*") { + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kMul, + std::make_shared(std::move(rhs)), + }; + } else if (op == "/") { + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kDiv, + std::make_shared(std::move(rhs)), + }; + } else if (op == "%") { + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kMod, + std::make_shared(std::move(rhs)), + }; + } + op_it += 2; + expr_it++; + } + return result; +} + +std::any Visitor::visitA_expr_caret(codegen::PostgreSQLParser::A_expr_caretContext *ctx) { + auto result = std::any_cast(visit(ctx->a_expr_unary_sign(0))); + if (ctx->CARET()) { + auto rhs = std::any_cast(visit(ctx->a_expr_unary_sign(1))); + result = BinaryExpression{ + std::make_shared(std::move(result)), + BinaryOp::kPow, + std::make_shared(std::move(rhs)), + }; + } + return result; +} + +std::any Visitor::visitA_expr_unary_sign(codegen::PostgreSQLParser::A_expr_unary_signContext *ctx) { + auto result = std::any_cast(visit(ctx->a_expr_at_time_zone())); + if (ctx->MINUS()) { + result = BinaryExpression{ + std::make_shared(IntConst{0}), + BinaryOp::kPow, + std::make_shared(std::move(result)), + }; + } + return result; +} + +std::any Visitor::visitA_expr_at_time_zone(codegen::PostgreSQLParser::A_expr_at_time_zoneContext *ctx) { + if (ctx->AT()) { + throw Error{ErrorType::kQueryNotSupported, "AT TIME ZONE literal is not supported"}; + } + return visit(ctx->a_expr_collate()); +} + +std::any Visitor::visitA_expr_collate(codegen::PostgreSQLParser::A_expr_collateContext *ctx) { + if (ctx->COLLATE()) { + throw Error{ErrorType::kQueryNotSupported, "COLLATE clause is not supported"}; + } + return visit(ctx->a_expr_typecast()); +} + +std::any Visitor::visitA_expr_typecast(codegen::PostgreSQLParser::A_expr_typecastContext *ctx) { + if (!ctx->TYPECAST().empty()) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "TYPECAST clause is not supported"}; + } + return visit(ctx->c_expr()); +} + +std::any Visitor::visitC_expr_exists(codegen::PostgreSQLParser::C_expr_existsContext *ctx) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "EXISTS clause is not supported"}; +} + +std::any Visitor::visitC_expr_expr(codegen::PostgreSQLParser::C_expr_exprContext *ctx) { + if (ctx->columnref()) { + return visit(ctx->columnref()); + } + if (ctx->aexprconst()) { + return visit(ctx->aexprconst()); + } + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "c_expr is not fully supported"}; +} + +std::any Visitor::visitC_expr_case(codegen::PostgreSQLParser::C_expr_caseContext *ctx) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "CASE clause is not supported"}; +} + +std::any Visitor::visitAexprconst(codegen::PostgreSQLParser::AexprconstContext *ctx) { + if (ctx->constinterval()) { + throw Error{ErrorType::kQueryNotSupported, "intervals are not supported"}; + } + if (ctx->sconst()) { + throw Error{ErrorType::kQueryNotSupported, "strings are not supported"}; + } + if (ctx->xconst()) { + throw Error{ErrorType::kQueryNotSupported, "hex literals are not supported"}; + } + if (ctx->bconst()) { + throw Error{ErrorType::kQueryNotSupported, "binary literals are not supported"}; + } + if (ctx->fconst()) { + throw Error{ErrorType::kQueryNotSupported, "float literals are not supported"}; + } + if (ctx->TRUE_P()) { + return Expression{Literal::kTrue}; + } + if (ctx->FALSE_P()) { + return Expression{Literal::kFalse}; + } + if (ctx->NULL_P()) { + return Expression{Literal::kNull}; + } + if (ctx->iconst()) { + return visit(ctx->iconst()); + } + throw Error{ErrorType::kUnknown, "some literal is not supported"}; +} + +std::any Visitor::visitIconst(codegen::PostgreSQLParser::IconstContext *ctx) { + if (!ctx->Integral()) { + throw Error{ErrorType::kQueryNotSupported, "binary, octal and hexadecimal constants are not supported"}; + } + + std::stringstream ss(ctx->Integral()->getText()); + int64_t value; + + if (ss >> value) { + return Expression{IntConst{value}}; + } else { + throw Error{ErrorType::kConversionError, std::format("integer literal \"{}\" cannot be converted to 64-bit signed integer", ctx->Integral()->getText())}; + } +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 7887508..6b28839 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -18,9 +18,44 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitSimple_select_intersect(codegen::PostgreSQLParser::Simple_select_intersectContext *ctx) override; virtual std::any visitSimple_select_pramary(codegen::PostgreSQLParser::Simple_select_pramaryContext *ctx) override; virtual std::any visitTarget_list(codegen::PostgreSQLParser::Target_listContext *ctx) override; + virtual std::any visitTarget_label(codegen::PostgreSQLParser::Target_labelContext *ctx) override; + virtual std::any visitTarget_star(codegen::PostgreSQLParser::Target_starContext *ctx) override; virtual std::any visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) override; virtual std::any visitWhere_clause(codegen::PostgreSQLParser::Where_clauseContext *ctx) override; virtual std::any visitSelect_with_parens(codegen::PostgreSQLParser::Select_with_parensContext *ctx) override; + virtual std::any visitA_expr_qual(codegen::PostgreSQLParser::A_expr_qualContext *ctx) override; + virtual std::any visitA_expr_lessless(codegen::PostgreSQLParser::A_expr_lesslessContext *ctx) override; + virtual std::any visitA_expr_or(codegen::PostgreSQLParser::A_expr_orContext *ctx) override; + virtual std::any visitA_expr_and(codegen::PostgreSQLParser::A_expr_andContext *ctx) override; + virtual std::any visitA_expr_between(codegen::PostgreSQLParser::A_expr_betweenContext *ctx) override; + virtual std::any visitA_expr_in(codegen::PostgreSQLParser::A_expr_inContext *ctx) override; + virtual std::any visitA_expr_unary_not(codegen::PostgreSQLParser::A_expr_unary_notContext *ctx) override; + virtual std::any visitA_expr_isnull( + codegen::PostgreSQLParser::A_expr_isnullContext *ctx) override; + virtual std::any visitA_expr_is_not( + codegen::PostgreSQLParser::A_expr_is_notContext *ctx) override; + virtual std::any visitA_expr_like(codegen::PostgreSQLParser::A_expr_likeContext *ctx) override; + virtual std::any visitA_expr_qual_op( + codegen::PostgreSQLParser::A_expr_qual_opContext *ctx) override; + virtual std::any visitA_expr_unary_qualop( + codegen::PostgreSQLParser::A_expr_unary_qualopContext *ctx) override; + virtual std::any visitA_expr_add(codegen::PostgreSQLParser::A_expr_addContext *ctx) override; + virtual std::any visitA_expr_mul(codegen::PostgreSQLParser::A_expr_mulContext *ctx) override; + virtual std::any visitA_expr_caret( + codegen::PostgreSQLParser::A_expr_caretContext *ctx) override; + virtual std::any visitA_expr_unary_sign( + codegen::PostgreSQLParser::A_expr_unary_signContext *ctx) override; + virtual std::any visitA_expr_at_time_zone( + codegen::PostgreSQLParser::A_expr_at_time_zoneContext *ctx) override; + virtual std::any visitA_expr_collate( + codegen::PostgreSQLParser::A_expr_collateContext *ctx) override; + virtual std::any visitA_expr_typecast( + codegen::PostgreSQLParser::A_expr_typecastContext *ctx) override; + virtual std::any visitC_expr_exists(codegen::PostgreSQLParser::C_expr_existsContext *ctx) override; + virtual std::any visitC_expr_expr(codegen::PostgreSQLParser::C_expr_exprContext *ctx) override; + virtual std::any visitC_expr_case(codegen::PostgreSQLParser::C_expr_caseContext *ctx) override; + virtual std::any visitAexprconst(codegen::PostgreSQLParser::AexprconstContext *ctx) override; + virtual std::any visitIconst(codegen::PostgreSQLParser::IconstContext *ctx) override; private: codegen::PostgreSQLParser* parser_; diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp index 8db6bde..a709ef3 100644 --- a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -3,7 +3,7 @@ namespace stewkk::sql { bool Projection::operator==(const Projection& other) const { - return attributes == other.attributes && *source == *other.source; + return expressions == other.expressions && *source == *other.source; } bool Filter::operator==(const Filter& other) const { @@ -14,10 +14,32 @@ bool BinaryExpression::operator==(const BinaryExpression& other) const { return *lhs == *other.lhs && binop == other.binop && *rhs == *other.rhs; } +bool UnaryExpression::operator==(const UnaryExpression& other) const { + return op == other.op && *child == *other.child; +} + std::string ToString(BinaryOp binop) { switch (binop) { case BinaryOp::kGt: return ">"; + case BinaryOp::kOr: + return "or"; + case BinaryOp::kAnd: + return "and"; + case BinaryOp::kEq: + return "="; + case BinaryOp::kPlus: + return "+"; + case BinaryOp::kMinus: + return "-"; + case BinaryOp::kMul: + return "*"; + case BinaryOp::kDiv: + return "/"; + case BinaryOp::kMod: + return "%"; + case BinaryOp::kPow: + return "^"; } } @@ -25,4 +47,45 @@ std::string ToString(const Attribute& attr) { return std::format("{}.{}", attr.table, attr.name); } +std::string ToString(UnaryOp op) { + switch (op) { + case UnaryOp::kNot: + return "not"; + } +} + +std::string ToString(Literal literal) { + switch (literal) { + case Literal::kNull: + return "NULL"; + case Literal::kTrue: + return "TRUE"; + case Literal::kFalse: + return "FALSE"; + case Literal::kUnknown: + return "UNKNOWN"; + } +} + +std::string ToString(const Expression& expr) { + struct Formatter { + std::string operator()(const BinaryExpression& expr) { + return std::format("{} {} {}", ToString(*expr.lhs), ToString(expr.binop), ToString(*expr.rhs)); + } + std::string operator()(const Attribute& expr) { + return ToString(expr); + } + std::string operator()(const IntConst& expr) { + return std::to_string(expr); + } + std::string operator()(const UnaryExpression& expr) { + return std::format("{} {}", ToString(expr.op), ToString(*expr.child)); + } + std::string operator()(const Literal& expr) { + return ToString(expr); + } + }; + return std::visit(Formatter{}, expr); +} + } // namespace stewkk::sql diff --git a/test/static/parser/expected_arithmetical.dot b/test/static/parser/expected_arithmetical.dot new file mode 100644 index 0000000..e01c1f1 --- /dev/null +++ b/test/static/parser/expected_arithmetical.dot @@ -0,0 +1,3 @@ +digraph G { rankdir=BT; "π 1 + 2 - 3 + 4 + 5 - 6" +"_EMPTY_TABLE_" -> "π 1 + 2 - 3 + 4 + 5 - 6" +"_EMPTY_TABLE_" } From aadaaefc0021bf7b384d626fb9aa391fa9882fb0 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 4 Dec 2025 00:43:21 +0300 Subject: [PATCH 19/43] Add bool expression test --- src/stewkk/sql/logic/parser/parser_test.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index c0e784c..2eefb31 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -73,10 +73,27 @@ TEST(ParserTest, GetDotRepresentation) { ASSERT_THAT(got, Eq(expected)); } -TEST(ParserTest, DISABLED_SelectWithBooleanExpression) { +TEST(ParserTest, SelectWithBooleanExpression) { std::stringstream s{"SELECT TRUE AND NULL OR FALSE AND NOT NULL;"}; Operator got = GetAST(s).value(); + + ASSERT_THAT(got, VariantWith(Projection{ + {BinaryExpression{ + std::make_shared(BinaryExpression{ + std::make_shared(Literal::kTrue), + BinaryOp::kAnd, + std::make_shared(Literal::kNull), + }), + BinaryOp::kOr, + std::make_shared(BinaryExpression{ + std::make_shared(Literal::kFalse), + BinaryOp::kAnd, + std::make_shared(UnaryExpression{ + UnaryOp::kNot, std::make_shared(Literal::kNull)}), + }), + }}, + std::make_shared(Table{"_EMPTY_TABLE_"})})); } TEST(ParserTest, SelectWithArithmeticalOperations) { From 57a8ca715d724d1253c1352d2286aacddc08527e Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 4 Dec 2025 01:21:03 +0300 Subject: [PATCH 20/43] Refactor --- src/stewkk/sql/logic/parser/visitor.cpp | 41 +++++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index f385bce..4ea20ef 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -7,6 +7,29 @@ namespace stewkk::sql { +namespace { + +Operator GetOperatorWithChild(Operator&& op, Operator&& child) { + struct ChildSetter { + Operator operator() (Table&& parent) { + std::unreachable(); + } + Operator operator() (Projection&& parent) { + parent.source = std::make_shared(std::move(child)); + return parent; + } + Operator operator() (Filter&& filter) { + filter.source = std::make_shared(std::move(child)); + return filter; + } + + Operator&& child; + }; + return std::visit(ChildSetter{std::move(child)}, std::move(op)); +} + +} // namespace + std::any Visitor::visitStmt(codegen::PostgreSQLParser::StmtContext *ctx) { if (!ctx->children.empty() && !ctx->selectstmt()) { auto& rule_names = parser_->getRuleNames(); @@ -114,7 +137,7 @@ std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext | std::ranges::to(); if (!target_expressions.empty()) { - return Projection{std::move(target_expressions), nullptr}; + return Operator{Projection{std::move(target_expressions), nullptr}}; } return {}; @@ -129,7 +152,7 @@ std::any Visitor::visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext ->identifier() ->getText(); - return Table{from_ident}; + return Operator{Table{from_ident}}; } std::any Visitor::visitSimple_select_pramary( @@ -151,21 +174,19 @@ std::any Visitor::visitSimple_select_pramary( Operator result = Table{kEmptyTableName}; if (ctx->from_clause()) { - result = std::any_cast
(visit(ctx->from_clause())); + result = std::any_cast(visit(ctx->from_clause())); } if (ctx->where_clause()) { - auto filter_op = std::any_cast(visit(ctx->where_clause())); - filter_op.source = std::make_shared(std::move(result)); - result = std::move(filter_op); + auto filter = std::any_cast(visit(ctx->where_clause())); + result = GetOperatorWithChild(std::move(filter), std::move(result)); } if (ctx->target_list_()) { auto target_list_opt = visit(ctx->target_list_()); if (target_list_opt.has_value()) { - auto projection_op = std::any_cast(target_list_opt); - projection_op.source = std::make_shared(std::move(result)); - result = std::move(projection_op); + auto projection = std::any_cast(target_list_opt); + result = GetOperatorWithChild(std::move(projection), std::move(result)); } } @@ -174,7 +195,7 @@ std::any Visitor::visitSimple_select_pramary( std::any Visitor::visitWhere_clause(codegen::PostgreSQLParser::Where_clauseContext *ctx) { auto where_expr = std::any_cast(visit(ctx->a_expr())); - return Filter{where_expr, nullptr}; + return Operator{Filter{where_expr, nullptr}}; } std::any Visitor::visitSelect_clause(codegen::PostgreSQLParser::Select_clauseContext *ctx) { From f0a259cd60519e4993a70e57341ebc521050809d Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Tue, 9 Dec 2025 17:30:42 +0300 Subject: [PATCH 21/43] Impl cross joins --- .../models/parser/relational_algebra_ast.hpp | 10 +- .../sql/logic/parser/PostgreSQLParser.g4 | 6 +- src/stewkk/sql/logic/parser/parser.cpp | 20 ++- src/stewkk/sql/logic/parser/parser_test.cpp | 12 +- src/stewkk/sql/logic/parser/visitor.cpp | 116 ++++++++++++++++-- src/stewkk/sql/logic/parser/visitor.hpp | 4 + .../models/parser/relational_algebra_ast.cpp | 4 + test/static/parser/expected_join.dot | 5 + 8 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 test/static/parser/expected_join.dot diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index 684cc6a..9a00c67 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -23,8 +23,16 @@ using IntConst = std::int64_t; struct Table; struct Projection; struct Filter; +struct CrossJoin; -using Operator = std::variant; +using Operator = std::variant; + +struct CrossJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + + bool operator==(const CrossJoin& other) const; +}; struct Table { std::string name; diff --git a/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 index cb1b6bc..a3d56ff 100644 --- a/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 +++ b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 @@ -3202,11 +3202,7 @@ table_ref | func_table func_alias_clause? | select_with_parens alias_clause? ) - | OPEN_PAREN table_ref ( - CROSS JOIN table_ref - | NATURAL join_type? JOIN table_ref - | join_type? JOIN table_ref join_qual - )? CLOSE_PAREN alias_clause? + | OPEN_PAREN table_ref CLOSE_PAREN alias_clause? ) ( CROSS JOIN table_ref | NATURAL join_type? JOIN table_ref diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 005c32d..523cdbf 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -75,18 +75,26 @@ std::string GetDotRepresentation(const Operator& op) { auto exprs = op.expressions | std::views::transform([](const Expression& expr) { return ToString(expr); }) | std::views::join_with(',') | std::ranges::to(); - auto node = std::format("\"π {}\"", exprs); + auto node = std::format("π {}", exprs); auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); - return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; + return {node, std::format("\"{}\"\n\"{}\" -> \"{}\"\n{}", node, source_node, node, rest)}; } std::pair operator()(const Filter& op) { - auto node = std::format("\"σ {}\"", GetDotRepresentation(op.expr)); + auto node = std::format("σ {}", GetDotRepresentation(op.expr)); auto [source_node, rest] = std::visit(DotFormatter{}, *op.source); - return {node, std::format("{}\n{} -> {}\n{}", node, source_node, node, rest)}; + return {node, std::format("\"{}\"\n\"{}\" -> \"{}\"\n{}", node, source_node, node, rest)}; } std::pair operator()(const Table& op) { - auto node = std::format("\"{}\"", op.name); - return {node, node}; + auto node = std::format("{}", op.name); + return {node, std::format("\"{}\"", node)}; + } + std::pair operator()(const CrossJoin& op) { + auto [lhs_node, lhs_rest] = std::visit(DotFormatter{}, *op.lhs); + auto [rhs_node, rhs_rest] = std::visit(DotFormatter{}, *op.rhs); + auto node = std::format("crossjoin_{}_{}", lhs_node, rhs_node); + auto rest = std::format("\"{}\"[label=\"×\"]\n\"{}\" -> \"{}\"\n\"{}\" -> \"{}\"\n{}\n{}", node, lhs_node, node, rhs_node, + node, lhs_rest, rhs_rest); + return {node, rest}; } }; auto [_, code] = std::visit(DotFormatter{}, op); diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 2eefb31..5d57736 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -167,10 +167,20 @@ TEST(ParserTest, EmptySelect) { ASSERT_THAT(got, VariantWith
(Table{"_EMPTY_TABLE_"})); } +TEST(ParserTest, SelectWithJoinDot) { + std::stringstream s{"SELECT * FROM users, books;"}; + auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_join.dot"); + Operator op = GetAST(s).value(); + + auto got = GetDotRepresentation(op); + + ASSERT_THAT(got, Eq(expected)); +} + /* ** ORDER BY -** full support of a_expr ** aggregations: SELECT kind, sum(len) AS total FROM films GROUP BY kind; +** JOIN */ } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 4ea20ef..ec16d4d 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -22,6 +22,9 @@ Operator GetOperatorWithChild(Operator&& op, Operator&& child) { filter.source = std::make_shared(std::move(child)); return filter; } + Operator operator() (CrossJoin&& cross_join) { + std::unreachable(); + } Operator&& child; }; @@ -144,15 +147,110 @@ std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext } std::any Visitor::visitFrom_clause(codegen::PostgreSQLParser::From_clauseContext *ctx) { - auto from_ident = ctx->from_list() - ->table_ref()[0] - ->relation_expr() - ->qualified_name() - ->colid() - ->identifier() - ->getText(); - - return Operator{Table{from_ident}}; + return visit(ctx->from_list()); +} + +std::any Visitor::visitFrom_list(codegen::PostgreSQLParser::From_listContext *ctx) { + auto table_ref = ctx->table_ref(); + auto result = std::any_cast(visit(table_ref.front())); + for (auto it = table_ref.begin()+1; it != table_ref.end(); it++) { + auto rhs = std::any_cast(visit(*it)); + result = CrossJoin{ + std::make_shared(std::move(result)), + std::make_shared(std::move(rhs)), + }; + } + return result; +} + +std::any Visitor::visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ctx) { + if (ctx->xmltable()) { + throw Error{ErrorType::kQueryNotSupported, "xmltable is not supported"}; + } + if (ctx->func_table()) { + // NOTE: may want to support + throw Error{ErrorType::kQueryNotSupported, "func_table is not supported"}; + } + if (ctx->LATERAL_P()) { + throw Error{ErrorType::kQueryNotSupported, "LATERAL clause is not supported"}; + } + + auto children = ctx->children; + auto children_it = children.begin(); + + Operator res; + if (ctx->relation_expr()) { + auto table = std::any_cast(visit(ctx->relation_expr())); + children_it++; + res = Table{std::move(table)}; + } + if (ctx->select_with_parens()) { + res = std::any_cast(visit(ctx->select_with_parens())); + children_it++; + children_it++; + } + auto table_refs = ctx->table_ref(); + auto table_ref_it = table_refs.begin(); + if (ctx->OPEN_PAREN()) { + res = std::any_cast(visit(*table_ref_it)); + children_it++; + table_ref_it++; + } + + if (ctx->alias_clause()) { + throw Error{ErrorType::kQueryNotSupported, "alias_clause is not supported"}; + } + if (ctx->tablesample_clause()) { + throw Error{ErrorType::kQueryNotSupported, "tablesample_clause is not supported"}; + } + + for (; children_it != children.end(); children_it++) { + auto text = (*children_it)->getText(); + if (text == "CROSS") { + children_it += 2; + auto rhs = std::any_cast(visit(*children_it)); + res = CrossJoin{ + std::make_shared(std::move(res)), + std::make_shared(std::move(rhs)), + }; + } else if (text == "NATURAL") { + throw Error{ErrorType::kQueryNotSupported, "NATURAL clause is not supported"}; + } else if (text == "JOIN") { + children_it++; + auto rhs = std::any_cast(visit(*children_it)); + children_it++; + // TODO: join_qual + visit(*children_it); + // res = ... + } else { + // TODO: join_type + auto join_type = visit(*children_it); + children_it += 2; + auto rhs = std::any_cast(visit(*children_it)); + children_it++; + // TODO: join_qual + visit(*children_it); + // res = ... + } + } + return res; +} + +std::any Visitor::visitRelation_expr(codegen::PostgreSQLParser::Relation_exprContext *ctx) { + if (ctx->ONLY()) { + throw Error{ErrorType::kQueryNotSupported, "ONLY clause is not supported"}; + } + if (ctx->STAR()) { + throw Error{ErrorType::kQueryNotSupported, "tablename * clause is not supported"}; + } + return visit(ctx->qualified_name()); +} + +std::any Visitor::visitQualified_name(codegen::PostgreSQLParser::Qualified_nameContext *ctx) { + if (ctx->indirection()) { + throw Error{ErrorType::kQueryNotSupported, "bare column names are not supported"}; + } + return ctx->colid()->getText(); } std::any Visitor::visitSimple_select_pramary( diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 6b28839..8c189cb 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -56,6 +56,10 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitC_expr_case(codegen::PostgreSQLParser::C_expr_caseContext *ctx) override; virtual std::any visitAexprconst(codegen::PostgreSQLParser::AexprconstContext *ctx) override; virtual std::any visitIconst(codegen::PostgreSQLParser::IconstContext *ctx) override; + virtual std::any visitFrom_list(codegen::PostgreSQLParser::From_listContext *ctx) override; + virtual std::any visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ctx) override; + virtual std::any visitRelation_expr(codegen::PostgreSQLParser::Relation_exprContext *ctx) override; + virtual std::any visitQualified_name(codegen::PostgreSQLParser::Qualified_nameContext *ctx) override; private: codegen::PostgreSQLParser* parser_; diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp index a709ef3..d069ca8 100644 --- a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -18,6 +18,10 @@ bool UnaryExpression::operator==(const UnaryExpression& other) const { return op == other.op && *child == *other.child; } +bool CrossJoin::operator==(const CrossJoin& other) const { + return *lhs == *other.lhs && *rhs == *other.rhs; +} + std::string ToString(BinaryOp binop) { switch (binop) { case BinaryOp::kGt: diff --git a/test/static/parser/expected_join.dot b/test/static/parser/expected_join.dot new file mode 100644 index 0000000..c3daabb --- /dev/null +++ b/test/static/parser/expected_join.dot @@ -0,0 +1,5 @@ +digraph G { rankdir=BT; "crossjoin_users_books"[label="×"] +"users" -> "crossjoin_users_books" +"books" -> "crossjoin_users_books" +"users" +"books" } From 99c57fda1e0eaccc4c21077c048329fab905fe4c Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Tue, 9 Dec 2025 18:42:41 +0300 Subject: [PATCH 22/43] Impl outer joins --- .../models/parser/relational_algebra_ast.hpp | 39 +++++-- .../sql/logic/parser/PostgreSQLParser.g4 | 3 +- src/stewkk/sql/logic/parser/parser.cpp | 11 ++ src/stewkk/sql/logic/parser/parser_test.cpp | 11 +- src/stewkk/sql/logic/parser/visitor.cpp | 104 +++++++++++++----- src/stewkk/sql/logic/parser/visitor.hpp | 2 + .../models/parser/relational_algebra_ast.cpp | 25 +++++ test/static/parser/expected_outer_join.dot | 5 + 8 files changed, 163 insertions(+), 37 deletions(-) create mode 100644 test/static/parser/expected_outer_join.dot diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index 9a00c67..74a7f53 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -24,15 +24,9 @@ struct Table; struct Projection; struct Filter; struct CrossJoin; +struct Join; -using Operator = std::variant; - -struct CrossJoin { - std::shared_ptr lhs; - std::shared_ptr rhs; - - bool operator==(const CrossJoin& other) const; -}; +using Operator = std::variant; struct Table { std::string name; @@ -65,6 +59,10 @@ struct Projection { enum class BinaryOp { kGt, + kLt, + kLe, + kGe, + kNotEq, kEq, kOr, kAnd, @@ -106,4 +104,29 @@ struct Filter { bool operator==(const Filter& other) const; }; +struct CrossJoin { + std::shared_ptr lhs; + std::shared_ptr rhs; + + bool operator==(const CrossJoin& other) const; +}; + +enum class JoinType { + kInner, + kFull, + kLeft, + kRight, +}; + +std::string ToString(JoinType type); + +struct Join { + JoinType type; + Expression qual; + std::shared_ptr lhs; + std::shared_ptr rhs; + + bool operator==(const Join& other) const; +}; + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 index a3d56ff..a5e7a48 100644 --- a/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 +++ b/src/stewkk/sql/logic/parser/PostgreSQLParser.g4 @@ -3221,7 +3221,8 @@ func_alias_clause ; join_type - : (FULL | LEFT | RIGHT | INNER_P) OUTER_P? + : INNER_P + | (FULL | LEFT | RIGHT) OUTER_P? ; join_qual diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 523cdbf..6165a86 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -96,6 +96,17 @@ std::string GetDotRepresentation(const Operator& op) { node, lhs_rest, rhs_rest); return {node, rest}; } + std::pair operator()(const Join& op) { + auto [lhs_node, lhs_rest] = std::visit(DotFormatter{}, *op.lhs); + auto [rhs_node, rhs_rest] = std::visit(DotFormatter{}, *op.rhs); + auto join_type = ToString(op.type); + auto qual_expr = GetDotRepresentation(op.qual); + auto node = std::format("join_{}_{}", lhs_node, rhs_node); + auto rest = std::format( + "\"{}\"[label=\"{}\\nON {}\"]\n\"{}\" -> \"{}\"\n\"{}\" -> \"{}\"\n{}\n{}", node, + join_type, qual_expr, lhs_node, node, rhs_node, node, lhs_rest, rhs_rest); + return {node, rest}; + } }; auto [_, code] = std::visit(DotFormatter{}, op); return std::format("digraph G {{ rankdir=BT; {} }}\n", code); diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 5d57736..06f66ab 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -177,10 +177,19 @@ TEST(ParserTest, SelectWithJoinDot) { ASSERT_THAT(got, Eq(expected)); } +TEST(ParserTest, SelectWithOuterJoinDot) { + std::stringstream s{"SELECT * FROM users LEFT OUTER JOIN books ON users.book = books.id;"}; + auto expected = ReadFromFile(kProjectDir + "/test/static/parser/expected_outer_join.dot"); + Operator op = GetAST(s).value(); + + auto got = GetDotRepresentation(op); + + ASSERT_THAT(got, Eq(expected)); +} + /* ** ORDER BY ** aggregations: SELECT kind, sum(len) AS total FROM films GROUP BY kind; -** JOIN */ } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index ec16d4d..de48646 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -25,6 +25,9 @@ Operator GetOperatorWithChild(Operator&& op, Operator&& child) { Operator operator() (CrossJoin&& cross_join) { std::unreachable(); } + Operator operator() (Join&& cross_join) { + std::unreachable(); + } Operator&& child; }; @@ -86,18 +89,36 @@ std::any Visitor::visitColumnref(codegen::PostgreSQLParser::ColumnrefContext *ct } std::any Visitor::visitA_expr_compare(codegen::PostgreSQLParser::A_expr_compareContext *ctx) { - auto lhs = ctx->a_expr_like(0); - auto rhs = ctx->a_expr_like(1); - if (!lhs || !rhs) { - return visitChildren(ctx); - } - auto lhs_any = visit(lhs); - if (Attribute* attr = std::any_cast(&lhs_any)) { - return Expression{ - BinaryExpression{std::make_shared(*attr), BinaryOp::kGt, - std::make_shared(IntConst{std::stoi(rhs->getText())})}}; - } - std::unreachable(); + if (ctx->subquery_Op()) { + throw Error{ErrorType::kQueryNotSupported, "subquery_Op is not supported"}; + } + auto exprs = ctx->a_expr_like(); + if (exprs.size() == 1) { + return visit(exprs.front()); + } + auto lhs_expr = std::any_cast(visit(exprs.front())); + auto rhs_expr = std::any_cast(visit(exprs.back())); + BinaryOp op; + if (ctx->LT()) { + op = BinaryOp::kLt; + } else if (ctx->GT()) { + op = BinaryOp::kGt; + } else if (ctx->EQUAL()) { + op = BinaryOp::kEq; + } else if (ctx->LESS_EQUALS()) { + op = BinaryOp::kLe; + } else if (ctx->GREATER_EQUALS()) { + op = BinaryOp::kGe; + } else if (ctx->NOT_EQUALS()) { + op = BinaryOp::kNotEq; + } + return Expression{ + BinaryExpression { + std::make_shared(lhs_expr), + op, + std::make_shared(rhs_expr), + }, + }; } Visitor::Visitor(codegen::PostgreSQLParser* parser) : parser_(parser) {} @@ -207,6 +228,7 @@ std::any Visitor::visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ct for (; children_it != children.end(); children_it++) { auto text = (*children_it)->getText(); if (text == "CROSS") { + // CROSS JOIN table_ref children_it += 2; auto rhs = std::any_cast(visit(*children_it)); res = CrossJoin{ @@ -215,27 +237,55 @@ std::any Visitor::visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ct }; } else if (text == "NATURAL") { throw Error{ErrorType::kQueryNotSupported, "NATURAL clause is not supported"}; - } else if (text == "JOIN") { - children_it++; - auto rhs = std::any_cast(visit(*children_it)); - children_it++; - // TODO: join_qual - visit(*children_it); - // res = ... } else { - // TODO: join_type - auto join_type = visit(*children_it); - children_it += 2; - auto rhs = std::any_cast(visit(*children_it)); - children_it++; - // TODO: join_qual - visit(*children_it); - // res = ... + Operator rhs; + auto join_type = JoinType::kInner; + Expression qual_expression; + if (text == "JOIN") { + // JOIN table_ref join_qual + children_it++; + rhs = std::any_cast(visit(*children_it)); + children_it++; + qual_expression = std::any_cast(visit(*children_it)); + } else { + // join_type JOIN table_ref join_qual + join_type = std::any_cast(visit(*children_it)); + children_it += 2; + rhs = std::any_cast(visit(*children_it)); + children_it++; + qual_expression = std::any_cast(visit(*children_it)); + } + res = Join{ + join_type, + std::move(qual_expression), + std::make_shared(std::move(res)), + std::make_shared(std::move(rhs)), + }; } } return res; } +std::any Visitor::visitJoin_type(codegen::PostgreSQLParser::Join_typeContext *ctx) { + if (ctx->INNER_P()) { + return JoinType::kInner; + } + if (ctx->FULL()) { + return JoinType::kFull; + } + if (ctx->LEFT()) { + return JoinType::kLeft; + } + return JoinType::kRight; +} + +std::any Visitor::visitJoin_qual(codegen::PostgreSQLParser::Join_qualContext *ctx) { + if (ctx->USING()) { + throw Error{ErrorType::kQueryNotSupported, "USING clause is not supported"}; + } + return visit(ctx->a_expr()); +} + std::any Visitor::visitRelation_expr(codegen::PostgreSQLParser::Relation_exprContext *ctx) { if (ctx->ONLY()) { throw Error{ErrorType::kQueryNotSupported, "ONLY clause is not supported"}; diff --git a/src/stewkk/sql/logic/parser/visitor.hpp b/src/stewkk/sql/logic/parser/visitor.hpp index 8c189cb..85b5a9b 100644 --- a/src/stewkk/sql/logic/parser/visitor.hpp +++ b/src/stewkk/sql/logic/parser/visitor.hpp @@ -60,6 +60,8 @@ class Visitor : public codegen::PostgreSQLParserBaseVisitor { virtual std::any visitTable_ref(codegen::PostgreSQLParser::Table_refContext *ctx) override; virtual std::any visitRelation_expr(codegen::PostgreSQLParser::Relation_exprContext *ctx) override; virtual std::any visitQualified_name(codegen::PostgreSQLParser::Qualified_nameContext *ctx) override; + virtual std::any visitJoin_type(codegen::PostgreSQLParser::Join_typeContext *ctx) override; + virtual std::any visitJoin_qual(codegen::PostgreSQLParser::Join_qualContext *ctx) override; private: codegen::PostgreSQLParser* parser_; diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp index d069ca8..fd1e314 100644 --- a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -22,6 +22,10 @@ bool CrossJoin::operator==(const CrossJoin& other) const { return *lhs == *other.lhs && *rhs == *other.rhs; } +bool Join::operator==(const Join& other) const { + return type == other.type && qual == other.qual && *lhs == *other.lhs && *rhs == *other.rhs; +} + std::string ToString(BinaryOp binop) { switch (binop) { case BinaryOp::kGt: @@ -44,6 +48,14 @@ std::string ToString(BinaryOp binop) { return "%"; case BinaryOp::kPow: return "^"; + case BinaryOp::kLt: + return "<"; + case BinaryOp::kLe: + return "<="; + case BinaryOp::kGe: + return ">="; + case BinaryOp::kNotEq: + return "!="; } } @@ -92,4 +104,17 @@ std::string ToString(const Expression& expr) { return std::visit(Formatter{}, expr); } +std::string ToString(JoinType type) { + switch (type) { + case JoinType::kInner: + return "⋈"; + case JoinType::kFull: + return "⟗"; + case JoinType::kLeft: + return "⟕"; + case JoinType::kRight: + return "⟖"; + } +} + } // namespace stewkk::sql diff --git a/test/static/parser/expected_outer_join.dot b/test/static/parser/expected_outer_join.dot new file mode 100644 index 0000000..cd130ae --- /dev/null +++ b/test/static/parser/expected_outer_join.dot @@ -0,0 +1,5 @@ +digraph G { rankdir=BT; "join_users_books"[label="⟕\nON users.book = books.id"] +"users" -> "join_users_books" +"books" -> "join_users_books" +"users" +"books" } From 34953104c6b5d8755cc40757a144cc443297451f Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sun, 14 Dec 2025 18:59:08 +0300 Subject: [PATCH 23/43] Impl simple select execution --- cmake/FetchBoost.cmake | 14 ++++ flake.nix | 3 +- include/stewkk/sql/logic/executor/channel.hpp | 14 ++++ .../stewkk/sql/logic/executor/executor.hpp | 31 +++++++ .../sql/logic/executor/sequential_scan.hpp | 16 ++++ include/stewkk/sql/models/executor/tuple.hpp | 23 ++++++ src/stewkk/sql/CMakeLists.txt | 5 ++ src/stewkk/sql/logic/executor/executor.cpp | 43 ++++++++++ .../sql/logic/executor/executor_test.cpp | 41 ++++++++++ .../sql/logic/executor/sequential_scan.cpp | 80 +++++++++++++++++++ src/stewkk/sql/logic/parser/parser_test.cpp | 5 ++ test/CMakeLists.txt | 6 +- test/static/executor/test_data/users.csv | 3 + 13 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 cmake/FetchBoost.cmake create mode 100644 include/stewkk/sql/logic/executor/channel.hpp create mode 100644 include/stewkk/sql/logic/executor/executor.hpp create mode 100644 include/stewkk/sql/logic/executor/sequential_scan.hpp create mode 100644 include/stewkk/sql/models/executor/tuple.hpp create mode 100644 src/stewkk/sql/logic/executor/executor.cpp create mode 100644 src/stewkk/sql/logic/executor/executor_test.cpp create mode 100644 src/stewkk/sql/logic/executor/sequential_scan.cpp create mode 100644 test/static/executor/test_data/users.csv diff --git a/cmake/FetchBoost.cmake b/cmake/FetchBoost.cmake new file mode 100644 index 0000000..50da835 --- /dev/null +++ b/cmake/FetchBoost.cmake @@ -0,0 +1,14 @@ +include(FetchContent) + +FetchContent_Declare( + Boost + URL https://github.com/boostorg/boost/releases/download/boost-1.90.0/boost-1.90.0-cmake.tar.gz + URL_HASH SHA256=913ca43d49e93d1b158c9862009add1518a4c665e7853b349a6492d158b036d4 + DOWNLOAD_EXTRACT_TIMESTAMP ON + EXCLUDE_FROM_ALL + OVERRIDE_FIND_PACKAGE +) + +set(BOOST_INCLUDE_LIBRARIES asio thread) + +FetchContent_MakeAvailable(Boost) diff --git a/flake.nix b/flake.nix index 6389826..90cbcc5 100644 --- a/flake.nix +++ b/flake.nix @@ -35,7 +35,7 @@ ]; nativeBuildInputs = [ - pkgs.clang-tools_19 + pkgs.clang-tools ]; NIX_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ @@ -67,4 +67,3 @@ }; }); } - diff --git a/include/stewkk/sql/logic/executor/channel.hpp b/include/stewkk/sql/logic/executor/channel.hpp new file mode 100644 index 0000000..ef1625c --- /dev/null +++ b/include/stewkk/sql/logic/executor/channel.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace stewkk::sql { + +using Channel = boost::asio::experimental::concurrent_channel)>; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp new file mode 100644 index 0000000..5c6a236 --- /dev/null +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace stewkk::sql { + +class Executor { +public: + using SequentialScan = std::function>(const std::string& table_name, Channel& chan)>; + Executor(boost::asio::any_io_executor executor, SequentialScan seq_scan); + + boost::asio::awaitable>> Execute(const Operator& op) const; +private: + boost::asio::awaitable Execute(const Operator& op, Channel& chan) const; + +private: + SequentialScan sequential_scan_; + boost::asio::any_io_executor executor_; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/sequential_scan.hpp b/include/stewkk/sql/logic/executor/sequential_scan.hpp new file mode 100644 index 0000000..0054b0b --- /dev/null +++ b/include/stewkk/sql/logic/executor/sequential_scan.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include +#include + +namespace stewkk::sql { + +struct CsvDirSequentialScanner { + std::string dir; + + boost::asio::awaitable> operator()(const std::string& table_name, Channel& chan) const; +}; + +} // namespace stewkk::sql diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp new file mode 100644 index 0000000..d0cc93e --- /dev/null +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace stewkk::sql { + +struct NullValue{ + auto operator<=>(const NullValue& other) const = default; +}; + +struct AttributeValue { + std::string table; + std::string name; + std::variant value; + + auto operator<=>(const AttributeValue& other) const = default; +}; + +using Tuple = std::vector; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index ebfe788..34ba64b 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -1,5 +1,6 @@ include(FetchAntlr4) include(SetupLLVM) +include(FetchBoost) add_definitions(-DANTLR4CPP_STATIC) @@ -17,6 +18,8 @@ add_library(libsql models/parser/relational_algebra_ast.cpp logic/result/error.cpp logic/result/result.cpp + logic/executor/executor.cpp + logic/executor/sequential_scan.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) @@ -41,6 +44,8 @@ llvm_map_components_to_libnames(llvm_libs support core irreader) target_link_libraries(libsql PRIVATE antlr4_static ${llvm_libs} + Boost::asio + Boost::thread ) add_executable(sql diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp new file mode 100644 index 0000000..e4d74dc --- /dev/null +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -0,0 +1,43 @@ +#include + +namespace stewkk::sql { + +Executor::Executor(boost::asio::any_io_executor executor, SequentialScan seq_scan) + : executor_(std::move(executor)), sequential_scan_(std::move(seq_scan)) {} + +boost::asio::awaitable>> Executor::Execute(const Operator& op) const { + Channel chan{executor_, 1}; + boost::asio::co_spawn(executor_, Execute(op, chan), boost::asio::detached); + + auto buf = co_await chan.async_receive(boost::asio::use_awaitable); + + co_return Ok(std::move(buf)); +} + +boost::asio::awaitable Executor::Execute(const Operator& op, Channel& chan) const { + struct ExecuteVisitor{ + boost::asio::awaitable operator()(const Table& table) { + co_await executor.sequential_scan_(table.name, chan); + co_return; + } + boost::asio::awaitable operator()(const Projection& table) { + co_return; + } + boost::asio::awaitable operator()(const Filter& table) { + co_return; + } + boost::asio::awaitable operator()(const Join& table) { + co_return; + } + boost::asio::awaitable operator()(const CrossJoin& table) { + co_return; + } + + Channel& chan; + const Executor& executor; + }; + co_await std::visit(ExecuteVisitor{chan, *this}, op); + co_return; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp new file mode 100644 index 0000000..5d39ad1 --- /dev/null +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include + +#include +#include +#include + +using ::testing::Eq; + +namespace stewkk::sql { + +const static std::string kProjectDir = std::getenv("PWD"); + +TEST(ExecutorTest, SimpleSelect) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable>> { + std::stringstream s{"SELECT * FROM users;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + auto io_executor = co_await boost::asio::this_coro::executor; + Executor executor(io_executor, std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result> got) { + if (p) std::rethrow_exception(p); + + // FIXME: check values + ASSERT_THAT(got.value().size(), Eq(2)); + }); + + ctx.run(); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp new file mode 100644 index 0000000..b7034fc --- /dev/null +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -0,0 +1,80 @@ +#include + +#include +#include + +#include + +namespace stewkk::sql { + +constexpr static std::size_t kBufSize = 10; + +namespace { + +enum class Type { + kInt, +}; + +Type GetTypeFromString(const std::string& s) { + if (s == "int") { + return Type::kInt; + } + std::unreachable(); +} + +AttributeValue BuildAttributeValueFromString(Type type, const std::string& table, const std::string attr_name, const std::string& value) { + switch (type) { + case Type::kInt: + return AttributeValue{table, attr_name, std::stoi(value)}; + default: + std::unreachable(); + } +} + +Tuple ParseTuple(const std::string& line, + const std::vector>& attributes, + const std::string& table_name) { + return line | std::views::split(',') | std::views::enumerate + | std::views::transform([&attributes, &table_name](const auto& attr) { + const auto& [index, value_range] = attr; + auto value = value_range | std::ranges::to(); + const auto& [attr_name, type] = attributes[index]; + return BuildAttributeValueFromString(type, table_name, attr_name, value); + }) + | std::ranges::to(); +} + +} // namespace + +boost::asio::awaitable> CsvDirSequentialScanner::operator()(const std::string& table_name, Channel& chan) const { + auto path = std::format("{}/{}.csv", dir, table_name); + std::ifstream input{std::move(path)}; + std::string line; + std::getline(input, line); + auto attributes + = line | std::views::split(',') | std::views::transform([](const auto& attr) { + auto mid = std::find(attr.begin(), attr.end(), ':'); + return std::make_pair(std::string{attr.begin(), mid}, GetTypeFromString(std::string{mid + 1, attr.end()})); + }) + | std::ranges::to(); + + std::vector buf; + buf.reserve(kBufSize); + while (std::getline(input, line)) { + auto tuple = ParseTuple(line, attributes, table_name); + buf.emplace_back(std::move(tuple)); + + if (buf.size() == buf.capacity()) { + co_await chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); + buf.clear(); + } + } + + if (!buf.empty()) { + co_await chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); + } + + co_return Ok(); +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/parser_test.cpp b/src/stewkk/sql/logic/parser/parser_test.cpp index 06f66ab..bdb12f9 100644 --- a/src/stewkk/sql/logic/parser/parser_test.cpp +++ b/src/stewkk/sql/logic/parser/parser_test.cpp @@ -14,6 +14,8 @@ namespace stewkk::sql { const static std::string kProjectDir = std::getenv("PWD"); +namespace { + std::string ReadFromFile(std::filesystem::path path) { std::ifstream f{path}; std::ostringstream stream; @@ -21,6 +23,9 @@ std::string ReadFromFile(std::filesystem::path path) { return stream.str(); } +} // namespace + + TEST(ParserTest, SelectAllFromSingleTable) { std::stringstream s{"SELECT * FROM users;"}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc0b4e1..b5c41ed 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,7 @@ include(GoogleTest) add_executable(unittests) target_sources(unittests PRIVATE ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/parser/parser_test.cpp + ${PROJECT_SOURCE_DIR}/src/stewkk/sql/logic/executor/executor_test.cpp ) target_compile_features(unittests PRIVATE cxx_std_23) set_target_properties(unittests PROPERTIES @@ -19,5 +20,8 @@ target_include_directories( ) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) -target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static) +target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static + Boost::asio + Boost::thread +) gtest_discover_tests(unittests) diff --git a/test/static/executor/test_data/users.csv b/test/static/executor/test_data/users.csv new file mode 100644 index 0000000..5cadecd --- /dev/null +++ b/test/static/executor/test_data/users.csv @@ -0,0 +1,3 @@ +id:int,age:int +1,33 +2,64 From 5881a63c81c4b671cc7d9ae3c9060b69e6f90436 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Tue, 16 Dec 2025 16:39:23 +0300 Subject: [PATCH 24/43] Impl seq_scan for more than 10 tuples --- src/stewkk/sql/logic/executor/executor.cpp | 8 ++++++-- src/stewkk/sql/logic/executor/executor_test.cpp | 4 ++-- src/stewkk/sql/logic/executor/sequential_scan.cpp | 2 ++ test/static/executor/test_data/users.csv | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index e4d74dc..70be76e 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -9,9 +9,13 @@ boost::asio::awaitable>> Executor::Execute(const Opera Channel chan{executor_, 1}; boost::asio::co_spawn(executor_, Execute(op, chan), boost::asio::detached); - auto buf = co_await chan.async_receive(boost::asio::use_awaitable); + std::vector result; + while (chan.is_open()) { + auto buf = co_await chan.async_receive(boost::asio::use_awaitable); + std::copy(buf.begin(), buf.end(), std::back_inserter(result)); + } - co_return Ok(std::move(buf)); + co_return Ok(std::move(result)); } boost::asio::awaitable Executor::Execute(const Operator& op, Channel& chan) const { diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 5d39ad1..3517292 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -31,8 +31,8 @@ TEST(ExecutorTest, SimpleSelect) { [](std::exception_ptr p, Result> got) { if (p) std::rethrow_exception(p); - // FIXME: check values - ASSERT_THAT(got.value().size(), Eq(2)); + ASSERT_THAT(got.value()[0], Eq(Tuple{{"users", "id", 1}, {"users", "age", 33}})); + ASSERT_THAT(got.value().size(), Eq(17)); }); ctx.run(); diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index b7034fc..faca8d8 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -74,6 +74,8 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()(const std:: co_await chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); } + chan.close(); + co_return Ok(); } diff --git a/test/static/executor/test_data/users.csv b/test/static/executor/test_data/users.csv index 5cadecd..78e94b1 100644 --- a/test/static/executor/test_data/users.csv +++ b/test/static/executor/test_data/users.csv @@ -1,3 +1,18 @@ id:int,age:int 1,33 2,64 +3,64 +4,64 +5,64 +6,64 +7,64 +8,64 +8,64 +10,64 +11,64 +12,64 +13,64 +14,64 +15,64 +16,64 +17,64 From 73b196afa57eff74cfb60bf92a3c3f05b66c12c7 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Tue, 16 Dec 2025 17:32:04 +0300 Subject: [PATCH 25/43] Add parallelizm test --- src/stewkk/sql/logic/executor/executor.cpp | 6 ++++- .../sql/logic/executor/executor_test.cpp | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 70be76e..66bbed2 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -11,7 +11,11 @@ boost::asio::awaitable>> Executor::Execute(const Opera std::vector result; while (chan.is_open()) { - auto buf = co_await chan.async_receive(boost::asio::use_awaitable); + std::vector buf; + try { + buf = co_await chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) {} + std::copy(buf.begin(), buf.end(), std::back_inserter(result)); } diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 3517292..4ab313b 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -38,4 +38,29 @@ TEST(ExecutorTest, SimpleSelect) { ctx.run(); } +TEST(ExecutorTest, SimpleSelectWithParallelism) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable>> { + std::stringstream s{"SELECT * FROM users;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + auto io_executor = co_await boost::asio::this_coro::executor; + Executor executor(io_executor, std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result> got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value()[0], Eq(Tuple{{"users", "id", 1}, {"users", "age", 33}})); + ASSERT_THAT(got.value().size(), Eq(17)); + }); + + pool.join(); +} + } // namespace stewkk::sql From 0b9dba8034fbfcb168a052f50de37e69731ab82f Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Tue, 16 Dec 2025 21:10:18 +0300 Subject: [PATCH 26/43] Impl projection execution --- include/stewkk/sql/logic/executor/channel.hpp | 6 +- .../stewkk/sql/logic/executor/executor.hpp | 17 +- .../sql/logic/executor/sequential_scan.hpp | 4 +- include/stewkk/sql/models/executor/tuple.hpp | 36 +- .../models/parser/relational_algebra_ast.hpp | 1 + src/stewkk/sql/CMakeLists.txt | 1 + src/stewkk/sql/logic/executor/executor.cpp | 340 +++++++++++++++++- .../sql/logic/executor/executor_test.cpp | 55 ++- .../sql/logic/executor/sequential_scan.cpp | 87 +++-- src/stewkk/sql/logic/parser/visitor.cpp | 7 +- src/stewkk/sql/models/executor/tuple.cpp | 11 + .../models/parser/relational_algebra_ast.cpp | 2 + 12 files changed, 477 insertions(+), 90 deletions(-) create mode 100644 src/stewkk/sql/models/executor/tuple.cpp diff --git a/include/stewkk/sql/logic/executor/channel.hpp b/include/stewkk/sql/logic/executor/channel.hpp index ef1625c..293cc6f 100644 --- a/include/stewkk/sql/logic/executor/channel.hpp +++ b/include/stewkk/sql/logic/executor/channel.hpp @@ -1,14 +1,12 @@ #pragma once -#include - -#include #include #include namespace stewkk::sql { -using Channel = boost::asio::experimental::concurrent_channel)>; +using TuplesChannel = boost::asio::experimental::concurrent_channel; +using AttributesInfoChannel = boost::asio::experimental::concurrent_channel; } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 5c6a236..ad84569 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -16,16 +15,20 @@ namespace stewkk::sql { class Executor { public: - using SequentialScan = std::function>(const std::string& table_name, Channel& chan)>; - Executor(boost::asio::any_io_executor executor, SequentialScan seq_scan); + using SequentialScan = std::function>( + const std::string& table_name, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan)>; + explicit Executor(SequentialScan seq_scan); - boost::asio::awaitable>> Execute(const Operator& op) const; + boost::asio::awaitable> Execute(const Operator& op) const; private: - boost::asio::awaitable Execute(const Operator& op, Channel& chan) const; + boost::asio::awaitable Execute(const Operator& op, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const; + boost::asio::awaitable ExecuteProjection(const Projection& proj, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const; private: - SequentialScan sequential_scan_; - boost::asio::any_io_executor executor_; + SequentialScan sequential_scan_; }; } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/sequential_scan.hpp b/include/stewkk/sql/logic/executor/sequential_scan.hpp index 0054b0b..51451e1 100644 --- a/include/stewkk/sql/logic/executor/sequential_scan.hpp +++ b/include/stewkk/sql/logic/executor/sequential_scan.hpp @@ -10,7 +10,9 @@ namespace stewkk::sql { struct CsvDirSequentialScanner { std::string dir; - boost::asio::awaitable> operator()(const std::string& table_name, Channel& chan) const; + boost::asio::awaitable> operator()(const std::string& table_name, + AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const; }; } // namespace stewkk::sql diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp index d0cc93e..947fd03 100644 --- a/include/stewkk/sql/models/executor/tuple.hpp +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -6,18 +6,44 @@ namespace stewkk::sql { -struct NullValue{ +struct NullValue { auto operator<=>(const NullValue& other) const = default; }; -struct AttributeValue { +enum class Type { + kInt, + kBool, +}; + +struct AttributeInfo { std::string table; std::string name; - std::variant value; + Type type; - auto operator<=>(const AttributeValue& other) const = default; + auto operator<=>(const AttributeInfo& other) const = default; }; -using Tuple = std::vector; +enum class Trilean { + kTrue, + kFalse, + kUnknown, +}; + +union NonNullValue { + int64_t int_value; + Trilean trilean_value; +}; + +NonNullValue GetTrileanValue(Trilean v); + +using Value = std::variant; +using Tuple = std::vector; +using Tuples = std::vector; +using AttributesInfo = std::vector; + +struct Relation { + AttributesInfo attributes; + Tuples tuples; +}; } // namespace stewkk::sql diff --git a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp index 74a7f53..0f8ea9d 100644 --- a/include/stewkk/sql/models/parser/relational_algebra_ast.hpp +++ b/include/stewkk/sql/models/parser/relational_algebra_ast.hpp @@ -86,6 +86,7 @@ struct BinaryExpression { enum class UnaryOp { kNot, + kMinus, }; std::string ToString(UnaryOp op); diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 34ba64b..dd07115 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(libsql logic/result/result.cpp logic/executor/executor.cpp logic/executor/sequential_scan.cpp + models/executor/tuple.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 66bbed2..74f47ad 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -1,51 +1,353 @@ #include +#include +#include +#include +#include + +#include +#include + namespace stewkk::sql { -Executor::Executor(boost::asio::any_io_executor executor, SequentialScan seq_scan) - : executor_(std::move(executor)), sequential_scan_(std::move(seq_scan)) {} +namespace { + +Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs) { + struct ExpressionTypeVisitor { + Type operator()(const BinaryExpression& binop) const { + auto lhs_type = std::visit(*this, *binop.lhs); + auto rhs_type = std::visit(*this, *binop.rhs); + if (lhs_type != rhs_type) { + throw std::logic_error{"types mismatch"}; + } + if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kDiv, + BinaryOp::kMod, BinaryOp::kPow}, + binop.binop)) { + if (lhs_type != Type::kInt) { + throw std::logic_error{"types mismatch"}; + } + return Type::kInt; + } + if (lhs_type != Type::kBool) { + throw std::logic_error{"types mismatch"}; + } + return Type::kBool; + } + Type operator()(const UnaryExpression& unop) const { + auto child_type = std::visit(*this, *unop.child); + if (unop.op == UnaryOp::kMinus) { + if (child_type != Type::kInt) { + throw std::logic_error{"types mismatch"}; + } + return Type::kInt; + } + if (child_type != Type::kBool) { + throw std::logic_error{"types mismatch"}; + } + return Type::kBool; + } + Type operator()(const IntConst& iconst) const { + return Type::kInt; + } + Type operator()(const Literal& literal) const { + // NOTE: we are using switch because compiler will remind about adding + // type of new literal here + switch (literal) { + case Literal::kNull: + return Type::kBool; + case Literal::kTrue: + return Type::kBool; + case Literal::kFalse: + return Type::kBool; + case Literal::kUnknown: + return Type::kBool; + } + } + Type operator()(const Attribute& attr) const { + auto it = std::find_if(available_attrs.begin(), available_attrs.end(), + [&attr](const AttributeInfo& attr_info) { + return attr_info.name == attr.name && attr_info.table == attr.table; + }); + if (it != available_attrs.end()) { + return it->type; + } + throw std::logic_error{"no such attribute"}; + } + + const AttributesInfo& available_attrs; + }; + return std::visit(ExpressionTypeVisitor{available_attrs}, expr); +} + +AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const Projection& proj) { + AttributesInfo result_attributes; + result_attributes.reserve(proj.expressions.size()); + for (const auto& target : proj.expressions) { + AttributeInfo projection_result; + projection_result.type = GetExpressionType(target, attrs); + if (const Attribute* attr = std::get_if(&target)) { + projection_result.name = std::move(attr->name); + projection_result.table = std::move(attr->table); + } + result_attributes.push_back(std::move(projection_result)); + } + return result_attributes; +} + +template + requires std::invocable + && std::same_as, int64_t> +Value ApplyBinaryIntegerOperator(Value lhs, Value rhs) { + if (std::get_if(&lhs) || std::get_if(&rhs)) { + return NullValue{}; + } + auto lhs_value = std::get(std::move(lhs)); + auto rhs_value = std::get(std::move(rhs)); + return NonNullValue{Op{}(lhs_value.int_value, rhs_value.int_value)}; +} -boost::asio::awaitable>> Executor::Execute(const Operator& op) const { - Channel chan{executor_, 1}; - boost::asio::co_spawn(executor_, Execute(op, chan), boost::asio::detached); +template + requires std::invocable + && std::same_as, bool> +Value ApplyBinaryBooleanOperator(Value lhs, Value rhs) { + if (std::get_if(&lhs) || std::get_if(&rhs)) { + return GetTrileanValue(Trilean::kUnknown); + } + auto lhs_value = std::get(std::move(lhs)); + auto rhs_value = std::get(std::move(rhs)); + bool lhs_bool; + switch (lhs_value.trilean_value) { + case Trilean::kTrue: + lhs_bool = true; + case Trilean::kFalse: + lhs_bool = false; + case Trilean::kUnknown: + return GetTrileanValue(Trilean::kUnknown); + } + bool rhs_bool; + switch (rhs_value.trilean_value) { + case Trilean::kTrue: + rhs_bool = true; + case Trilean::kFalse: + rhs_bool = false; + case Trilean::kUnknown: + return GetTrileanValue(Trilean::kUnknown); + } + if (Op{}(lhs_bool, rhs_bool)) { + return GetTrileanValue(Trilean::kTrue); + } + return GetTrileanValue(Trilean::kFalse); +} - std::vector result; - while (chan.is_open()) { - std::vector buf; +struct IntPow { + int64_t operator()(int64_t base, int64_t exp) const { + return static_cast(std::pow(base, exp)); + } +}; + +Value ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr) { + struct ExpressionVisitor { + Value operator()(const BinaryExpression& expr) { + auto lhs = std::visit(*this, *expr.lhs); + auto rhs = std::visit(*this, *expr.rhs); + switch (expr.binop) { + case BinaryOp::kGt: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kLt: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kLe: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kGe: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kNotEq: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kEq: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kOr: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kAnd: + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kPlus: + return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kMinus: + return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kMul: + return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kDiv: + return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kMod: + return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + case BinaryOp::kPow: + return ApplyBinaryIntegerOperator(std::move(lhs), std::move(rhs)); + } + } + Value operator()(const UnaryExpression& expr) { + auto child = std::visit(*this, *expr.child); + + switch (expr.op) { + case UnaryOp::kNot: + struct NotVisitor { + Value operator()(const NullValue& n) { + return GetTrileanValue(Trilean::kUnknown); + } + Value operator()(const NonNullValue& val) { + switch (val.trilean_value) { + case Trilean::kTrue: + return GetTrileanValue(Trilean::kFalse); + case Trilean::kFalse: + return GetTrileanValue(Trilean::kTrue); + case Trilean::kUnknown: + return GetTrileanValue(Trilean::kUnknown); + } + } + }; + return std::visit(NotVisitor{}, child); + case UnaryOp::kMinus: + struct MinusVisitor { + Value operator()(const NullValue& n) { + return NullValue{}; + } + Value operator()(const NonNullValue& val) { + return NonNullValue{-val.int_value}; + } + }; + return std::visit(MinusVisitor{}, child); + } + } + Value operator()(const Attribute& expr) { + auto it = std::find_if(source_attrs.begin(), source_attrs.end(), + [&expr](const AttributeInfo& attr_info) { + return attr_info.name == expr.name && attr_info.table == expr.table; + }); + // NOTE: already checked in GetExpressionType + auto index = it - source_attrs.begin(); + return source[index]; + } + Value operator()(const IntConst& expr) { + return NonNullValue{expr}; + } + Value operator()(const Literal& expr) { + switch (expr) { + case Literal::kNull: + return NullValue{}; + case Literal::kTrue: { + return GetTrileanValue(Trilean::kTrue); + } + case Literal::kFalse: { + return GetTrileanValue(Trilean::kFalse); + } + case Literal::kUnknown: { + return GetTrileanValue(Trilean::kUnknown); + } + } + } + + const AttributesInfo& source_attrs; + const Tuple& source; + }; + + return std::visit(ExpressionVisitor{source_attrs, source}, expr); +} + +Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const Projection& proj) { + return proj.expressions | std::views::transform([&](const auto& target) { + return ApplyProjection(source, source_attrs, target); + }) | std::ranges::to(); +} + +} // namespace + +Executor::Executor(SequentialScan seq_scan) + : sequential_scan_(std::move(seq_scan)) {} + +boost::asio::awaitable> Executor::Execute(const Operator& op) const { + auto executor = co_await boost::asio::this_coro::executor; + AttributesInfoChannel attr_chan{executor, 1}; + TuplesChannel tuples_chan{executor, 1}; + boost::asio::co_spawn(executor, Execute(op, attr_chan, tuples_chan), boost::asio::detached); + + auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); + std::clog << "Received attrs in root\n"; + + Tuples result; + for (;;) { + Tuples buf; try { - buf = co_await chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) {} + buf = co_await tuples_chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) { + break; + } + std::clog << std::format("Received {} tuples in root\n", buf.size()); std::copy(buf.begin(), buf.end(), std::back_inserter(result)); } - co_return Ok(std::move(result)); + std::clog << std::format("Total {} tuples in root\n", result.size()); + co_return Ok(Relation{std::move(attrs), std::move(result)}); } -boost::asio::awaitable Executor::Execute(const Operator& op, Channel& chan) const { +boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const { struct ExecuteVisitor{ boost::asio::awaitable operator()(const Table& table) { - co_await executor.sequential_scan_(table.name, chan); + co_await executor.sequential_scan_(table.name, attr_chan, tuples_chan); co_return; } - boost::asio::awaitable operator()(const Projection& table) { + boost::asio::awaitable operator()(const Projection& projection) { + // NOTE: We are using multiset relational algebra projection (i.e. not + // eleminating duplicate tuples) + co_await executor.ExecuteProjection(projection, attr_chan, tuples_chan); co_return; } - boost::asio::awaitable operator()(const Filter& table) { + boost::asio::awaitable operator()(const Filter& filter) { co_return; } - boost::asio::awaitable operator()(const Join& table) { + boost::asio::awaitable operator()(const Join& join) { co_return; } - boost::asio::awaitable operator()(const CrossJoin& table) { + boost::asio::awaitable operator()(const CrossJoin& cross_join) { co_return; } - Channel& chan; + AttributesInfoChannel& attr_chan; + TuplesChannel& tuples_chan; const Executor& executor; }; - co_await std::visit(ExecuteVisitor{chan, *this}, op); + co_await std::visit(ExecuteVisitor{attr_chan, tuples_chan, *this}, op); co_return; } +boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) const { + auto executor = co_await boost::asio::this_coro::executor; + AttributesInfoChannel in_attrs_chan{executor, 1}; + TuplesChannel in_tuples_chan{executor, 1}; + boost::asio::co_spawn(executor, Execute(*proj.source, in_attrs_chan, in_tuples_chan), + boost::asio::detached); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + + auto attrs_after = GetAttributesAfterProjection(attrs, proj); + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, + boost::asio::use_awaitable); + out_attr_chan.close(); + + for (;;) { + Tuples buf; + try { + buf = co_await in_tuples_chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) { + break; + } + // FIXME: add JIT + std::clog << std::format("Received {} tuples in projection\n", buf.size()); + buf = buf | std::views::transform([&](const auto& tuple) { + return ApplyProjection(tuple, attrs, proj); + }) | std::ranges::to(); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } + out_tuples_chan.close(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 4ab313b..fa14d9e 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -17,22 +18,24 @@ TEST(ExecutorTest, SimpleSelect) { boost::asio::io_context ctx; boost::asio::co_spawn( ctx, - []() -> boost::asio::awaitable>> { + []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM users;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - auto io_executor = co_await boost::asio::this_coro::executor; - Executor executor(io_executor, std::move(seq_scan)); + Executor executor(std::move(seq_scan)); auto got = co_await executor.Execute(op); co_return got; }(), - [](std::exception_ptr p, Result> got) { + [](std::exception_ptr p, Result got) { if (p) std::rethrow_exception(p); - ASSERT_THAT(got.value()[0], Eq(Tuple{{"users", "id", 1}, {"users", "age", 33}})); - ASSERT_THAT(got.value().size(), Eq(17)); + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); + ASSERT_THAT(std::get(got.value().tuples[0][1]).int_value, Eq(33)); + ASSERT_THAT(got.value().tuples.size(), Eq(17)); }); ctx.run(); @@ -42,22 +45,50 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, - []() -> boost::asio::awaitable>> { + []() -> boost::asio::awaitable> { std::stringstream s{"SELECT * FROM users;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - auto io_executor = co_await boost::asio::this_coro::executor; - Executor executor(io_executor, std::move(seq_scan)); + Executor executor(std::move(seq_scan)); auto got = co_await executor.Execute(op); co_return got; }(), - [](std::exception_ptr p, Result> got) { + [](std::exception_ptr p, Result got) { if (p) std::rethrow_exception(p); - ASSERT_THAT(got.value()[0], Eq(Tuple{{"users", "id", 1}, {"users", "age", 33}})); - ASSERT_THAT(got.value().size(), Eq(17)); + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); + ASSERT_THAT(std::get(got.value().tuples[0][1]).int_value, Eq(33)); + ASSERT_THAT(got.value().tuples.size(), Eq(17)); + }); + + pool.join(); +} + +TEST(ExecutorTest, Projection) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.id FROM users;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}})); + ASSERT_THAT(std::get(got.value().tuples[1][0]).int_value, Eq(2)); + ASSERT_THAT(got.value().tuples.size(), Eq(17)); }); pool.join(); diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index faca8d8..2e88821 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -2,6 +2,9 @@ #include #include +#include + +#include #include @@ -11,10 +14,6 @@ constexpr static std::size_t kBufSize = 10; namespace { -enum class Type { - kInt, -}; - Type GetTypeFromString(const std::string& s) { if (s == "int") { return Type::kInt; @@ -22,61 +21,77 @@ Type GetTypeFromString(const std::string& s) { std::unreachable(); } -AttributeValue BuildAttributeValueFromString(Type type, const std::string& table, const std::string attr_name, const std::string& value) { +Value BuildValueFromString(Type type, const std::string& table, const std::string attr_name, const std::string& value) { + if (value == "NULL") { + return NullValue{}; + } switch (type) { case Type::kInt: - return AttributeValue{table, attr_name, std::stoi(value)}; + return NonNullValue{std::stoi(value)}; default: std::unreachable(); } } Tuple ParseTuple(const std::string& line, - const std::vector>& attributes, + const AttributesInfo& attributes, const std::string& table_name) { return line | std::views::split(',') | std::views::enumerate | std::views::transform([&attributes, &table_name](const auto& attr) { const auto& [index, value_range] = attr; auto value = value_range | std::ranges::to(); - const auto& [attr_name, type] = attributes[index]; - return BuildAttributeValueFromString(type, table_name, attr_name, value); + const auto& [_, attr_name, type] = attributes[index]; + return BuildValueFromString(type, table_name, attr_name, value); }) | std::ranges::to(); } } // namespace -boost::asio::awaitable> CsvDirSequentialScanner::operator()(const std::string& table_name, Channel& chan) const { - auto path = std::format("{}/{}.csv", dir, table_name); - std::ifstream input{std::move(path)}; - std::string line; - std::getline(input, line); - auto attributes - = line | std::views::split(',') | std::views::transform([](const auto& attr) { - auto mid = std::find(attr.begin(), attr.end(), ':'); - return std::make_pair(std::string{attr.begin(), mid}, GetTypeFromString(std::string{mid + 1, attr.end()})); - }) - | std::ranges::to(); - - std::vector buf; - buf.reserve(kBufSize); - while (std::getline(input, line)) { - auto tuple = ParseTuple(line, attributes, table_name); - buf.emplace_back(std::move(tuple)); - - if (buf.size() == buf.capacity()) { - co_await chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); - buf.clear(); - } +boost::asio::awaitable> CsvDirSequentialScanner::operator()( + const std::string& table_name, AttributesInfoChannel& attrs_chan, + TuplesChannel& tuples_chan) const { + auto path = std::format("{}/{}.csv", dir, table_name); + std::ifstream input{std::move(path)}; + std::string line; + std::getline(input, line); + auto attributes = line | std::views::split(',') | std::views::transform([&table_name](const auto& attr) { + auto mid = std::find(attr.begin(), attr.end(), ':'); + auto attr_name = std::string{attr.begin(), mid}; + auto attr_type = GetTypeFromString(std::string{mid + 1, attr.end()}); + return AttributeInfo{table_name, std::move(attr_name), attr_type}; + }) + | std::ranges::to(); + + co_await attrs_chan.async_send(boost::system::error_code{}, attributes, + boost::asio::use_awaitable); + attrs_chan.close(); + + Tuples buf; + buf.reserve(kBufSize); + while (std::getline(input, line)) { + auto tuple = ParseTuple(line, attributes, table_name); + buf.emplace_back(std::move(tuple)); + std::clog << std::format("buf size is {}\n", buf.size()); + + if (buf.size() == kBufSize) { + std::clog << std::format("Sending {} tuples from table\n", buf.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + buf.clear(); } + } - if (!buf.empty()) { - co_await chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); - } + if (!buf.empty()) { + std::clog << std::format("Sending {} tuples from table\n", buf.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } - chan.close(); + tuples_chan.close(); + std::clog << "Done sending tuples from table\n"; - co_return Ok(); + co_return Ok(); } } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index de48646..6f9aeb3 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -149,7 +149,6 @@ std::any Visitor::visitTarget_star(codegen::PostgreSQLParser::Target_starContext return {}; } -// TODO: make this functions return Expression, and make operator that sets child expression std::any Visitor::visitTarget_list(codegen::PostgreSQLParser::Target_listContext* ctx) { const auto& targets = ctx->target_el(); auto target_expressions @@ -580,11 +579,7 @@ std::any Visitor::visitA_expr_caret(codegen::PostgreSQLParser::A_expr_caretConte std::any Visitor::visitA_expr_unary_sign(codegen::PostgreSQLParser::A_expr_unary_signContext *ctx) { auto result = std::any_cast(visit(ctx->a_expr_at_time_zone())); if (ctx->MINUS()) { - result = BinaryExpression{ - std::make_shared(IntConst{0}), - BinaryOp::kPow, - std::make_shared(std::move(result)), - }; + result = UnaryExpression{UnaryOp::kMinus, std::make_shared(std::move(result))}; } return result; } diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp new file mode 100644 index 0000000..417e756 --- /dev/null +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -0,0 +1,11 @@ +#include + +namespace stewkk::sql { + +NonNullValue GetTrileanValue(Trilean v) { + NonNullValue res; + res.trilean_value = v; + return res; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp index fd1e314..1518f35 100644 --- a/src/stewkk/sql/models/parser/relational_algebra_ast.cpp +++ b/src/stewkk/sql/models/parser/relational_algebra_ast.cpp @@ -67,6 +67,8 @@ std::string ToString(UnaryOp op) { switch (op) { case UnaryOp::kNot: return "not"; + case UnaryOp::kMinus: + return "-"; } } From 61f0cc662562c2b5961e588ecaaba8abc281e62f Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 18 Dec 2025 18:58:32 +0300 Subject: [PATCH 27/43] Impl filter --- .../stewkk/sql/logic/executor/executor.hpp | 5 +- src/stewkk/sql/logic/executor/executor.cpp | 200 +++++++++++++++--- .../sql/logic/executor/executor_test.cpp | 52 +++++ .../sql/logic/executor/sequential_scan.cpp | 1 + test/static/executor/test_data/users.csv | 20 +- 5 files changed, 235 insertions(+), 43 deletions(-) diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index ad84569..ff6f577 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -23,9 +23,10 @@ class Executor { private: boost::asio::awaitable Execute(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const; - boost::asio::awaitable ExecuteProjection(const Projection& proj, - AttributesInfoChannel& attr_chan, + boost::asio::awaitable ExecuteProjection(const Projection& proj, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const; + boost::asio::awaitable ExecuteFilter(const Filter& filter, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const; private: SequentialScan sequential_scan_; diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 74f47ad..5561f78 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -10,6 +10,9 @@ namespace stewkk::sql { +// TODO: deduplicate with seq_scan +constexpr static std::size_t kBufSize = 10; + namespace { Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs) { @@ -28,9 +31,6 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a } return Type::kInt; } - if (lhs_type != Type::kBool) { - throw std::logic_error{"types mismatch"}; - } return Type::kBool; } Type operator()(const UnaryExpression& unop) const { @@ -79,6 +79,53 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a return std::visit(ExpressionTypeVisitor{available_attrs}, expr); } +Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& available_attrs) { + struct ExpressionTypeVisitor { + Type operator()(const BinaryExpression& binop) const { + if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kDiv, + BinaryOp::kMod, BinaryOp::kPow}, + binop.binop)) { + return Type::kInt; + } + return Type::kBool; + } + Type operator()(const UnaryExpression& unop) const { + if (unop.op == UnaryOp::kMinus) { + return Type::kInt; + } + return Type::kBool; + } + Type operator()(const IntConst& iconst) const { + return Type::kInt; + } + Type operator()(const Literal& literal) const { + switch (literal) { + case Literal::kNull: + return Type::kBool; + case Literal::kTrue: + return Type::kBool; + case Literal::kFalse: + return Type::kBool; + case Literal::kUnknown: + return Type::kBool; + } + } + Type operator()(const Attribute& attr) const { + auto it = std::find_if(available_attrs.begin(), available_attrs.end(), + [&attr](const AttributeInfo& attr_info) { + return attr_info.name == attr.name && attr_info.table == attr.table; + }); + if (it != available_attrs.end()) { + return it->type; + } + throw std::logic_error{"no such attribute"}; + } + + const AttributesInfo& available_attrs; + }; + return std::visit(ExpressionTypeVisitor{available_attrs}, expr); +} + AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const Projection& proj) { AttributesInfo result_attributes; result_attributes.reserve(proj.expressions.size()); @@ -106,33 +153,36 @@ Value ApplyBinaryIntegerOperator(Value lhs, Value rhs) { return NonNullValue{Op{}(lhs_value.int_value, rhs_value.int_value)}; } +template + requires std::invocable + && std::same_as, bool> +Value ApplyCompareIntegersOperator(Value lhs, Value rhs) { + if (std::get_if(&lhs) || std::get_if(&rhs)) { + return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; + } + auto lhs_value = std::get(std::move(lhs)).int_value; + auto rhs_value = std::get(std::move(rhs)).int_value; + auto res = Op{}(lhs_value, rhs_value); + if (res) { + return GetTrileanValue(Trilean::kTrue); + } + return GetTrileanValue(Trilean::kFalse); +} + template requires std::invocable && std::same_as, bool> Value ApplyBinaryBooleanOperator(Value lhs, Value rhs) { if (std::get_if(&lhs) || std::get_if(&rhs)) { - return GetTrileanValue(Trilean::kUnknown); + return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; } - auto lhs_value = std::get(std::move(lhs)); - auto rhs_value = std::get(std::move(rhs)); - bool lhs_bool; - switch (lhs_value.trilean_value) { - case Trilean::kTrue: - lhs_bool = true; - case Trilean::kFalse: - lhs_bool = false; - case Trilean::kUnknown: - return GetTrileanValue(Trilean::kUnknown); - } - bool rhs_bool; - switch (rhs_value.trilean_value) { - case Trilean::kTrue: - rhs_bool = true; - case Trilean::kFalse: - rhs_bool = false; - case Trilean::kUnknown: - return GetTrileanValue(Trilean::kUnknown); + auto lhs_value = std::get(std::move(lhs)).trilean_value; + auto rhs_value = std::get(std::move(rhs)).trilean_value; + if (lhs_value == Trilean::kUnknown || rhs_value == Trilean::kUnknown) { + return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; } + auto lhs_bool = lhs_value == Trilean::kTrue; + auto rhs_bool = rhs_value == Trilean::kTrue; if (Op{}(lhs_bool, rhs_bool)) { return GetTrileanValue(Trilean::kTrue); } @@ -145,24 +195,42 @@ struct IntPow { } }; -Value ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr) { +Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr) { struct ExpressionVisitor { Value operator()(const BinaryExpression& expr) { auto lhs = std::visit(*this, *expr.lhs); auto rhs = std::visit(*this, *expr.rhs); switch (expr.binop) { case BinaryOp::kGt: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kLt: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kLe: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kGe: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kNotEq: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kEq: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { + return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + } + return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kOr: return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kAnd: @@ -251,10 +319,22 @@ Value ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, c Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const Projection& proj) { return proj.expressions | std::views::transform([&](const auto& target) { - return ApplyProjection(source, source_attrs, target); + return CalcExpression(source, source_attrs, target); }) | std::ranges::to(); } +bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const Filter& filter) { + struct FilterVisitor { + bool operator()(const NullValue&) { + return false; + } + bool operator()(const NonNullValue& v) { + return v.trilean_value == Trilean::kTrue; + } + }; + return std::visit(FilterVisitor{}, CalcExpression(source, source_attrs, filter.expr)); +} + } // namespace Executor::Executor(SequentialScan seq_scan) @@ -299,6 +379,7 @@ boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInf co_return; } boost::asio::awaitable operator()(const Filter& filter) { + co_await executor.ExecuteFilter(filter, attr_chan, tuples_chan); co_return; } boost::asio::awaitable operator()(const Join& join) { @@ -319,6 +400,7 @@ boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInf boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, AttributesInfoChannel& out_attr_chan, TuplesChannel& out_tuples_chan) const { + std::clog << "Executing projection\n"; auto executor = co_await boost::asio::this_coro::executor; AttributesInfoChannel in_attrs_chan{executor, 1}; TuplesChannel in_tuples_chan{executor, 1}; @@ -350,4 +432,60 @@ boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, out_tuples_chan.close(); } +boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) const { + std::clog << "Executing filter\n"; + auto executor = co_await boost::asio::this_coro::executor; + AttributesInfoChannel in_attrs_chan{executor, 1}; + TuplesChannel in_tuples_chan{executor, 1}; + boost::asio::co_spawn(executor, Execute(*filter.source, in_attrs_chan, in_tuples_chan), + boost::asio::detached); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + std::clog << "Filter received attrs\n"; + + if (GetExpressionType(filter.expr, attrs) != Type::kBool) { + throw std::logic_error{"filter expr should return bool"}; + } + + std::clog << "Filter sending attrs\n"; + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + out_attr_chan.close(); + std::clog << "Filter sent attrs\n"; + + Tuples output_buf; + output_buf.reserve(kBufSize); + for (;;) { + Tuples input_buf; + try { + std::clog << "Filter waiting on tuples_chan\n"; + input_buf = co_await in_tuples_chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) { + break; + } + // FIXME: add JIT + std::clog << std::format("Received {} tuples in filter\n", input_buf.size()); + auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { + return ApplyFilter(tuple, attrs, filter); + }) | std::views::as_rvalue; + for (auto&& tuple : filtered_view) { + output_buf.push_back(std::move(tuple)); + if (output_buf.size() == kBufSize) { + std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + output_buf.clear(); + } + } + } + std::cout << std::format("{} tuples left in output_buf\n", output_buf.size()); + if (!output_buf.empty()) { + std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + } + out_tuples_chan.close(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index fa14d9e..8d6f807 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -94,4 +94,56 @@ TEST(ExecutorTest, Projection) { pool.join(); } +TEST(ExecutorTest, Filter) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.id FROM users WHERE users.age < 10;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}})); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(5)); + ASSERT_THAT(got.value().tuples.size(), Eq(2)); + }); + + pool.join(); +} + +TEST(ExecutorTest, FilterMany) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT users.id FROM users WHERE users.age > 10;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, + Eq(AttributesInfo{{"users", "id", Type::kInt}})); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); + ASSERT_THAT(got.value().tuples.size(), Eq(15)); + }); + + pool.join(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index 2e88821..21a421c 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -51,6 +51,7 @@ Tuple ParseTuple(const std::string& line, boost::asio::awaitable> CsvDirSequentialScanner::operator()( const std::string& table_name, AttributesInfoChannel& attrs_chan, TuplesChannel& tuples_chan) const { + std::clog << "Executing sequential scan\n"; auto path = std::format("{}/{}.csv", dir, table_name); std::ifstream input{std::move(path)}; std::string line; diff --git a/test/static/executor/test_data/users.csv b/test/static/executor/test_data/users.csv index 78e94b1..3820d04 100644 --- a/test/static/executor/test_data/users.csv +++ b/test/static/executor/test_data/users.csv @@ -1,18 +1,18 @@ id:int,age:int 1,33 2,64 -3,64 -4,64 -5,64 -6,64 -7,64 +3,123 +4,11 +5,1 +6,5 +7,88 8,64 8,64 -10,64 +10,22 11,64 12,64 -13,64 +13,33 14,64 -15,64 -16,64 -17,64 +15,443 +16,21 +17,18 From 1ea65e64a9e392d048b5898eef9d1c9a8ca86694 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Thu, 18 Dec 2025 23:58:39 +0300 Subject: [PATCH 28/43] Impl cross join --- cmake/FetchBoost.cmake | 2 +- .../stewkk/sql/logic/executor/buffer_size.hpp | 10 +++ .../stewkk/sql/logic/executor/executor.hpp | 3 + .../sql/logic/executor/materialization.hpp | 35 ++++++++ src/stewkk/sql/CMakeLists.txt | 2 + src/stewkk/sql/logic/executor/executor.cpp | 86 ++++++++++++++++++- .../sql/logic/executor/executor_test.cpp | 31 +++++++ .../sql/logic/executor/materialization.cpp | 52 +++++++++++ .../sql/logic/executor/sequential_scan.cpp | 3 +- test/CMakeLists.txt | 1 + test/static/executor/test_data/books.csv | 4 + 11 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 include/stewkk/sql/logic/executor/buffer_size.hpp create mode 100644 include/stewkk/sql/logic/executor/materialization.hpp create mode 100644 src/stewkk/sql/logic/executor/materialization.cpp create mode 100644 test/static/executor/test_data/books.csv diff --git a/cmake/FetchBoost.cmake b/cmake/FetchBoost.cmake index 50da835..359c609 100644 --- a/cmake/FetchBoost.cmake +++ b/cmake/FetchBoost.cmake @@ -9,6 +9,6 @@ FetchContent_Declare( OVERRIDE_FIND_PACKAGE ) -set(BOOST_INCLUDE_LIBRARIES asio thread) +set(BOOST_INCLUDE_LIBRARIES asio thread filesystem) FetchContent_MakeAvailable(Boost) diff --git a/include/stewkk/sql/logic/executor/buffer_size.hpp b/include/stewkk/sql/logic/executor/buffer_size.hpp new file mode 100644 index 0000000..438228b --- /dev/null +++ b/include/stewkk/sql/logic/executor/buffer_size.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace stewkk::sql { + +constexpr static std::size_t kBufSize = 10; + +} // stewkk::sql + diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index ff6f577..9ab9550 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -27,6 +27,9 @@ class Executor { TuplesChannel& tuples_chan) const; boost::asio::awaitable ExecuteFilter(const Filter& filter, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const; + boost::asio::awaitable ExecuteCrossJoin(const CrossJoin& cross_join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const; private: SequentialScan sequential_scan_; diff --git a/include/stewkk/sql/logic/executor/materialization.hpp b/include/stewkk/sql/logic/executor/materialization.hpp new file mode 100644 index 0000000..a283a69 --- /dev/null +++ b/include/stewkk/sql/logic/executor/materialization.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +#include + +namespace stewkk::sql { + +namespace fs = boost::filesystem; + +class DiskFileReader { + public: + DiskFileReader(fs::path path, std::size_t tuple_size); + ~DiskFileReader(); + Tuples Read(); + private: + fs::path path_; + std::ifstream f_; + std::size_t tuple_size_; +}; + +class DiskFileWriter { + public: + DiskFileWriter(); + void Write(const Tuples& tuples); + DiskFileReader GetDiskFileReader() &&; + private: + fs::path path_; + std::ofstream f_; + std::size_t tuple_size_; +}; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index dd07115..8ebe9a3 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(libsql logic/executor/executor.cpp logic/executor/sequential_scan.cpp models/executor/tuple.cpp + logic/executor/materialization.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) @@ -47,6 +48,7 @@ target_link_libraries(libsql PRIVATE ${llvm_libs} Boost::asio Boost::thread + Boost::filesystem ) add_executable(sql diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 5561f78..6fe4641 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -8,10 +8,10 @@ #include #include -namespace stewkk::sql { +#include +#include -// TODO: deduplicate with seq_scan -constexpr static std::size_t kBufSize = 10; +namespace stewkk::sql { namespace { @@ -359,7 +359,7 @@ boost::asio::awaitable> Executor::Execute(const Operator& op) c } std::clog << std::format("Received {} tuples in root\n", buf.size()); - std::copy(buf.begin(), buf.end(), std::back_inserter(result)); + std::move(buf.begin(), buf.end(), std::back_inserter(result)); } std::clog << std::format("Total {} tuples in root\n", result.size()); @@ -386,6 +386,7 @@ boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInf co_return; } boost::asio::awaitable operator()(const CrossJoin& cross_join) { + co_await executor.ExecuteCrossJoin(cross_join, attr_chan, tuples_chan); co_return; } @@ -488,4 +489,81 @@ boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, out_tuples_chan.close(); } +boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const { + std::clog << "Executing cross join\n"; + auto executor = co_await boost::asio::this_coro::executor; + AttributesInfoChannel lhs_attrs_chan{executor, 1}; + TuplesChannel lhs_tuples_chan{executor, 1}; + boost::asio::co_spawn(executor, Execute(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan), + boost::asio::detached); + AttributesInfoChannel rhs_attrs_chan{executor, 1}; + TuplesChannel rhs_tuples_chan{executor, 1}; + boost::asio::co_spawn(executor, Execute(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan), + boost::asio::detached); + + auto attrs = co_await lhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto rhs_attrs = co_await rhs_attrs_chan.async_receive(boost::asio::use_awaitable); + std::ranges::copy(std::move(rhs_attrs), std::back_inserter(attrs)); + std::clog << "Cross join received attrs\n"; + + std::clog << "Cross join sending attrs\n"; + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + std::clog << "Cross join sent attrs\n"; + + // FIXME: optimization: check how cross_joins currently generate tree. If + // branching is happening on lhs, than rhs should be materialized instead of + // lhs. + + // step 1: materialize lhs + DiskFileWriter writer; + for (;;) { + Tuples buf; + try { + buf = co_await lhs_tuples_chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) { + break; + } + std::clog << std::format("Received {} tuples in cross join as lhs\n", buf.size()); + writer.Write(buf); + } + + // step 2: receive chunks from rhs and join them with lhs + auto reader = std::move(writer).GetDiskFileReader(); + for (;;) { + Tuples buf_rhs; + try { + buf_rhs = co_await rhs_tuples_chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) { + break; + } + std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_res; + buf_res.reserve(kBufSize); + // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) + for (const auto& tuple_rhs : buf_rhs) { + Tuple joined_tuple; + joined_tuple.reserve(tuple_lhs.size()+tuple_rhs.size()); + std::ranges::copy(tuple_lhs, std::back_inserter(joined_tuple)); + std::ranges::copy(tuple_rhs, std::back_inserter(joined_tuple)); + buf_res.push_back(std::move(joined_tuple)); + } + std::clog << std::format("Sending {} tuples from cross join\n", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + tuples_chan.close(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 8d6f807..e040a86 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -146,4 +146,35 @@ TEST(ExecutorTest, FilterMany) { pool.join(); } +TEST(ExecutorTest, CrossJoin) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT * FROM users, books WHERE users.age < 10;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{ + {"users", "id", Type::kInt}, + {"users", "age", Type::kInt}, + {"books", "id", Type::kInt}, + {"books", "price", Type::kInt}, + })); + ASSERT_THAT(std::get(got.value().tuples[0][3]).int_value, Eq(55)); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(5)); + ASSERT_THAT(got.value().tuples.size(), Eq(6)); + }); + + pool.join(); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/materialization.cpp b/src/stewkk/sql/logic/executor/materialization.cpp new file mode 100644 index 0000000..3dcbfe0 --- /dev/null +++ b/src/stewkk/sql/logic/executor/materialization.cpp @@ -0,0 +1,52 @@ +#include + +#include + +// TODO: implement async file io with asio (and benchmark it) + +namespace stewkk::sql { + +DiskFileWriter::DiskFileWriter() + : path_(fs::temp_directory_path() / fs::unique_path("%%%%.tmp")), f_(path_, std::ios::binary) {} + +void DiskFileWriter::Write(const Tuples& tuples) { + tuple_size_ = tuples.front().size(); + for (const auto& tuple : tuples) { + std::size_t tuple_size = tuple.size(); + // NOTE: better to store tuples in continious buffer and than flush to + // disk + f_.write(reinterpret_cast(tuple.data()), + tuple.size() * sizeof(Tuple::value_type)); + } +} + +DiskFileReader DiskFileWriter::GetDiskFileReader() && { + f_.flush(); + f_.close(); + return DiskFileReader(std::move(path_), tuple_size_); +} + +DiskFileReader::DiskFileReader(fs::path path, std::size_t tuple_size) + : path_(std::move(path)), f_(path_, std::ios::binary), tuple_size_(tuple_size) {} + +DiskFileReader::~DiskFileReader() { + f_.close(); + fs::remove(path_); +} + +Tuples DiskFileReader::Read() { + Tuples buf; + buf.reserve(kBufSize); + while (buf.size() < kBufSize) { + Tuple tuple; + tuple.resize(tuple_size_); + f_.read(reinterpret_cast(tuple.data()), tuple_size_*sizeof(Tuple::value_type)); + if (f_.gcount() == 0) { + break; + } + buf.push_back(std::move(tuple)); + } + return buf; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index 21a421c..40f3955 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -7,11 +7,10 @@ #include #include +#include namespace stewkk::sql { -constexpr static std::size_t kBufSize = 10; - namespace { Type GetTypeFromString(const std::string& s) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b5c41ed..e7aea43 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,5 +23,6 @@ target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static Boost::asio Boost::thread + Boost::filesystem ) gtest_discover_tests(unittests) diff --git a/test/static/executor/test_data/books.csv b/test/static/executor/test_data/books.csv new file mode 100644 index 0000000..244d833 --- /dev/null +++ b/test/static/executor/test_data/books.csv @@ -0,0 +1,4 @@ +id:int,price:int +1,55 +2,66 +3,77 From 37fc077fa2f4758ded1f0411a59300d9b5435916 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Fri, 19 Dec 2025 21:42:25 +0300 Subject: [PATCH 29/43] Impl inner join --- .../stewkk/sql/logic/executor/executor.hpp | 3 + .../sql/logic/executor/materialization.hpp | 2 + src/stewkk/sql/logic/executor/executor.cpp | 223 ++++++++++++------ .../sql/logic/executor/executor_test.cpp | 30 +++ .../static/executor/test_data/departments.csv | 6 + test/static/executor/test_data/employees.csv | 12 + 6 files changed, 209 insertions(+), 67 deletions(-) create mode 100644 test/static/executor/test_data/departments.csv create mode 100644 test/static/executor/test_data/employees.csv diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 9ab9550..e5a7ad9 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -30,6 +30,9 @@ class Executor { boost::asio::awaitable ExecuteCrossJoin(const CrossJoin& cross_join, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const; + boost::asio::awaitable ExecuteJoin(const Join& join, AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const; + boost::asio::awaitable SpawnExecutor(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuple_chan) const; private: SequentialScan sequential_scan_; diff --git a/include/stewkk/sql/logic/executor/materialization.hpp b/include/stewkk/sql/logic/executor/materialization.hpp index a283a69..e8a3387 100644 --- a/include/stewkk/sql/logic/executor/materialization.hpp +++ b/include/stewkk/sql/logic/executor/materialization.hpp @@ -14,6 +14,8 @@ class DiskFileReader { public: DiskFileReader(fs::path path, std::size_t tuple_size); ~DiskFileReader(); + DiskFileReader(DiskFileReader&& other) = default; + DiskFileReader& operator=(DiskFileReader&& other) = default; Tuples Read(); private: fs::path path_; diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 6fe4641..0ec4ee6 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -335,30 +335,66 @@ bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const return std::visit(FilterVisitor{}, CalcExpression(source, source_attrs, filter.expr)); } +boost::asio::awaitable> GetChannels() { + auto executor = co_await boost::asio::this_coro::executor; + co_return std::make_pair(AttributesInfoChannel{executor, 1}, TuplesChannel{executor, 1}); +} + +boost::asio::awaitable ReceiveTuples(TuplesChannel& chan) { + Tuples buf; + try { + buf = co_await chan.async_receive(boost::asio::use_awaitable); + } catch (const boost::system::system_error& ex) {} + co_return buf; +} + +boost::asio::awaitable ConcatAttrs(AttributesInfoChannel& lhs_attrs_chan, AttributesInfoChannel& rhs_attrs_chan) { + auto attrs = co_await lhs_attrs_chan.async_receive(boost::asio::use_awaitable); + auto rhs_attrs = co_await rhs_attrs_chan.async_receive(boost::asio::use_awaitable); + std::ranges::copy(std::move(rhs_attrs), std::back_inserter(attrs)); + co_return attrs; +} + +boost::asio::awaitable MaterializeChannel(TuplesChannel& tuples_chan) { + DiskFileWriter writer; + for (;;) { + auto buf = co_await ReceiveTuples(tuples_chan); + if (buf.empty()) { + break; + } + writer.Write(buf); + } + + co_return std::move(writer).GetDiskFileReader(); +} + +Tuple ConcatTuples(const Tuple& lhs, const Tuple& rhs) { + Tuple joined_tuple; + joined_tuple.reserve(lhs.size() + rhs.size()); + std::ranges::copy(lhs, std::back_inserter(joined_tuple)); + std::ranges::copy(rhs, std::back_inserter(joined_tuple)); + return joined_tuple; +} + } // namespace Executor::Executor(SequentialScan seq_scan) : sequential_scan_(std::move(seq_scan)) {} boost::asio::awaitable> Executor::Execute(const Operator& op) const { - auto executor = co_await boost::asio::this_coro::executor; - AttributesInfoChannel attr_chan{executor, 1}; - TuplesChannel tuples_chan{executor, 1}; - boost::asio::co_spawn(executor, Execute(op, attr_chan, tuples_chan), boost::asio::detached); + auto [attr_chan, tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(op, attr_chan, tuples_chan); auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); std::clog << "Received attrs in root\n"; Tuples result; for (;;) { - Tuples buf; - try { - buf = co_await tuples_chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) { + auto buf = co_await ReceiveTuples(tuples_chan); + if (buf.empty()) { break; } std::clog << std::format("Received {} tuples in root\n", buf.size()); - std::move(buf.begin(), buf.end(), std::back_inserter(result)); } @@ -383,6 +419,7 @@ boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInf co_return; } boost::asio::awaitable operator()(const Join& join) { + co_await executor.ExecuteJoin(join, attr_chan, tuples_chan); co_return; } boost::asio::awaitable operator()(const CrossJoin& cross_join) { @@ -402,24 +439,18 @@ boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, AttributesInfoChannel& out_attr_chan, TuplesChannel& out_tuples_chan) const { std::clog << "Executing projection\n"; - auto executor = co_await boost::asio::this_coro::executor; - AttributesInfoChannel in_attrs_chan{executor, 1}; - TuplesChannel in_tuples_chan{executor, 1}; - boost::asio::co_spawn(executor, Execute(*proj.source, in_attrs_chan, in_tuples_chan), - boost::asio::detached); + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*proj.source, in_attrs_chan, in_tuples_chan); auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); - auto attrs_after = GetAttributesAfterProjection(attrs, proj); co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, boost::asio::use_awaitable); out_attr_chan.close(); for (;;) { - Tuples buf; - try { - buf = co_await in_tuples_chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) { break; } // FIXME: add JIT @@ -437,11 +468,8 @@ boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, AttributesInfoChannel& out_attr_chan, TuplesChannel& out_tuples_chan) const { std::clog << "Executing filter\n"; - auto executor = co_await boost::asio::this_coro::executor; - AttributesInfoChannel in_attrs_chan{executor, 1}; - TuplesChannel in_tuples_chan{executor, 1}; - boost::asio::co_spawn(executor, Execute(*filter.source, in_attrs_chan, in_tuples_chan), - boost::asio::detached); + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*filter.source, in_attrs_chan, in_tuples_chan); auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); std::clog << "Filter received attrs\n"; @@ -458,11 +486,8 @@ boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, Tuples output_buf; output_buf.reserve(kBufSize); for (;;) { - Tuples input_buf; - try { - std::clog << "Filter waiting on tuples_chan\n"; - input_buf = co_await in_tuples_chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) { + auto input_buf = co_await ReceiveTuples(in_tuples_chan); + if (input_buf.empty()) { break; } // FIXME: add JIT @@ -493,19 +518,12 @@ boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_j AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const { std::clog << "Executing cross join\n"; - auto executor = co_await boost::asio::this_coro::executor; - AttributesInfoChannel lhs_attrs_chan{executor, 1}; - TuplesChannel lhs_tuples_chan{executor, 1}; - boost::asio::co_spawn(executor, Execute(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan), - boost::asio::detached); - AttributesInfoChannel rhs_attrs_chan{executor, 1}; - TuplesChannel rhs_tuples_chan{executor, 1}; - boost::asio::co_spawn(executor, Execute(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan), - boost::asio::detached); + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan); - auto attrs = co_await lhs_attrs_chan.async_receive(boost::asio::use_awaitable); - auto rhs_attrs = co_await rhs_attrs_chan.async_receive(boost::asio::use_awaitable); - std::ranges::copy(std::move(rhs_attrs), std::back_inserter(attrs)); + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); std::clog << "Cross join received attrs\n"; std::clog << "Cross join sending attrs\n"; @@ -513,51 +531,115 @@ boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_j attr_chan.close(); std::clog << "Cross join sent attrs\n"; - // FIXME: optimization: check how cross_joins currently generate tree. If - // branching is happening on lhs, than rhs should be materialized instead of - // lhs. + auto reader = co_await MaterializeChannel(lhs_tuples_chan); + std::clog << std::format("Materialized tuples in cross join\n"); - // step 1: materialize lhs - DiskFileWriter writer; for (;;) { - Tuples buf; - try { - buf = co_await lhs_tuples_chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { break; } - std::clog << std::format("Received {} tuples in cross join as lhs\n", buf.size()); - writer.Write(buf); + std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); + + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_joined; + buf_joined.reserve(kBufSize); + // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) + for (const auto& tuple_rhs : buf_rhs) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + buf_joined.push_back(std::move(joined_tuple)); + } + std::clog << std::format("Sending {} tuples from cross join\n", buf_joined.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), + boost::asio::use_awaitable); + } + } + } + tuples_chan.close(); +} + +boost::asio::awaitable Executor::ExecuteJoin(const Join& join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) const { + std::clog << "Executing join\n"; + if (join.type == JoinType::kFull) { + throw std::logic_error{"Full joins are not supported by executor"}; + } + if (join.type == JoinType::kLeft) { + std::swap(*join.lhs, *join.rhs); } + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); + std::clog << "Join received attrs\n"; + + std::clog << "Join sending attrs\n"; + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + std::clog << "Join sent attrs\n"; - // step 2: receive chunks from rhs and join them with lhs - auto reader = std::move(writer).GetDiskFileReader(); + auto reader = co_await MaterializeChannel(lhs_tuples_chan); for (;;) { - Tuples buf_rhs; - try { - buf_rhs = co_await rhs_tuples_chan.async_receive(boost::asio::use_awaitable); - } catch (const boost::system::system_error& ex) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { break; } - std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); + std::clog << std::format("Received {} tuples in join as rhs\n", buf_rhs.size()); + + std::vector used(buf_rhs.size(), false); for (;;) { auto buf_lhs = reader.Read(); if (buf_lhs.empty()) { break; } std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); + for (const auto& tuple_lhs : buf_lhs) { Tuples buf_res; buf_res.reserve(kBufSize); - // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) - for (const auto& tuple_rhs : buf_rhs) { - Tuple joined_tuple; - joined_tuple.reserve(tuple_lhs.size()+tuple_rhs.size()); - std::ranges::copy(tuple_lhs, std::back_inserter(joined_tuple)); - std::ranges::copy(tuple_rhs, std::back_inserter(joined_tuple)); - buf_res.push_back(std::move(joined_tuple)); + for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + auto qual_expr_res = CalcExpression(joined_tuple, attrs, join.qual); + if (std::get(std::move(qual_expr_res)).trilean_value == Trilean::kTrue) { + buf_res.push_back(std::move(joined_tuple)); + used[rhs_index] = true; + } } - std::clog << std::format("Sending {} tuples from cross join\n", buf_res.size()); + if (!buf_res.empty()) { + std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + + if (join.type == JoinType::kRight || join.type == JoinType::kLeft) { + Tuples buf_res; + buf_res.reserve(kBufSize); + for (auto [rhs_index, is_used] : used | std::views::enumerate) { + if (is_used) { + continue; + } + + auto rhs_tuple = std::move(buf_rhs[rhs_index]); + + auto lhs_size = attrs.size() - rhs_tuple.size(); + Tuple lhs_tuple(lhs_size, NullValue{}); + + auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); + buf_res.push_back(std::move(joined_tuple)); + } + if (!buf_res.empty()) { + std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), boost::asio::use_awaitable); } @@ -566,4 +648,11 @@ boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_j tuples_chan.close(); } +boost::asio::awaitable Executor::SpawnExecutor(const Operator& op, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuple_chan) const { + auto executor = co_await boost::asio::this_coro::executor; + boost::asio::co_spawn(executor, Execute(op, attr_chan, tuple_chan), boost::asio::detached); +} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index e040a86..d6b3dd6 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -177,4 +177,34 @@ TEST(ExecutorTest, CrossJoin) { pool.join(); } +TEST(ExecutorTest, InnerJoin) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT * FROM employees JOIN departments ON employees.department_id = departments.id;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{ + {"employees", "id", Type::kInt}, + {"employees", "department_id", Type::kInt}, + {"departments", "id", Type::kInt}, + })); + ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); + ASSERT_THAT(std::get(got.value().tuples[0][2]).int_value, Eq(3)); + ASSERT_THAT(got.value().tuples.size(), Eq(3)); + }); + + pool.join(); +} + } // namespace stewkk::sql diff --git a/test/static/executor/test_data/departments.csv b/test/static/executor/test_data/departments.csv new file mode 100644 index 0000000..49afd56 --- /dev/null +++ b/test/static/executor/test_data/departments.csv @@ -0,0 +1,6 @@ +id:int +1 +2 +3 +4 +5 diff --git a/test/static/executor/test_data/employees.csv b/test/static/executor/test_data/employees.csv new file mode 100644 index 0000000..fa1dfce --- /dev/null +++ b/test/static/executor/test_data/employees.csv @@ -0,0 +1,12 @@ +id:int,department_id:int +1,3 +2,6 +3,12 +4,11 +5,1 +6,5 +33,31 +66,32 +67,33 +68,34 +69,35 From 2bcf4f79602f9a0048661153a76247a69f53c0aa Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Fri, 19 Dec 2025 22:24:55 +0300 Subject: [PATCH 30/43] Impl left join --- include/stewkk/sql/models/executor/tuple.hpp | 4 ++ .../sql/logic/executor/executor_test.cpp | 44 +++++++++++++++++++ src/stewkk/sql/logic/parser/parser.cpp | 1 + src/stewkk/sql/models/executor/tuple.cpp | 42 ++++++++++++++++++ test/static/executor/expected_inner_join.txt | 3 ++ test/static/executor/expected_left_join.txt | 11 +++++ 6 files changed, 105 insertions(+) create mode 100644 test/static/executor/expected_inner_join.txt create mode 100644 test/static/executor/expected_left_join.txt diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp index 947fd03..cc04f42 100644 --- a/include/stewkk/sql/models/executor/tuple.hpp +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -34,6 +34,8 @@ union NonNullValue { Trilean trilean_value; }; +std::string ToString(Trilean v); + NonNullValue GetTrileanValue(Trilean v); using Value = std::variant; @@ -46,4 +48,6 @@ struct Relation { Tuples tuples; }; +std::string ToString(const Relation& relation); + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index d6b3dd6..dd233b4 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -1,5 +1,8 @@ #include +#include +#include + #include #include #include @@ -14,6 +17,17 @@ namespace stewkk::sql { const static std::string kProjectDir = std::getenv("PWD"); +namespace { + +std::string ReadFromFile(std::filesystem::path path) { + std::ifstream f{path}; + std::ostringstream stream; + stream << f.rdbuf(); + return stream.str(); +} + +} // namespace + TEST(ExecutorTest, SimpleSelect) { boost::asio::io_context ctx; boost::asio::co_spawn( @@ -202,6 +216,36 @@ TEST(ExecutorTest, InnerJoin) { ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); ASSERT_THAT(std::get(got.value().tuples[0][2]).int_value, Eq(3)); ASSERT_THAT(got.value().tuples.size(), Eq(3)); + ASSERT_THAT(ToString(got.value()), Eq(ReadFromFile(kProjectDir+"/test/static/executor/expected_inner_join.txt"))); + }); + + pool.join(); +} + +TEST(ExecutorTest, LeftJoin) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT * FROM employees LEFT OUTER JOIN departments ON employees.department_id = departments.id;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{ + {"departments", "id", Type::kInt}, + {"employees", "id", Type::kInt}, + {"employees", "department_id", Type::kInt}, + })); + ASSERT_THAT(got.value().tuples.size(), Eq(11)); + ASSERT_THAT(ToString(got.value()), Eq(ReadFromFile(kProjectDir+"/test/static/executor/expected_left_join.txt"))); }); pool.join(); diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 6165a86..9ffe304 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -70,6 +70,7 @@ std::string GetDotRepresentation(const Expression& expr) { } std::string GetDotRepresentation(const Operator& op) { + // FIXME: refactor to use osstream struct DotFormatter { std::pair operator()(const Projection& op) { auto exprs = op.expressions diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp index 417e756..bf5a20b 100644 --- a/src/stewkk/sql/models/executor/tuple.cpp +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -1,5 +1,8 @@ #include +#include +#include + namespace stewkk::sql { NonNullValue GetTrileanValue(Trilean v) { @@ -8,4 +11,43 @@ NonNullValue GetTrileanValue(Trilean v) { return res; } +std::string ToString(Trilean v) { + switch (v) { + case Trilean::kTrue: + return "TRUE "; + case Trilean::kFalse: + return "FALSE "; + case Trilean::kUnknown: + return "UNKNOWN "; + } +} + +std::string ToString(const Relation& relation) { + struct FormatVisitor { + void operator()(const NullValue& v) { + s << "NULL "; + } + void operator()(const NonNullValue& v) { + if (attr.type == Type::kInt) { + s << std::format("{:<8}", v.int_value); + return; + } + s << ToString(v.trilean_value) << ' '; + } + + std::ostringstream& s; + const AttributeInfo& attr; + }; + + std::ostringstream s; + for (const auto& tuple : relation.tuples) { + for (const auto& [val, attr] : std::views::zip(tuple, relation.attributes)) { + std::visit(FormatVisitor{s, attr}, val); + } + s << '\n'; + } + + return s.str(); +} + } // namespace stewkk::sql diff --git a/test/static/executor/expected_inner_join.txt b/test/static/executor/expected_inner_join.txt new file mode 100644 index 0000000..21fe6a9 --- /dev/null +++ b/test/static/executor/expected_inner_join.txt @@ -0,0 +1,3 @@ +1 3 3 +5 1 1 +6 5 5 diff --git a/test/static/executor/expected_left_join.txt b/test/static/executor/expected_left_join.txt new file mode 100644 index 0000000..a4a1e6d --- /dev/null +++ b/test/static/executor/expected_left_join.txt @@ -0,0 +1,11 @@ +1 5 1 +3 1 3 +5 6 5 +NULL 2 6 +NULL 3 12 +NULL 4 11 +NULL 33 31 +NULL 66 32 +NULL 67 33 +NULL 68 34 +NULL 69 35 From 582a8a4f30de0685b82763a02823f96ac42626ad Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Fri, 19 Dec 2025 22:28:21 +0300 Subject: [PATCH 31/43] Add right join test --- .../sql/logic/executor/executor_test.cpp | 29 +++++++++++++++++++ test/static/executor/expected_right_join.txt | 5 ++++ 2 files changed, 34 insertions(+) create mode 100644 test/static/executor/expected_right_join.txt diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index dd233b4..f3bdaa2 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -251,4 +251,33 @@ TEST(ExecutorTest, LeftJoin) { pool.join(); } +TEST(ExecutorTest, RightJoin) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan)); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{ + {"employees", "id", Type::kInt}, + {"employees", "department_id", Type::kInt}, + {"departments", "id", Type::kInt}, + })); + ASSERT_THAT(got.value().tuples.size(), Eq(5)); + ASSERT_THAT(ToString(got.value()), Eq(ReadFromFile(kProjectDir+"/test/static/executor/expected_right_join.txt"))); + }); + + pool.join(); +} + } // namespace stewkk::sql diff --git a/test/static/executor/expected_right_join.txt b/test/static/executor/expected_right_join.txt new file mode 100644 index 0000000..380ede1 --- /dev/null +++ b/test/static/executor/expected_right_join.txt @@ -0,0 +1,5 @@ +1 3 3 +5 1 1 +6 5 5 +NULL NULL 2 +NULL NULL 4 From 4688566c53782861ceb71903ed9dcbaa889c70f4 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 20 Dec 2025 13:39:02 +0300 Subject: [PATCH 32/43] Refactor --- CMakeLists.txt | 2 + include/stewkk/sql/models/executor/tuple.hpp | 27 ++-- src/stewkk/sql/CMakeLists.txt | 3 +- src/stewkk/sql/logic/executor/executor.cpp | 152 ++++++------------ .../sql/logic/executor/executor_test.cpp | 17 +- .../sql/logic/executor/sequential_scan.cpp | 4 +- src/stewkk/sql/models/executor/tuple.cpp | 47 +++--- 7 files changed, 95 insertions(+), 157 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0671f9..6e8e076 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ option(SANITIZE_ADDRESS "Enable AddressSanitizer" OFF) option(SANITIZE_UNDEFINED "Enable UndefinedBehaviorSanitizer" OFF) option(SANITIZE_THREADS "Enable ThreadSanitizer" OFF) +add_compile_options(-Wno-deprecated-declarations) + if (SANITIZE_ADDRESS) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp index cc04f42..89f606d 100644 --- a/include/stewkk/sql/models/executor/tuple.hpp +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -2,14 +2,9 @@ #include #include -#include namespace stewkk::sql { -struct NullValue { - auto operator<=>(const NullValue& other) const = default; -}; - enum class Type { kInt, kBool, @@ -23,22 +18,24 @@ struct AttributeInfo { auto operator<=>(const AttributeInfo& other) const = default; }; -enum class Trilean { - kTrue, - kFalse, - kUnknown, -}; - +// NOTE: union leaves possibility to add other data types later union NonNullValue { int64_t int_value; - Trilean trilean_value; + bool bool_value; }; -std::string ToString(Trilean v); +std::string ToString(bool v); + +// NOTE: bare struct for compatibility with llvm +struct Value { + bool is_null; + NonNullValue value; + + bool operator==(const Value& other) const; +}; -NonNullValue GetTrileanValue(Trilean v); +std::string ToString(Value v, const AttributeInfo& attr); -using Value = std::variant; using Tuple = std::vector; using Tuples = std::vector; using AttributesInfo = std::vector; diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 8ebe9a3..793541e 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(libsql logic/executor/sequential_scan.cpp models/executor/tuple.cpp logic/executor/materialization.cpp + logic/executor/llvm.cpp ) add_library(stewkk::libsql ALIAS libsql) target_compile_features(libsql PUBLIC cxx_std_23) @@ -42,7 +43,7 @@ target_include_directories( ) target_compile_options(libsql PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(libsql PRIVATE ${BASE_LINK_FLAGS}) -llvm_map_components_to_libnames(llvm_libs support core irreader) +llvm_map_components_to_libnames(llvm_libs support core irreader orcjit) target_link_libraries(libsql PRIVATE antlr4_static ${llvm_libs} diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index 0ec4ee6..d764622 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -141,52 +141,24 @@ AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const P return result_attributes; } -template - requires std::invocable - && std::same_as, int64_t> -Value ApplyBinaryIntegerOperator(Value lhs, Value rhs) { - if (std::get_if(&lhs) || std::get_if(&rhs)) { - return NullValue{}; - } - auto lhs_value = std::get(std::move(lhs)); - auto rhs_value = std::get(std::move(rhs)); - return NonNullValue{Op{}(lhs_value.int_value, rhs_value.int_value)}; -} - -template +template requires std::invocable - && std::same_as, bool> -Value ApplyCompareIntegersOperator(Value lhs, Value rhs) { - if (std::get_if(&lhs) || std::get_if(&rhs)) { - return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; + && std::same_as, Ret> +Value ApplyIntegersOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; } - auto lhs_value = std::get(std::move(lhs)).int_value; - auto rhs_value = std::get(std::move(rhs)).int_value; - auto res = Op{}(lhs_value, rhs_value); - if (res) { - return GetTrileanValue(Trilean::kTrue); - } - return GetTrileanValue(Trilean::kFalse); + return Value{false, Op{}(lhs.value.int_value, rhs.value.int_value)}; } template requires std::invocable && std::same_as, bool> -Value ApplyBinaryBooleanOperator(Value lhs, Value rhs) { - if (std::get_if(&lhs) || std::get_if(&rhs)) { - return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; - } - auto lhs_value = std::get(std::move(lhs)).trilean_value; - auto rhs_value = std::get(std::move(rhs)).trilean_value; - if (lhs_value == Trilean::kUnknown || rhs_value == Trilean::kUnknown) { - return NonNullValue{GetTrileanValue(Trilean::kUnknown)}; +Value ApplyBooleanOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; } - auto lhs_bool = lhs_value == Trilean::kTrue; - auto rhs_bool = rhs_value == Trilean::kTrue; - if (Op{}(lhs_bool, rhs_bool)) { - return GetTrileanValue(Trilean::kTrue); - } - return GetTrileanValue(Trilean::kFalse); + return Value{false, Op{}(lhs.value.bool_value, rhs.value.bool_value)}; } struct IntPow { @@ -203,50 +175,50 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co switch (expr.binop) { case BinaryOp::kGt: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kLt: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kLe: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kGe: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kNotEq: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kEq: if (GetExpressionTypeUnchecked(*expr.lhs, source_attrs) == Type::kBool) { - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); } - return ApplyCompareIntegersOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, bool>(std::move(lhs), std::move(rhs)); case BinaryOp::kOr: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kAnd: - return ApplyBinaryBooleanOperator>(std::move(lhs), std::move(rhs)); + return ApplyBooleanOperator>(std::move(lhs), std::move(rhs)); case BinaryOp::kPlus: - return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kMinus: - return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kMul: - return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kDiv: - return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kMod: - return ApplyBinaryIntegerOperator>(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator, int64_t>(std::move(lhs), std::move(rhs)); case BinaryOp::kPow: - return ApplyBinaryIntegerOperator(std::move(lhs), std::move(rhs)); + return ApplyIntegersOperator(std::move(lhs), std::move(rhs)); } } Value operator()(const UnaryExpression& expr) { @@ -254,32 +226,18 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co switch (expr.op) { case UnaryOp::kNot: - struct NotVisitor { - Value operator()(const NullValue& n) { - return GetTrileanValue(Trilean::kUnknown); - } - Value operator()(const NonNullValue& val) { - switch (val.trilean_value) { - case Trilean::kTrue: - return GetTrileanValue(Trilean::kFalse); - case Trilean::kFalse: - return GetTrileanValue(Trilean::kTrue); - case Trilean::kUnknown: - return GetTrileanValue(Trilean::kUnknown); - } - } - }; - return std::visit(NotVisitor{}, child); + if (child.is_null) { + return Value{true}; + } + if (child.value.bool_value) { + return Value{false, false}; + } + return Value{false, true}; case UnaryOp::kMinus: - struct MinusVisitor { - Value operator()(const NullValue& n) { - return NullValue{}; - } - Value operator()(const NonNullValue& val) { - return NonNullValue{-val.int_value}; - } - }; - return std::visit(MinusVisitor{}, child); + if (child.is_null) { + return Value{true}; + } + return Value{false, -child.value.int_value}; } } Value operator()(const Attribute& expr) { @@ -292,20 +250,20 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co return source[index]; } Value operator()(const IntConst& expr) { - return NonNullValue{expr}; + return Value{false, expr}; } Value operator()(const Literal& expr) { switch (expr) { case Literal::kNull: - return NullValue{}; + return Value{true}; case Literal::kTrue: { - return GetTrileanValue(Trilean::kTrue); + return Value{false, true}; } case Literal::kFalse: { - return GetTrileanValue(Trilean::kFalse); + return Value{false, false}; } case Literal::kUnknown: { - return GetTrileanValue(Trilean::kUnknown); + return Value{true}; } } } @@ -324,15 +282,11 @@ Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, c } bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const Filter& filter) { - struct FilterVisitor { - bool operator()(const NullValue&) { - return false; - } - bool operator()(const NonNullValue& v) { - return v.trilean_value == Trilean::kTrue; - } - }; - return std::visit(FilterVisitor{}, CalcExpression(source, source_attrs, filter.expr)); + auto v = CalcExpression(source, source_attrs, filter.expr); + if (v.is_null) { + return false; + } + return v.value.bool_value; } boost::asio::awaitable> GetChannels() { @@ -609,7 +563,7 @@ boost::asio::awaitable Executor::ExecuteJoin(const Join& join, for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); auto qual_expr_res = CalcExpression(joined_tuple, attrs, join.qual); - if (std::get(std::move(qual_expr_res)).trilean_value == Trilean::kTrue) { + if (qual_expr_res.value.bool_value) { buf_res.push_back(std::move(joined_tuple)); used[rhs_index] = true; } @@ -633,7 +587,7 @@ boost::asio::awaitable Executor::ExecuteJoin(const Join& join, auto rhs_tuple = std::move(buf_rhs[rhs_index]); auto lhs_size = attrs.size() - rhs_tuple.size(); - Tuple lhs_tuple(lhs_size, NullValue{}); + Tuple lhs_tuple(lhs_size, Value{true}); auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); buf_res.push_back(std::move(joined_tuple)); diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index f3bdaa2..83efc1c 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -47,8 +47,7 @@ TEST(ExecutorTest, SimpleSelect) { ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); - ASSERT_THAT(std::get(got.value().tuples[0][1]).int_value, Eq(33)); + ASSERT_THAT(got.value().tuples[0], Eq(Tuple{Value{false, 1}, Value{false, 33}})); ASSERT_THAT(got.value().tuples.size(), Eq(17)); }); @@ -74,8 +73,7 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}, {"users", "age", Type::kInt}})); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); - ASSERT_THAT(std::get(got.value().tuples[0][1]).int_value, Eq(33)); + ASSERT_THAT(got.value().tuples[0], Eq(Tuple{Value{false, 1}, Value{false, 33}})); ASSERT_THAT(got.value().tuples.size(), Eq(17)); }); @@ -101,7 +99,7 @@ TEST(ExecutorTest, Projection) { ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}})); - ASSERT_THAT(std::get(got.value().tuples[1][0]).int_value, Eq(2)); + ASSERT_THAT(got.value().tuples[1], Eq(Tuple{Value{false, 2}})); ASSERT_THAT(got.value().tuples.size(), Eq(17)); }); @@ -127,7 +125,7 @@ TEST(ExecutorTest, Filter) { ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}})); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(5)); + ASSERT_THAT(got.value().tuples[0], Eq(Tuple{Value{false, 5}})); ASSERT_THAT(got.value().tuples.size(), Eq(2)); }); @@ -153,7 +151,7 @@ TEST(ExecutorTest, FilterMany) { ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{{"users", "id", Type::kInt}})); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); + ASSERT_THAT(got.value().tuples[0], Eq(Tuple{Value{false, 1}})); ASSERT_THAT(got.value().tuples.size(), Eq(15)); }); @@ -183,8 +181,7 @@ TEST(ExecutorTest, CrossJoin) { {"books", "id", Type::kInt}, {"books", "price", Type::kInt}, })); - ASSERT_THAT(std::get(got.value().tuples[0][3]).int_value, Eq(55)); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(5)); + ASSERT_THAT(got.value().tuples[0], Eq(Tuple{Value{false, 5}, Value{false, 1}, Value{false, 1}, Value{false, 55}})); ASSERT_THAT(got.value().tuples.size(), Eq(6)); }); @@ -213,8 +210,6 @@ TEST(ExecutorTest, InnerJoin) { {"employees", "department_id", Type::kInt}, {"departments", "id", Type::kInt}, })); - ASSERT_THAT(std::get(got.value().tuples[0][0]).int_value, Eq(1)); - ASSERT_THAT(std::get(got.value().tuples[0][2]).int_value, Eq(3)); ASSERT_THAT(got.value().tuples.size(), Eq(3)); ASSERT_THAT(ToString(got.value()), Eq(ReadFromFile(kProjectDir+"/test/static/executor/expected_inner_join.txt"))); }); diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index 40f3955..acb675f 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -22,11 +22,11 @@ Type GetTypeFromString(const std::string& s) { Value BuildValueFromString(Type type, const std::string& table, const std::string attr_name, const std::string& value) { if (value == "NULL") { - return NullValue{}; + return Value{true}; } switch (type) { case Type::kInt: - return NonNullValue{std::stoi(value)}; + return Value{false, std::stoi(value)}; default: std::unreachable(); } diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp index bf5a20b..7456c9e 100644 --- a/src/stewkk/sql/models/executor/tuple.cpp +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -5,44 +5,28 @@ namespace stewkk::sql { -NonNullValue GetTrileanValue(Trilean v) { - NonNullValue res; - res.trilean_value = v; - return res; +std::string ToString(bool v) { + if (v) { + return "TRUE "; + } + return "FALSE "; } -std::string ToString(Trilean v) { - switch (v) { - case Trilean::kTrue: - return "TRUE "; - case Trilean::kFalse: - return "FALSE "; - case Trilean::kUnknown: - return "UNKNOWN "; +std::string ToString(Value v, const AttributeInfo& attr) { + if (v.is_null) { + return "NULL "; + } + if (attr.type == Type::kInt) { + return std::format("{:<8}", v.value.int_value); } + return ToString(v.value.bool_value)+' '; } std::string ToString(const Relation& relation) { - struct FormatVisitor { - void operator()(const NullValue& v) { - s << "NULL "; - } - void operator()(const NonNullValue& v) { - if (attr.type == Type::kInt) { - s << std::format("{:<8}", v.int_value); - return; - } - s << ToString(v.trilean_value) << ' '; - } - - std::ostringstream& s; - const AttributeInfo& attr; - }; - std::ostringstream s; for (const auto& tuple : relation.tuples) { for (const auto& [val, attr] : std::views::zip(tuple, relation.attributes)) { - std::visit(FormatVisitor{s, attr}, val); + s << ToString(val, attr); } s << '\n'; } @@ -50,4 +34,9 @@ std::string ToString(const Relation& relation) { return s.str(); } + +bool Value::operator==(const Value& other) const { + return (is_null && other.is_null) || (!is_null && !other.is_null && value.int_value == other.value.int_value); +} + } // namespace stewkk::sql From aaef50b13f7140c6d69b80d57fb7ea28d525c223 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 20 Dec 2025 16:46:22 +0300 Subject: [PATCH 33/43] Add llvm jit --- CMakeLists.txt | 6 +- .../stewkk/sql/logic/executor/executor.hpp | 376 +++++++++++++++++- include/stewkk/sql/logic/executor/llvm.hpp | 35 ++ src/stewkk/sql/CMakeLists.txt | 3 +- src/stewkk/sql/logic/executor/executor.cpp | 336 ++-------------- .../sql/logic/executor/executor_test.cpp | 41 +- src/stewkk/sql/logic/executor/llvm.cpp | 284 +++++++++++++ src/stewkk/sql/models/executor/tuple.cpp | 1 - test/CMakeLists.txt | 3 + 9 files changed, 744 insertions(+), 341 deletions(-) create mode 100644 include/stewkk/sql/logic/executor/llvm.hpp create mode 100644 src/stewkk/sql/logic/executor/llvm.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8e076..1d80203 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,17 +16,17 @@ option(SANITIZE_THREADS "Enable ThreadSanitizer" OFF) add_compile_options(-Wno-deprecated-declarations) if (SANITIZE_ADDRESS) - add_compile_options(-fsanitize=address) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -g) add_link_options(-fsanitize=address) endif() if (SANITIZE_UNDEFINED) - add_compile_options(-fsanitize=undefined) + add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer -g) add_link_options(-fsanitize=undefined) endif() if (SANITIZE_THREADS) - add_compile_options(-fsanitize=thread) + add_compile_options(-fsanitize=thread -fno-omit-frame-pointer -g) add_link_options(-fsanitize=thread) endif() diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index e5a7ad9..08ea97c 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -1,41 +1,401 @@ #pragma once +#include +#include +#include +#include #include #include +#include +#include #include #include +#include +#include #include #include #include #include +#include namespace stewkk::sql { +using ExecExpression = std::function; + +class InterpretedExpressionExecutor { + public: + explicit InterpretedExpressionExecutor(boost::asio::any_io_executor executor); + boost::asio::awaitable GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs); +}; + +class JitCompiledExpressionExecutor { + public: + explicit JitCompiledExpressionExecutor(boost::asio::any_io_executor executor); + boost::asio::awaitable GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs); + private: + JITCompiler compiler_; +}; + +template class Executor { public: using SequentialScan = std::function>( const std::string& table_name, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan)>; - explicit Executor(SequentialScan seq_scan); + Executor(SequentialScan seq_scan, boost::asio::any_io_executor executor); - boost::asio::awaitable> Execute(const Operator& op) const; + boost::asio::awaitable> Execute(const Operator& op); private: boost::asio::awaitable Execute(const Operator& op, AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const; + TuplesChannel& tuples_chan); boost::asio::awaitable ExecuteProjection(const Projection& proj, AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const; + TuplesChannel& tuples_chan); boost::asio::awaitable ExecuteFilter(const Filter& filter, AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const; + TuplesChannel& tuples_chan); boost::asio::awaitable ExecuteCrossJoin(const CrossJoin& cross_join, AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const; + TuplesChannel& tuples_chan); boost::asio::awaitable ExecuteJoin(const Join& join, AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const; - boost::asio::awaitable SpawnExecutor(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuple_chan) const; + TuplesChannel& tuples_chan); + boost::asio::awaitable SpawnExecutor(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuple_chan); private: SequentialScan sequential_scan_; + ExpressionExecutor expression_executor_; +}; + +Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs); +Type GetExpressionTypeUnchecked(const Expression& expr, const AttributesInfo& available_attrs); +AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const Projection& proj); + +template + requires std::invocable + && std::same_as, Ret> +Value ApplyIntegersOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; + } + return Value{false, Op{}(lhs.value.int_value, rhs.value.int_value)}; +} + +template + requires std::invocable + && std::same_as, bool> +Value ApplyBooleanOperator(Value lhs, Value rhs) { + if (lhs.is_null || rhs.is_null) { + return Value{true}; + } + return Value{false, Op{}(lhs.value.bool_value, rhs.value.bool_value)}; +} + +struct IntPow { + int64_t operator()(int64_t base, int64_t exp) const { + return static_cast(std::pow(base, exp)); + } }; +Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr); +Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const std::vector& expressions); +bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const ExecExpression& filter); +boost::asio::awaitable> GetChannels(); +boost::asio::awaitable ReceiveTuples(TuplesChannel& chan); +boost::asio::awaitable ConcatAttrs(AttributesInfoChannel& lhs_attrs_chan, AttributesInfoChannel& rhs_attrs_chan); +boost::asio::awaitable MaterializeChannel(TuplesChannel& tuples_chan); +Tuple ConcatTuples(const Tuple& lhs, const Tuple& rhs); + +template +Executor::Executor(SequentialScan seq_scan, boost::asio::any_io_executor executor) + : sequential_scan_(std::move(seq_scan)), expression_executor_(executor) {} + +template +boost::asio::awaitable> Executor::Execute(const Operator& op) { + auto [attr_chan, tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(op, attr_chan, tuples_chan); + + auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); + std::clog << "Received attrs in root\n"; + + Tuples result; + for (;;) { + auto buf = co_await ReceiveTuples(tuples_chan); + if (buf.empty()) { + break; + } + std::clog << std::format("Received {} tuples in root\n", buf.size()); + std::move(buf.begin(), buf.end(), std::back_inserter(result)); + } + + std::clog << std::format("Total {} tuples in root\n", result.size()); + co_return Ok(Relation{std::move(attrs), std::move(result)}); +} + +template +boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { + struct ExecuteVisitor{ + boost::asio::awaitable operator()(const Table& table) { + co_await executor.sequential_scan_(table.name, attr_chan, tuples_chan); + co_return; + } + boost::asio::awaitable operator()(const Projection& projection) { + // NOTE: We are using multiset relational algebra projection (i.e. not + // eleminating duplicate tuples) + co_await executor.ExecuteProjection(projection, attr_chan, tuples_chan); + co_return; + } + boost::asio::awaitable operator()(const Filter& filter) { + co_await executor.ExecuteFilter(filter, attr_chan, tuples_chan); + co_return; + } + boost::asio::awaitable operator()(const Join& join) { + co_await executor.ExecuteJoin(join, attr_chan, tuples_chan); + co_return; + } + boost::asio::awaitable operator()(const CrossJoin& cross_join) { + co_await executor.ExecuteCrossJoin(cross_join, attr_chan, tuples_chan); + co_return; + } + + AttributesInfoChannel& attr_chan; + TuplesChannel& tuples_chan; + Executor& executor; + }; + co_await std::visit(ExecuteVisitor{attr_chan, tuples_chan, *this}, op); + co_return; +} + +template +boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + std::clog << "Executing projection\n"; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*proj.source, in_attrs_chan, in_tuples_chan); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + auto attrs_after = GetAttributesAfterProjection(attrs, proj); + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, + boost::asio::use_awaitable); + out_attr_chan.close(); + + std::vector executors; + executors.reserve(proj.expressions.size()); + for (const auto& expr : proj.expressions) { + executors.push_back(co_await expression_executor_.GetExpressionExecutor(expr, attrs)); + } + + for (;;) { + auto buf = co_await ReceiveTuples(in_tuples_chan); + if (buf.empty()) { + break; + } + std::clog << std::format("Received {} tuples in projection\n", buf.size()); + buf = buf | std::views::transform([&](const auto& tuple) { + return ApplyProjection(tuple, attrs, executors); + }) | std::ranges::to(); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(buf), + boost::asio::use_awaitable); + } + out_tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, + AttributesInfoChannel& out_attr_chan, + TuplesChannel& out_tuples_chan) { + std::clog << "Executing filter\n"; + auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*filter.source, in_attrs_chan, in_tuples_chan); + + auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); + std::clog << "Filter received attrs\n"; + + if (GetExpressionType(filter.expr, attrs) != Type::kBool) { + throw std::logic_error{"filter expr should return bool"}; + } + + std::clog << "Filter sending attrs\n"; + co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + out_attr_chan.close(); + std::clog << "Filter sent attrs\n"; + + auto filter_executor = co_await expression_executor_.GetExpressionExecutor(filter.expr, attrs); + + Tuples output_buf; + output_buf.reserve(kBufSize); + for (;;) { + auto input_buf = co_await ReceiveTuples(in_tuples_chan); + if (input_buf.empty()) { + break; + } + std::clog << std::format("Received {} tuples in filter\n", input_buf.size()); + auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { + return ApplyFilter(tuple, attrs, filter_executor); + }) | std::views::as_rvalue; + for (auto&& tuple : filtered_view) { + output_buf.push_back(std::move(tuple)); + if (output_buf.size() == kBufSize) { + std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + output_buf.clear(); + } + } + } + std::cout << std::format("{} tuples left in output_buf\n", output_buf.size()); + if (!output_buf.empty()) { + std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); + co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), + boost::asio::use_awaitable); + } + out_tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) { + std::clog << "Executing cross join\n"; + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); + std::clog << "Cross join received attrs\n"; + + std::clog << "Cross join sending attrs\n"; + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + std::clog << "Cross join sent attrs\n"; + + auto reader = co_await MaterializeChannel(lhs_tuples_chan); + std::clog << std::format("Materialized tuples in cross join\n"); + + for (;;) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { + break; + } + std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); + + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_joined; + buf_joined.reserve(kBufSize); + // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) + for (const auto& tuple_rhs : buf_rhs) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + buf_joined.push_back(std::move(joined_tuple)); + } + std::clog << std::format("Sending {} tuples from cross join\n", buf_joined.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), + boost::asio::use_awaitable); + } + } + } + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::ExecuteJoin(const Join& join, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuples_chan) { + std::clog << "Executing join\n"; + if (join.type == JoinType::kFull) { + throw std::logic_error{"Full joins are not supported by executor"}; + } + if (join.type == JoinType::kLeft) { + std::swap(*join.lhs, *join.rhs); + } + auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*join.lhs, lhs_attrs_chan, lhs_tuples_chan); + auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); + co_await SpawnExecutor(*join.rhs, rhs_attrs_chan, rhs_tuples_chan); + + auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); + std::clog << "Join received attrs\n"; + + std::clog << "Join sending attrs\n"; + co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); + attr_chan.close(); + std::clog << "Join sent attrs\n"; + + auto qual_executor = co_await expression_executor_.GetExpressionExecutor(join.qual, attrs); + + auto reader = co_await MaterializeChannel(lhs_tuples_chan); + for (;;) { + auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); + if (buf_rhs.empty()) { + break; + } + std::clog << std::format("Received {} tuples in join as rhs\n", buf_rhs.size()); + + std::vector used(buf_rhs.size(), false); + for (;;) { + auto buf_lhs = reader.Read(); + if (buf_lhs.empty()) { + break; + } + std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); + + for (const auto& tuple_lhs : buf_lhs) { + Tuples buf_res; + buf_res.reserve(kBufSize); + for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { + auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); + auto qual_expr_res = qual_executor(joined_tuple, attrs); + if (qual_expr_res.value.bool_value) { + buf_res.push_back(std::move(joined_tuple)); + used[rhs_index] = true; + } + } + if (!buf_res.empty()) { + std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + + if (join.type == JoinType::kRight || join.type == JoinType::kLeft) { + Tuples buf_res; + buf_res.reserve(kBufSize); + for (auto [rhs_index, is_used] : used | std::views::enumerate) { + if (is_used) { + continue; + } + + auto rhs_tuple = std::move(buf_rhs[rhs_index]); + + auto lhs_size = attrs.size() - rhs_tuple.size(); + Tuple lhs_tuple(lhs_size, Value{true}); + + auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); + buf_res.push_back(std::move(joined_tuple)); + } + if (!buf_res.empty()) { + std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); + co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), + boost::asio::use_awaitable); + } + } + } + tuples_chan.close(); +} + +template +boost::asio::awaitable Executor::SpawnExecutor(const Operator& op, + AttributesInfoChannel& attr_chan, + TuplesChannel& tuple_chan) { + auto executor = co_await boost::asio::this_coro::executor; + boost::asio::co_spawn(executor, Execute(op, attr_chan, tuple_chan), boost::asio::detached); +} + + } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/llvm.hpp b/include/stewkk/sql/logic/executor/llvm.hpp new file mode 100644 index 0000000..1c5e8e3 --- /dev/null +++ b/include/stewkk/sql/logic/executor/llvm.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace stewkk::sql { + +class JITCompiler { + public: + using CompiledExpression = void (*)(Value*, const Value*, const AttributeInfo*); + + explicit JITCompiler(boost::asio::any_io_executor executor); + + boost::asio::awaitable> + CompileExpression(const Expression& expr, const AttributesInfo& attrs); + + private: + llvm::Function* GenerateIR(llvm::Module& llvm_module, const Expression& expr, + const AttributesInfo& attrs); + + private: + std::unique_ptr jit_; + std::atomic id_; + boost::asio::strand jit_strand_; +}; + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/CMakeLists.txt b/src/stewkk/sql/CMakeLists.txt index 793541e..be6ee2a 100644 --- a/src/stewkk/sql/CMakeLists.txt +++ b/src/stewkk/sql/CMakeLists.txt @@ -43,7 +43,8 @@ target_include_directories( ) target_compile_options(libsql PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(libsql PRIVATE ${BASE_LINK_FLAGS}) -llvm_map_components_to_libnames(llvm_libs support core irreader orcjit) +llvm_map_components_to_libnames(llvm_libs Support Core IRReader OrcJIT X86CodeGen X86AsmParser X86Desc X86Info) +target_link_directories(libsql PUBLIC ${LLVM_LIBRARY_DIRS}) target_link_libraries(libsql PRIVATE antlr4_static ${llvm_libs} diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index d764622..d378f77 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -1,20 +1,7 @@ #include -#include -#include -#include -#include - -#include -#include - -#include -#include - namespace stewkk::sql { -namespace { - Type GetExpressionType(const Expression& expr, const AttributesInfo& available_attrs) { struct ExpressionTypeVisitor { Type operator()(const BinaryExpression& binop) const { @@ -141,32 +128,6 @@ AttributesInfo GetAttributesAfterProjection(const AttributesInfo& attrs, const P return result_attributes; } -template - requires std::invocable - && std::same_as, Ret> -Value ApplyIntegersOperator(Value lhs, Value rhs) { - if (lhs.is_null || rhs.is_null) { - return Value{true}; - } - return Value{false, Op{}(lhs.value.int_value, rhs.value.int_value)}; -} - -template - requires std::invocable - && std::same_as, bool> -Value ApplyBooleanOperator(Value lhs, Value rhs) { - if (lhs.is_null || rhs.is_null) { - return Value{true}; - } - return Value{false, Op{}(lhs.value.bool_value, rhs.value.bool_value)}; -} - -struct IntPow { - int64_t operator()(int64_t base, int64_t exp) const { - return static_cast(std::pow(base, exp)); - } -}; - Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, const Expression& expr) { struct ExpressionVisitor { Value operator()(const BinaryExpression& expr) { @@ -275,14 +236,14 @@ Value CalcExpression(const Tuple& source, const AttributesInfo& source_attrs, co return std::visit(ExpressionVisitor{source_attrs, source}, expr); } -Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const Projection& proj) { - return proj.expressions | std::views::transform([&](const auto& target) { - return CalcExpression(source, source_attrs, target); +Tuple ApplyProjection(const Tuple& source, const AttributesInfo& source_attrs, const std::vector& expressions) { + return expressions | std::views::transform([&](const auto& expression) { + return expression(source, source_attrs); }) | std::ranges::to(); } -bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const Filter& filter) { - auto v = CalcExpression(source, source_attrs, filter.expr); +bool ApplyFilter(const Tuple& source, const AttributesInfo& source_attrs, const ExecExpression& filter) { + auto v = filter(source, source_attrs); if (v.is_null) { return false; } @@ -330,283 +291,34 @@ Tuple ConcatTuples(const Tuple& lhs, const Tuple& rhs) { return joined_tuple; } -} // namespace - -Executor::Executor(SequentialScan seq_scan) - : sequential_scan_(std::move(seq_scan)) {} - -boost::asio::awaitable> Executor::Execute(const Operator& op) const { - auto [attr_chan, tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(op, attr_chan, tuples_chan); - - auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); - std::clog << "Received attrs in root\n"; - - Tuples result; - for (;;) { - auto buf = co_await ReceiveTuples(tuples_chan); - if (buf.empty()) { - break; - } - std::clog << std::format("Received {} tuples in root\n", buf.size()); - std::move(buf.begin(), buf.end(), std::back_inserter(result)); - } - - std::clog << std::format("Total {} tuples in root\n", result.size()); - co_return Ok(Relation{std::move(attrs), std::move(result)}); -} - -boost::asio::awaitable Executor::Execute(const Operator& op, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) const { - struct ExecuteVisitor{ - boost::asio::awaitable operator()(const Table& table) { - co_await executor.sequential_scan_(table.name, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Projection& projection) { - // NOTE: We are using multiset relational algebra projection (i.e. not - // eleminating duplicate tuples) - co_await executor.ExecuteProjection(projection, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Filter& filter) { - co_await executor.ExecuteFilter(filter, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const Join& join) { - co_await executor.ExecuteJoin(join, attr_chan, tuples_chan); - co_return; - } - boost::asio::awaitable operator()(const CrossJoin& cross_join) { - co_await executor.ExecuteCrossJoin(cross_join, attr_chan, tuples_chan); - co_return; +boost::asio::awaitable InterpretedExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { + struct Executor { + Value operator()(const Tuple& source, const AttributesInfo& source_attrs) { + return CalcExpression(source, source_attrs, expr); } - AttributesInfoChannel& attr_chan; - TuplesChannel& tuples_chan; - const Executor& executor; + const Expression& expr; }; - co_await std::visit(ExecuteVisitor{attr_chan, tuples_chan, *this}, op); - co_return; -} - -boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, - AttributesInfoChannel& out_attr_chan, - TuplesChannel& out_tuples_chan) const { - std::clog << "Executing projection\n"; - auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*proj.source, in_attrs_chan, in_tuples_chan); - - auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); - auto attrs_after = GetAttributesAfterProjection(attrs, proj); - co_await out_attr_chan.async_send(boost::system::error_code{}, attrs_after, - boost::asio::use_awaitable); - out_attr_chan.close(); - - for (;;) { - auto buf = co_await ReceiveTuples(in_tuples_chan); - if (buf.empty()) { - break; - } - // FIXME: add JIT - std::clog << std::format("Received {} tuples in projection\n", buf.size()); - buf = buf | std::views::transform([&](const auto& tuple) { - return ApplyProjection(tuple, attrs, proj); - }) | std::ranges::to(); - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(buf), - boost::asio::use_awaitable); - } - out_tuples_chan.close(); -} - -boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, - AttributesInfoChannel& out_attr_chan, - TuplesChannel& out_tuples_chan) const { - std::clog << "Executing filter\n"; - auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*filter.source, in_attrs_chan, in_tuples_chan); - - auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); - std::clog << "Filter received attrs\n"; - - if (GetExpressionType(filter.expr, attrs) != Type::kBool) { - throw std::logic_error{"filter expr should return bool"}; - } - - std::clog << "Filter sending attrs\n"; - co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - out_attr_chan.close(); - std::clog << "Filter sent attrs\n"; - - Tuples output_buf; - output_buf.reserve(kBufSize); - for (;;) { - auto input_buf = co_await ReceiveTuples(in_tuples_chan); - if (input_buf.empty()) { - break; - } - // FIXME: add JIT - std::clog << std::format("Received {} tuples in filter\n", input_buf.size()); - auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { - return ApplyFilter(tuple, attrs, filter); - }) | std::views::as_rvalue; - for (auto&& tuple : filtered_view) { - output_buf.push_back(std::move(tuple)); - if (output_buf.size() == kBufSize) { - std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), - boost::asio::use_awaitable); - output_buf.clear(); - } - } - } - std::cout << std::format("{} tuples left in output_buf\n", output_buf.size()); - if (!output_buf.empty()) { - std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); - co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), - boost::asio::use_awaitable); - } - out_tuples_chan.close(); + co_return Executor{expr}; } -boost::asio::awaitable Executor::ExecuteCrossJoin(const CrossJoin& cross_join, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const { - std::clog << "Executing cross join\n"; - auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*cross_join.lhs, lhs_attrs_chan, lhs_tuples_chan); - auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*cross_join.rhs, rhs_attrs_chan, rhs_tuples_chan); - - auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); - std::clog << "Cross join received attrs\n"; - - std::clog << "Cross join sending attrs\n"; - co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - attr_chan.close(); - std::clog << "Cross join sent attrs\n"; - - auto reader = co_await MaterializeChannel(lhs_tuples_chan); - std::clog << std::format("Materialized tuples in cross join\n"); - - for (;;) { - auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); - if (buf_rhs.empty()) { - break; +boost::asio::awaitable JitCompiledExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { + struct Executor { + Value operator()(const Tuple& source, const AttributesInfo& source_attrs) { + Value result; + compiled_expr(&result, source.data(), source_attrs.data()); + return result; } - std::clog << std::format("Received {} tuples in cross join as rhs\n", buf_rhs.size()); - for (;;) { - auto buf_lhs = reader.Read(); - if (buf_lhs.empty()) { - break; - } - std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); - for (const auto& tuple_lhs : buf_lhs) { - Tuples buf_joined; - buf_joined.reserve(kBufSize); - // NOTE: not optimal if rhs is a small table (<< kBufSize tuples) - for (const auto& tuple_rhs : buf_rhs) { - auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); - buf_joined.push_back(std::move(joined_tuple)); - } - std::clog << std::format("Sending {} tuples from cross join\n", buf_joined.size()); - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), - boost::asio::use_awaitable); - } - } - } - tuples_chan.close(); + JITCompiler::CompiledExpression compiled_expr; + llvm::orc::ResourceTrackerSP guard; + }; + auto [compiled_expr, guard] = co_await compiler_.CompileExpression(expr, attrs); + co_return Executor{std::move(compiled_expr), std::move(guard)}; } -boost::asio::awaitable Executor::ExecuteJoin(const Join& join, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuples_chan) const { - std::clog << "Executing join\n"; - if (join.type == JoinType::kFull) { - throw std::logic_error{"Full joins are not supported by executor"}; - } - if (join.type == JoinType::kLeft) { - std::swap(*join.lhs, *join.rhs); - } - auto [lhs_attrs_chan, lhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*join.lhs, lhs_attrs_chan, lhs_tuples_chan); - auto [rhs_attrs_chan, rhs_tuples_chan] = co_await GetChannels(); - co_await SpawnExecutor(*join.rhs, rhs_attrs_chan, rhs_tuples_chan); - - auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); - std::clog << "Join received attrs\n"; - - std::clog << "Join sending attrs\n"; - co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); - attr_chan.close(); - std::clog << "Join sent attrs\n"; - - auto reader = co_await MaterializeChannel(lhs_tuples_chan); - for (;;) { - auto buf_rhs = co_await ReceiveTuples(rhs_tuples_chan); - if (buf_rhs.empty()) { - break; - } - std::clog << std::format("Received {} tuples in join as rhs\n", buf_rhs.size()); - - std::vector used(buf_rhs.size(), false); - for (;;) { - auto buf_lhs = reader.Read(); - if (buf_lhs.empty()) { - break; - } - std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); - - for (const auto& tuple_lhs : buf_lhs) { - Tuples buf_res; - buf_res.reserve(kBufSize); - for (const auto& [rhs_index, tuple_rhs] : buf_rhs | std::views::enumerate) { - auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); - auto qual_expr_res = CalcExpression(joined_tuple, attrs, join.qual); - if (qual_expr_res.value.bool_value) { - buf_res.push_back(std::move(joined_tuple)); - used[rhs_index] = true; - } - } - if (!buf_res.empty()) { - std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), - boost::asio::use_awaitable); - } - } - } +JitCompiledExpressionExecutor::JitCompiledExpressionExecutor(boost::asio::any_io_executor executor) : compiler_(executor) {} - if (join.type == JoinType::kRight || join.type == JoinType::kLeft) { - Tuples buf_res; - buf_res.reserve(kBufSize); - for (auto [rhs_index, is_used] : used | std::views::enumerate) { - if (is_used) { - continue; - } - - auto rhs_tuple = std::move(buf_rhs[rhs_index]); - - auto lhs_size = attrs.size() - rhs_tuple.size(); - Tuple lhs_tuple(lhs_size, Value{true}); - - auto joined_tuple = ConcatTuples(lhs_tuple, rhs_tuple); - buf_res.push_back(std::move(joined_tuple)); - } - if (!buf_res.empty()) { - std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); - co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), - boost::asio::use_awaitable); - } - } - } - tuples_chan.close(); -} - -boost::asio::awaitable Executor::SpawnExecutor(const Operator& op, - AttributesInfoChannel& attr_chan, - TuplesChannel& tuple_chan) const { - auto executor = co_await boost::asio::this_coro::executor; - boost::asio::co_spawn(executor, Execute(op, attr_chan, tuple_chan), boost::asio::detached); -} +InterpretedExpressionExecutor::InterpretedExpressionExecutor(boost::asio::any_io_executor executor) {} } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 83efc1c..3bccad6 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -28,6 +28,11 @@ std::string ReadFromFile(std::filesystem::path path) { } // namespace +template +class ExecutorTest : public testing::Test {}; + +TYPED_TEST_SUITE_P(ExecutorTest); + TEST(ExecutorTest, SimpleSelect) { boost::asio::io_context ctx; boost::asio::co_spawn( @@ -36,7 +41,7 @@ TEST(ExecutorTest, SimpleSelect) { std::stringstream s{"SELECT * FROM users;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -62,7 +67,7 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { std::stringstream s{"SELECT * FROM users;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -80,7 +85,7 @@ TEST(ExecutorTest, SimpleSelectWithParallelism) { pool.join(); } -TEST(ExecutorTest, Projection) { +TYPED_TEST_P(ExecutorTest, Projection) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -88,7 +93,7 @@ TEST(ExecutorTest, Projection) { std::stringstream s{"SELECT users.id FROM users;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -106,7 +111,7 @@ TEST(ExecutorTest, Projection) { pool.join(); } -TEST(ExecutorTest, Filter) { +TYPED_TEST_P(ExecutorTest, Filter) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -114,7 +119,7 @@ TEST(ExecutorTest, Filter) { std::stringstream s{"SELECT users.id FROM users WHERE users.age < 10;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -132,7 +137,7 @@ TEST(ExecutorTest, Filter) { pool.join(); } -TEST(ExecutorTest, FilterMany) { +TYPED_TEST_P(ExecutorTest, FilterMany) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -140,7 +145,7 @@ TEST(ExecutorTest, FilterMany) { std::stringstream s{"SELECT users.id FROM users WHERE users.age > 10;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -158,7 +163,7 @@ TEST(ExecutorTest, FilterMany) { pool.join(); } -TEST(ExecutorTest, CrossJoin) { +TYPED_TEST_P(ExecutorTest, CrossJoin) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -166,7 +171,7 @@ TEST(ExecutorTest, CrossJoin) { std::stringstream s{"SELECT * FROM users, books WHERE users.age < 10;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -188,7 +193,7 @@ TEST(ExecutorTest, CrossJoin) { pool.join(); } -TEST(ExecutorTest, InnerJoin) { +TYPED_TEST_P(ExecutorTest, InnerJoin) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -196,7 +201,7 @@ TEST(ExecutorTest, InnerJoin) { std::stringstream s{"SELECT * FROM employees JOIN departments ON employees.department_id = departments.id;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -217,7 +222,7 @@ TEST(ExecutorTest, InnerJoin) { pool.join(); } -TEST(ExecutorTest, LeftJoin) { +TYPED_TEST_P(ExecutorTest, LeftJoin) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -225,7 +230,7 @@ TEST(ExecutorTest, LeftJoin) { std::stringstream s{"SELECT * FROM employees LEFT OUTER JOIN departments ON employees.department_id = departments.id;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -246,7 +251,7 @@ TEST(ExecutorTest, LeftJoin) { pool.join(); } -TEST(ExecutorTest, RightJoin) { +TYPED_TEST_P(ExecutorTest, RightJoin) { boost::asio::thread_pool pool{4}; boost::asio::co_spawn( pool, @@ -254,7 +259,7 @@ TEST(ExecutorTest, RightJoin) { std::stringstream s{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan)); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); auto got = co_await executor.Execute(op); @@ -275,4 +280,8 @@ TEST(ExecutorTest, RightJoin) { pool.join(); } +REGISTER_TYPED_TEST_SUITE_P(ExecutorTest, Projection, Filter, FilterMany, CrossJoin, InnerJoin, LeftJoin, RightJoin); +using ExecutorTypes = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(TypedExecutorTest, ExecutorTest, ExecutorTypes); + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/llvm.cpp b/src/stewkk/sql/logic/executor/llvm.cpp new file mode 100644 index 0000000..05ea19d --- /dev/null +++ b/src/stewkk/sql/logic/executor/llvm.cpp @@ -0,0 +1,284 @@ +#include + +#include +#include +#include + +namespace stewkk::sql { + +JITCompiler::JITCompiler(boost::asio::any_io_executor executor) : jit_strand_(executor) { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + + auto jit_or_error = llvm::orc::LLJITBuilder().create(); + if (!jit_or_error) { + throw std::runtime_error("failed to create llvm::LLJIT"); + } + jit_ = std::move(*jit_or_error); +} + +boost::asio::awaitable> +JITCompiler::CompileExpression(const Expression& expr, const AttributesInfo& attrs) { + boost::asio::any_io_executor current_executor = co_await boost::asio::this_coro::executor; + co_await boost::asio::dispatch(boost::asio::bind_executor(jit_strand_, boost::asio::deferred)); + + auto resource_tracker = jit_->getMainJITDylib().createResourceTracker(); + + auto ctx = std::make_unique(); + + auto module_name = std::format("expr_module_{}", id_.fetch_add(1)); + auto llvm_module = std::make_unique(std::move(module_name), *ctx); + llvm_module->setDataLayout(jit_->getDataLayout()); + llvm::orc::ThreadSafeModule tsm(std::move(llvm_module), std::move(ctx)); + + auto* func = GenerateIR(*tsm.getModuleUnlocked(), expr, attrs); + + std::string func_name = func->getName().str(); + + tsm.getModuleUnlocked()->print(llvm::errs(), nullptr); + + auto err = jit_->addIRModule(resource_tracker, std::move(tsm)); + if (err) { + throw std::runtime_error("failed to add IR module"); + } + + auto symbol = jit_->lookup(func_name); + if (!symbol) { + throw std::runtime_error("lookup failed"); + } + + auto* compiled_expression = symbol->toPtr(); + co_await boost::asio::dispatch(boost::asio::bind_executor(current_executor, boost::asio::deferred)); + co_return std::make_pair(compiled_expression, std::move(resource_tracker)); +} + +llvm::Function* JITCompiler::GenerateIR( + llvm::Module& llvm_module, + const Expression& expr, + const AttributesInfo& attrs) { + + llvm::IRBuilder<> builder(llvm_module.getContext()); + + auto* value_type = llvm::StructType::create( + llvm_module.getContext(), + {builder.getInt8Ty(), builder.getInt64Ty()}, + "Value"); + + auto* tuple_type = llvm::PointerType::getUnqual(value_type); + auto* attrs_type = llvm::PointerType::getUnqual( + llvm::StructType::get(llvm_module.getContext())); + + auto* result_ptr_type = llvm::PointerType::getUnqual(value_type); + auto* func_type = llvm::FunctionType::get( + llvm::Type::getVoidTy(llvm_module.getContext()), + {result_ptr_type, tuple_type, attrs_type}, false); + auto func_name = std::format("eval_expr_{}", id_.fetch_add(1)); + auto* func = llvm::Function::Create( + func_type, llvm::Function::ExternalLinkage, func_name, &llvm_module); + + func->addParamAttr(0, llvm::Attribute::NoAlias); + + auto* entry = llvm::BasicBlock::Create(llvm_module.getContext(), "entry", func); + builder.SetInsertPoint(entry); + + struct GenerateIRVisitor { + llvm::Value* operator()(const BinaryExpression& expr) { + auto* lhs = std::visit(*this, *expr.lhs); + auto* rhs = std::visit(*this, *expr.rhs); + + auto* is_null_lhs = CheckNull(lhs); + auto* is_null_rhs = CheckNull(rhs); + auto* value_lhs = LoadValue(lhs); + auto* value_rhs = LoadValue(rhs); + + auto* is_null = builder.CreateOr(is_null_lhs, is_null_rhs); + llvm::Value* res_value; + + switch (expr.binop) { + case BinaryOp::kGt: + { + auto* tmp = builder.CreateICmpSGT(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kLt: + { + auto* tmp = builder.CreateICmpSLT(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kLe: + { + auto* tmp = builder.CreateICmpSLE(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kGe: + { + auto* tmp = builder.CreateICmpSGE(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kNotEq: + { + auto* tmp = builder.CreateICmpNE(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kEq: + { + auto* tmp = builder.CreateICmpEQ(value_lhs, value_rhs); + llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); + res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + break; + } + case BinaryOp::kOr: + throw std::logic_error{"or is not supported in llvm codegen"}; + case BinaryOp::kAnd: + throw std::logic_error{"and is not supported in llvm codegen"}; + case BinaryOp::kPlus: + res_value = builder.CreateAdd(value_lhs, value_rhs); + break; + case BinaryOp::kMinus: + res_value = builder.CreateSub(value_lhs, value_rhs); + break; + case BinaryOp::kMul: + res_value = builder.CreateMul(value_lhs, value_rhs); + break; + case BinaryOp::kDiv: + // TODO: handle div by zero + res_value = builder.CreateSDiv(value_lhs, value_rhs); + break; + case BinaryOp::kMod: + // TODO: handle div by zero + res_value = builder.CreateSRem(value_lhs, value_rhs); + break; + case BinaryOp::kPow: + throw std::logic_error{"pow is not supported in llvm codegen"}; + } + + auto* struct_ptr = builder.CreateAlloca(value_type, nullptr, "struct_ptr"); + auto* res_is_null_ptr = builder.CreateStructGEP(value_type, struct_ptr, 0, "ptr_is_null"); + auto* res_value_ptr = builder.CreateStructGEP(value_type, struct_ptr, 1, "ptr_value"); + + auto* is_null_i8 = builder.CreateZExt(is_null, builder.getInt8Ty(), "is_null_i8"); + builder.CreateStore(is_null_i8, res_is_null_ptr); + builder.CreateStore(res_value, res_value_ptr); + + return builder.CreateLoad(value_type, struct_ptr, "result"); + } + llvm::Value* operator()(const UnaryExpression& expr) { + auto* child = std::visit(*this, *expr.child); + switch (expr.op) { + case UnaryOp::kNot: { + auto is_null = CheckNull(child); + auto value = LoadValue(child); + + auto* is_zero = builder.CreateICmpEQ( + value, builder.getIntN(value->getType()->getIntegerBitWidth(), 0), "is_zero"); + auto* logical_not + = builder.CreateZExt(is_zero, value->getType(), "logical_not_result"); + + auto* select = builder.CreateSelect( + is_null, + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(1), builder.getInt64(0)}), + logical_not); + return select; + } + case UnaryOp::kMinus: { + auto is_null = CheckNull(child); + auto value = LoadValue(child); + auto* select = builder.CreateSelect( + is_null, + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(1), builder.getInt64(0)}), + builder.CreateSub(0, value)); + return select; + } + } + } + llvm::Value* operator()(const IntConst& expr) { + return llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(0), builder.getInt64(expr)}); + } + llvm::Value* operator()(const Literal& expr) { + switch (expr) { + case Literal::kNull: + return + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(1), builder.getInt64(0)}); + case Literal::kTrue: + return + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(0), builder.getInt64(1)}); + case Literal::kFalse: + return + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(0), builder.getInt64(0)}); + case Literal::kUnknown: + return + llvm::ConstantStruct::get(static_cast(value_type), + {builder.getInt8(1), builder.getInt64(0)}); + } + } + llvm::Value* operator()(const Attribute& expr) { + auto it = std::find_if( + attrs.begin(), attrs.end(), [&expr](const AttributeInfo& attr_info) { + return attr_info.name == expr.name && attr_info.table == expr.table; + }); + // NOTE: already checked in GetExpressionType + auto index = it - attrs.begin(); + auto* index_const = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm_module.getContext()), index); + + auto* struct_ptr = builder.CreateInBoundsGEP( + value_type, + tuples_arg, + index_const, + "struct_ptr"); + + auto* loaded_struct = builder.CreateLoad( + value_type, + struct_ptr, + "loaded_struct"); + + return loaded_struct; + } + + llvm::Value* LoadIsNull(llvm::Value* v) { + return builder.CreateExtractValue(v, {0}, "value.is_null"); + } + + llvm::Value* LoadValue(llvm::Value* v) { + return builder.CreateExtractValue(v, {1}, "value.value"); + } + + llvm::Value* CheckNull(llvm::Value* v) { + auto* is_null_value = LoadIsNull(v); + return builder.CreateICmpNE( + is_null_value, builder.getInt8(0), "is_null"); + } + + llvm::Module& llvm_module; + llvm::IRBuilder<>& builder; + llvm::StructType* value_type; + const AttributesInfo& attrs; + llvm::Value* tuples_arg; + }; + + auto* result_value = std::visit( + GenerateIRVisitor{llvm_module, builder, value_type, attrs, func->getArg(1)}, expr); + auto* result_ptr = func->getArg(0); + builder.CreateStore(result_value, result_ptr); + builder.CreateRetVoid(); + + return func; +} + +} // namespace stewkk::sql diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp index 7456c9e..0c0d032 100644 --- a/src/stewkk/sql/models/executor/tuple.cpp +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -34,7 +34,6 @@ std::string ToString(const Relation& relation) { return s.str(); } - bool Value::operator==(const Value& other) const { return (is_null && other.is_null) || (!is_null && !other.is_null && value.int_value == other.value.int_value); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e7aea43..65187f8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,9 +20,12 @@ target_include_directories( ) target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) target_link_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) +llvm_map_components_to_libnames(llvm_libs Support Core IRReader OrcJIT X86CodeGen X86AsmParser X86Desc X86Info) +target_link_directories(libsql PUBLIC ${LLVM_LIBRARY_DIRS}) target_link_libraries(unittests PRIVATE gmock_main libsql antlr4_static Boost::asio Boost::thread Boost::filesystem + ${llvm_libs} ) gtest_discover_tests(unittests) From e5d681334f4b326710f80a379e42279a0ac7d79c Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 10 Jan 2026 01:01:20 +0300 Subject: [PATCH 34/43] Impl basic benchmark --- .gitignore | 1 + CMakeLists.txt | 2 + benchmarks/CMakeLists.txt | 16 ++++ benchmarks/main.cpp | 50 ++++++++++ cmake/FetchGBenchmark.cmake | 15 +++ .../stewkk/sql/logic/executor/executor.hpp | 46 ++++++++- src/stewkk/sql/logic/executor/llvm.cpp | 95 ++++++++++++++++--- .../sql/logic/executor/sequential_scan.cpp | 10 ++ src/stewkk/sql/logic/parser/parser.cpp | 8 +- src/stewkk/sql/logic/parser/visitor.cpp | 8 +- 10 files changed, 233 insertions(+), 18 deletions(-) create mode 100644 benchmarks/CMakeLists.txt create mode 100644 benchmarks/main.cpp create mode 100644 cmake/FetchGBenchmark.cmake diff --git a/.gitignore b/.gitignore index d63dfcd..58ef3df 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /CMakeFiles/ **/codegen/ /build-sanitizers/ +/build-release/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d80203..96a5572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_subdirectory(src/stewkk/sql) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + add_subdirectory(benchmarks) + include(CTest) if(BUILD_TESTING) add_subdirectory(test) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 0000000..8cfaea0 --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,16 @@ +include(FetchGBenchmark) + +add_executable(benchmarks main.cpp) +target_compile_features(benchmarks PRIVATE cxx_std_23) +set_target_properties(benchmarks PROPERTIES + CXX_STANDART 23 + CXX_STANDART_REQUIRED YES + CXX_EXTENSIONS YES +) +target_compile_options(benchmarks PRIVATE ${BASE_COMPILE_FLAGS}) +target_link_options(benchmarks PRIVATE ${BASE_LINK_FLAGS}) +target_link_libraries(benchmarks PRIVATE stewkk::libsql benchmark::benchmark + Boost::asio + Boost::thread + Boost::filesystem +) diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp new file mode 100644 index 0000000..83f9a49 --- /dev/null +++ b/benchmarks/main.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace stewkk::sql { + +const static std::string kProjectDir = std::getenv("PWD"); + +namespace { +} // namespace + +template +void BM_SimpleSelect(benchmark::State& state) { + std::ofstream nullstream("/dev/null"); + std::clog.rdbuf(nullstream.rdbuf()); + for (auto _ : state) { + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + []() -> boost::asio::awaitable { + std::stringstream s{"SELECT * FROM users;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); + + benchmark::DoNotOptimize(co_await executor.Execute(op)); + }(), + [](std::exception_ptr p) { + }); + + ctx.run(); + } +} + +BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SimpleSelect); + +} // namespace stewkk::sql + +BENCHMARK_MAIN(); diff --git a/cmake/FetchGBenchmark.cmake b/cmake/FetchGBenchmark.cmake new file mode 100644 index 0000000..dbeb87a --- /dev/null +++ b/cmake/FetchGBenchmark.cmake @@ -0,0 +1,15 @@ +include(FetchContent) +include(FetchGTest) + +FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG 12235e24652fc7f809373e7c11a5f73c5763fc4c +) + +FetchContent_GetProperties(benchmark) +if (NOT benchmark_POPULATED) + FetchContent_Populate(benchmark) + add_subdirectory(${benchmark_SOURCE_DIR} ${benchmark_BINARY_DIR} + EXCLUDE_FROM_ALL) +endif () diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 08ea97c..693b34d 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -114,7 +114,9 @@ boost::asio::awaitable> Executor::Execute(c co_await SpawnExecutor(op, attr_chan, tuples_chan); auto attrs = co_await attr_chan.async_receive(boost::asio::use_awaitable); +#ifdef DEBUG std::clog << "Received attrs in root\n"; +#endif Tuples result; for (;;) { @@ -122,11 +124,15 @@ boost::asio::awaitable> Executor::Execute(c if (buf.empty()) { break; } +#ifdef DEBUG std::clog << std::format("Received {} tuples in root\n", buf.size()); +#endif std::move(buf.begin(), buf.end(), std::back_inserter(result)); } +#ifdef DEBUG std::clog << std::format("Total {} tuples in root\n", result.size()); +#endif co_return Ok(Relation{std::move(attrs), std::move(result)}); } @@ -168,7 +174,9 @@ template boost::asio::awaitable Executor::ExecuteProjection(const Projection& proj, AttributesInfoChannel& out_attr_chan, TuplesChannel& out_tuples_chan) { +#ifdef DEBUG std::clog << "Executing projection\n"; +#endif auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); co_await SpawnExecutor(*proj.source, in_attrs_chan, in_tuples_chan); @@ -189,7 +197,9 @@ boost::asio::awaitable Executor::ExecuteProjection(con if (buf.empty()) { break; } +#ifdef DEBUG std::clog << std::format("Received {} tuples in projection\n", buf.size()); +#endif buf = buf | std::views::transform([&](const auto& tuple) { return ApplyProjection(tuple, attrs, executors); }) | std::ranges::to(); @@ -203,21 +213,29 @@ template boost::asio::awaitable Executor::ExecuteFilter(const Filter& filter, AttributesInfoChannel& out_attr_chan, TuplesChannel& out_tuples_chan) { +#ifdef DEBUG std::clog << "Executing filter\n"; +#endif auto [in_attrs_chan, in_tuples_chan] = co_await GetChannels(); co_await SpawnExecutor(*filter.source, in_attrs_chan, in_tuples_chan); auto attrs = co_await in_attrs_chan.async_receive(boost::asio::use_awaitable); +#ifdef DEBUG std::clog << "Filter received attrs\n"; +#endif if (GetExpressionType(filter.expr, attrs) != Type::kBool) { throw std::logic_error{"filter expr should return bool"}; } +#ifdef DEBUG std::clog << "Filter sending attrs\n"; +#endif co_await out_attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); out_attr_chan.close(); +#ifdef DEBUG std::clog << "Filter sent attrs\n"; +#endif auto filter_executor = co_await expression_executor_.GetExpressionExecutor(filter.expr, attrs); @@ -228,23 +246,31 @@ boost::asio::awaitable Executor::ExecuteFilter(const F if (input_buf.empty()) { break; } +#ifdef DEBUG std::clog << std::format("Received {} tuples in filter\n", input_buf.size()); +#endif auto filtered_view = input_buf | std::views::filter([&](const auto& tuple) { return ApplyFilter(tuple, attrs, filter_executor); }) | std::views::as_rvalue; for (auto&& tuple : filtered_view) { output_buf.push_back(std::move(tuple)); if (output_buf.size() == kBufSize) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); +#endif co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), boost::asio::use_awaitable); output_buf.clear(); } } } - std::cout << std::format("{} tuples left in output_buf\n", output_buf.size()); +#ifdef DEBUG + std::clog << std::format("{} tuples left in output_buf\n", output_buf.size()); +#endif if (!output_buf.empty()) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); + #endif co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), boost::asio::use_awaitable); } @@ -293,7 +319,9 @@ boost::asio::awaitable Executor::ExecuteCrossJoin(cons auto joined_tuple = ConcatTuples(tuple_lhs, tuple_rhs); buf_joined.push_back(std::move(joined_tuple)); } +#ifdef DEBUG std::clog << std::format("Sending {} tuples from cross join\n", buf_joined.size()); +#endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_joined), boost::asio::use_awaitable); } @@ -306,7 +334,9 @@ template boost::asio::awaitable Executor::ExecuteJoin(const Join& join, AttributesInfoChannel& attr_chan, TuplesChannel& tuples_chan) { +#ifdef DEBUG std::clog << "Executing join\n"; +#endif if (join.type == JoinType::kFull) { throw std::logic_error{"Full joins are not supported by executor"}; } @@ -319,12 +349,18 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi co_await SpawnExecutor(*join.rhs, rhs_attrs_chan, rhs_tuples_chan); auto attrs = co_await ConcatAttrs(lhs_attrs_chan, rhs_attrs_chan); +#ifdef DEBUG std::clog << "Join received attrs\n"; +#endif +#ifdef DEBUG std::clog << "Join sending attrs\n"; +#endif co_await attr_chan.async_send(boost::system::error_code{}, attrs, boost::asio::use_awaitable); attr_chan.close(); +#ifdef DEBUG std::clog << "Join sent attrs\n"; +#endif auto qual_executor = co_await expression_executor_.GetExpressionExecutor(join.qual, attrs); @@ -334,7 +370,9 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi if (buf_rhs.empty()) { break; } +#ifdef DEBUG std::clog << std::format("Received {} tuples in join as rhs\n", buf_rhs.size()); +#endif std::vector used(buf_rhs.size(), false); for (;;) { @@ -342,7 +380,9 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi if (buf_lhs.empty()) { break; } +#ifdef DEBUG std::clog << std::format("Read {} tuples back from materialized form\n", buf_lhs.size()); +#endif for (const auto& tuple_lhs : buf_lhs) { Tuples buf_res; @@ -356,7 +396,9 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi } } if (!buf_res.empty()) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); + #endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), boost::asio::use_awaitable); } @@ -380,7 +422,9 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi buf_res.push_back(std::move(joined_tuple)); } if (!buf_res.empty()) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); +#endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), boost::asio::use_awaitable); } diff --git a/src/stewkk/sql/logic/executor/llvm.cpp b/src/stewkk/sql/logic/executor/llvm.cpp index 05ea19d..b4dccba 100644 --- a/src/stewkk/sql/logic/executor/llvm.cpp +++ b/src/stewkk/sql/logic/executor/llvm.cpp @@ -1,8 +1,21 @@ #include +#include + #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace stewkk::sql { @@ -16,6 +29,61 @@ JITCompiler::JITCompiler(boost::asio::any_io_executor executor) : jit_strand_(ex throw std::runtime_error("failed to create llvm::LLJIT"); } jit_ = std::move(*jit_or_error); + + jit_->getIRTransformLayer().setTransform( + [](llvm::orc::ThreadSafeModule tsm, llvm::orc::MaterializationResponsibility& r) + -> llvm::Expected { + tsm.withModuleDo([](llvm::Module& m) { +#ifdef DEBUG + std::clog << "IR before optimization:\n"; + m.print(llvm::errs(), nullptr); +#endif + llvm::LoopAnalysisManager lam; + llvm::FunctionAnalysisManager fam; + llvm::CGSCCAnalysisManager cgam; + llvm::ModuleAnalysisManager mam; + + llvm::PassBuilder pb; + + pb.registerModuleAnalyses(mam); + pb.registerCGSCCAnalyses(cgam); + pb.registerFunctionAnalyses(fam); + pb.registerLoopAnalyses(lam); + pb.crossRegisterProxies(lam, fam, cgam, mam); + + llvm::ModulePassManager mpm; + llvm::FunctionPassManager fpm; + + fpm.addPass(llvm::EarlyCSEPass(true)); + fpm.addPass(llvm::SROAPass(llvm::SROAOptions::ModifyCFG)); + + fpm.addPass(llvm::InstCombinePass()); + fpm.addPass(llvm::SimplifyCFGPass()); + + fpm.addPass(llvm::ReassociatePass()); + fpm.addPass(llvm::GVNPass()); + fpm.addPass(llvm::MemCpyOptPass()); + + fpm.addPass(llvm::SimplifyCFGPass()); + fpm.addPass(llvm::InstCombinePass()); + fpm.addPass(llvm::DCEPass()); + fpm.addPass(llvm::ADCEPass()); + + // TODO: SIMD + //fpm.addPass(llvm::SLPVectorizerPass()); + //fpm.addPass(llvm::LoopVectorizePass()); + + mpm.addPass(llvm::createModuleToFunctionPassAdaptor(std::move(fpm))); + + mpm.run(m, mam); + +#ifdef DEBUG + std::clog << "IR after optimization:\n"; + m.print(llvm::errs(), nullptr); +#endif + }); + return std::move(tsm); + }); } boost::asio::awaitable> @@ -32,11 +100,11 @@ JITCompiler::CompileExpression(const Expression& expr, const AttributesInfo& att llvm_module->setDataLayout(jit_->getDataLayout()); llvm::orc::ThreadSafeModule tsm(std::move(llvm_module), std::move(ctx)); - auto* func = GenerateIR(*tsm.getModuleUnlocked(), expr, attrs); - - std::string func_name = func->getName().str(); - - tsm.getModuleUnlocked()->print(llvm::errs(), nullptr); + std::string func_name; + tsm.withModuleDo([&](llvm::Module& m) { + auto* func = GenerateIR(m, expr, attrs); + func_name = func->getName().str(); + }); auto err = jit_->addIRModule(resource_tracker, std::move(tsm)); if (err) { @@ -163,15 +231,13 @@ llvm::Function* JITCompiler::GenerateIR( throw std::logic_error{"pow is not supported in llvm codegen"}; } - auto* struct_ptr = builder.CreateAlloca(value_type, nullptr, "struct_ptr"); - auto* res_is_null_ptr = builder.CreateStructGEP(value_type, struct_ptr, 0, "ptr_is_null"); - auto* res_value_ptr = builder.CreateStructGEP(value_type, struct_ptr, 1, "ptr_value"); - auto* is_null_i8 = builder.CreateZExt(is_null, builder.getInt8Ty(), "is_null_i8"); - builder.CreateStore(is_null_i8, res_is_null_ptr); - builder.CreateStore(res_value, res_value_ptr); + + llvm::Value* value = llvm::UndefValue::get(value_type); + value = builder.CreateInsertValue(value, is_null_i8, {0}, "result.with_is_null"); + value = builder.CreateInsertValue(value, res_value, {1}, "result"); - return builder.CreateLoad(value_type, struct_ptr, "result"); + return value; } llvm::Value* operator()(const UnaryExpression& expr) { auto* child = std::visit(*this, *expr.child); @@ -195,11 +261,14 @@ llvm::Function* JITCompiler::GenerateIR( case UnaryOp::kMinus: { auto is_null = CheckNull(child); auto value = LoadValue(child); + + auto* negated = builder.CreateNeg(value); + auto* select = builder.CreateSelect( is_null, llvm::ConstantStruct::get(static_cast(value_type), {builder.getInt8(1), builder.getInt64(0)}), - builder.CreateSub(0, value)); + negated); return select; } } diff --git a/src/stewkk/sql/logic/executor/sequential_scan.cpp b/src/stewkk/sql/logic/executor/sequential_scan.cpp index acb675f..26dc178 100644 --- a/src/stewkk/sql/logic/executor/sequential_scan.cpp +++ b/src/stewkk/sql/logic/executor/sequential_scan.cpp @@ -50,7 +50,9 @@ Tuple ParseTuple(const std::string& line, boost::asio::awaitable> CsvDirSequentialScanner::operator()( const std::string& table_name, AttributesInfoChannel& attrs_chan, TuplesChannel& tuples_chan) const { +#ifdef DEBUG std::clog << "Executing sequential scan\n"; +#endif auto path = std::format("{}/{}.csv", dir, table_name); std::ifstream input{std::move(path)}; std::string line; @@ -72,10 +74,14 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()( while (std::getline(input, line)) { auto tuple = ParseTuple(line, attributes, table_name); buf.emplace_back(std::move(tuple)); +#ifdef DEBUG std::clog << std::format("buf size is {}\n", buf.size()); +#endif if (buf.size() == kBufSize) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples from table\n", buf.size()); +#endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); buf.clear(); @@ -83,13 +89,17 @@ boost::asio::awaitable> CsvDirSequentialScanner::operator()( } if (!buf.empty()) { +#ifdef DEBUG std::clog << std::format("Sending {} tuples from table\n", buf.size()); +#endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf), boost::asio::use_awaitable); } tuples_chan.close(); +#ifdef DEBUG std::clog << "Done sending tuples from table\n"; +#endif co_return Ok(); } diff --git a/src/stewkk/sql/logic/parser/parser.cpp b/src/stewkk/sql/logic/parser/parser.cpp index 9ffe304..0615f49 100644 --- a/src/stewkk/sql/logic/parser/parser.cpp +++ b/src/stewkk/sql/logic/parser/parser.cpp @@ -18,9 +18,11 @@ Result GetAST(std::istream& in) { antlr4::CommonTokenStream tokens(&lexer); tokens.fill(); +#ifdef DEBUG for (auto token : tokens.getTokens()) { - std::cout << token->toString() << std::endl; + std::clog << token->toString() << std::endl; } +#endif codegen::PostgreSQLParser parser(&tokens); antlr4::tree::ParseTree* tree = parser.root(); @@ -29,7 +31,9 @@ Result GetAST(std::istream& in) { return MakeError("syntax error"); } - std::cout << tree->toStringTree(&parser, true) << std::endl << std::endl; +#ifdef DEBUG + std::clog << tree->toStringTree(&parser, true) << std::endl << std::endl; +#endif Visitor visitor(&parser); diff --git a/src/stewkk/sql/logic/parser/visitor.cpp b/src/stewkk/sql/logic/parser/visitor.cpp index 6f9aeb3..49b348f 100644 --- a/src/stewkk/sql/logic/parser/visitor.cpp +++ b/src/stewkk/sql/logic/parser/visitor.cpp @@ -72,9 +72,13 @@ std::any Visitor::visit(antlr4::tree::ParseTree *tree) { indentation = rule_context->depth()-1; } std::string indentation_str(indentation*4, ' '); - std::cout << std::format("{}visiting rule: {}\n", indentation_str, rule_name); +#ifdef DEBUG + std::clog << std::format("{}visiting rule: {}\n", indentation_str, rule_name); +#endif auto tmp = codegen::PostgreSQLParserBaseVisitor::visit(tree); - std::cout << std::format("{}exiting rule: {}\n", indentation_str, rule_name); +#ifdef DEBUG + std::clog << std::format("{}exiting rule: {}\n", indentation_str, rule_name); +#endif return tmp; } From 77f844ddea3e309f133ba594afb5981f537a76a6 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 10 Jan 2026 01:53:08 +0300 Subject: [PATCH 35/43] Impl benchmarks --- benchmarks/main.cpp | 16 +++++++++------- include/stewkk/sql/logic/executor/executor.hpp | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index 83f9a49..7bd7a8b 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -16,10 +16,10 @@ namespace stewkk::sql { const static std::string kProjectDir = std::getenv("PWD"); -namespace { -} // namespace +static constexpr char kSimpleSelect[]{"SELECT users.id FROM users;"}; +static constexpr char kJoin[]{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; -template +template void BM_SimpleSelect(benchmark::State& state) { std::ofstream nullstream("/dev/null"); std::clog.rdbuf(nullstream.rdbuf()); @@ -28,10 +28,10 @@ void BM_SimpleSelect(benchmark::State& state) { boost::asio::co_spawn( ctx, []() -> boost::asio::awaitable { - std::stringstream s{"SELECT * FROM users;"}; + std::stringstream s{Query}; Operator op = GetAST(s).value(); CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); benchmark::DoNotOptimize(co_await executor.Execute(op)); }(), @@ -42,8 +42,10 @@ void BM_SimpleSelect(benchmark::State& state) { } } -BENCHMARK(BM_SimpleSelect); -BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SimpleSelect); } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 693b34d..5749fde 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -270,7 +270,7 @@ boost::asio::awaitable Executor::ExecuteFilter(const F if (!output_buf.empty()) { #ifdef DEBUG std::clog << std::format("Sending {} tuples in filter\n", output_buf.size()); - #endif +#endif co_await out_tuples_chan.async_send(boost::system::error_code{}, std::move(output_buf), boost::asio::use_awaitable); } @@ -398,7 +398,7 @@ boost::asio::awaitable Executor::ExecuteJoin(const Joi if (!buf_res.empty()) { #ifdef DEBUG std::clog << std::format("Sending {} tuples from join\n", buf_res.size()); - #endif +#endif co_await tuples_chan.async_send(boost::system::error_code{}, std::move(buf_res), boost::asio::use_awaitable); } From eb2b51dac64548e35082bc85aaeaa54054dfa2f1 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 10 Jan 2026 02:19:28 +0300 Subject: [PATCH 36/43] Fix complex query --- benchmarks/main.cpp | 50 +++++++++++-------- .../stewkk/sql/logic/executor/executor.hpp | 9 ++++ include/stewkk/sql/models/executor/tuple.hpp | 2 + src/stewkk/sql/logic/executor/executor.cpp | 28 ++++++++++- .../sql/logic/executor/executor_test.cpp | 30 ++++++++++- src/stewkk/sql/logic/executor/llvm.cpp | 36 +++++++------ src/stewkk/sql/models/executor/tuple.cpp | 10 ++++ test/static/executor/README.md | 29 +++++++++++ .../static/executor/expected_complex_join.txt | 5 ++ 9 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 test/static/executor/README.md create mode 100644 test/static/executor/expected_complex_join.txt diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index 7bd7a8b..f0f898c 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -16,36 +16,42 @@ namespace stewkk::sql { const static std::string kProjectDir = std::getenv("PWD"); -static constexpr char kSimpleSelect[]{"SELECT users.id FROM users;"}; -static constexpr char kJoin[]{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; +static constexpr char kSimpleSelectSmall[]{"SELECT users.id FROM users;"}; +static constexpr char kJoinSmall[]{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; +static constexpr char kComplexSmall[]{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2 < 30;"}; template -void BM_SimpleSelect(benchmark::State& state) { +void BM_SQL(benchmark::State& state) { std::ofstream nullstream("/dev/null"); std::clog.rdbuf(nullstream.rdbuf()); - for (auto _ : state) { - boost::asio::io_context ctx; - boost::asio::co_spawn( - ctx, - []() -> boost::asio::awaitable { - std::stringstream s{Query}; - Operator op = GetAST(s).value(); - CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; - Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); - + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + [&state]() -> boost::asio::awaitable { + std::stringstream s{Query}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan), + co_await boost::asio::this_coro::executor); + + for (auto _ : state) { benchmark::DoNotOptimize(co_await executor.Execute(op)); - }(), - [](std::exception_ptr p) { - }); + } + }(), + [](std::exception_ptr p) {}); - ctx.run(); - } + ctx.run(); } -BENCHMARK(BM_SimpleSelect); -BENCHMARK(BM_SimpleSelect); -BENCHMARK(BM_SimpleSelect); -BENCHMARK(BM_SimpleSelect); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); } // namespace stewkk::sql diff --git a/include/stewkk/sql/logic/executor/executor.hpp b/include/stewkk/sql/logic/executor/executor.hpp index 5749fde..8cb3765 100644 --- a/include/stewkk/sql/logic/executor/executor.hpp +++ b/include/stewkk/sql/logic/executor/executor.hpp @@ -38,6 +38,15 @@ class JitCompiledExpressionExecutor { JITCompiler compiler_; }; +class CachedJitCompiledExpressionExecutor { + public: + explicit CachedJitCompiledExpressionExecutor(boost::asio::any_io_executor executor); + boost::asio::awaitable GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs); + private: + JITCompiler compiler_; + std::unordered_map cache_; +}; + template class Executor { public: diff --git a/include/stewkk/sql/models/executor/tuple.hpp b/include/stewkk/sql/models/executor/tuple.hpp index 89f606d..b7189a6 100644 --- a/include/stewkk/sql/models/executor/tuple.hpp +++ b/include/stewkk/sql/models/executor/tuple.hpp @@ -10,6 +10,8 @@ enum class Type { kBool, }; +std::string ToString(Type type); + struct AttributeInfo { std::string table; std::string name; diff --git a/src/stewkk/sql/logic/executor/executor.cpp b/src/stewkk/sql/logic/executor/executor.cpp index d378f77..322a550 100644 --- a/src/stewkk/sql/logic/executor/executor.cpp +++ b/src/stewkk/sql/logic/executor/executor.cpp @@ -10,8 +10,8 @@ Type GetExpressionType(const Expression& expr, const AttributesInfo& available_a if (lhs_type != rhs_type) { throw std::logic_error{"types mismatch"}; } - if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kDiv, - BinaryOp::kMod, BinaryOp::kPow}, + if (std::ranges::contains(std::vector{BinaryOp::kPlus, BinaryOp::kMinus, BinaryOp::kMul, + BinaryOp::kDiv, BinaryOp::kMod, BinaryOp::kPow}, binop.binop)) { if (lhs_type != Type::kInt) { throw std::logic_error{"types mismatch"}; @@ -321,4 +321,28 @@ JitCompiledExpressionExecutor::JitCompiledExpressionExecutor(boost::asio::any_io InterpretedExpressionExecutor::InterpretedExpressionExecutor(boost::asio::any_io_executor executor) {} +boost::asio::awaitable CachedJitCompiledExpressionExecutor::GetExpressionExecutor(const Expression& expr, const AttributesInfo& attrs) { + auto expr_str = ToString(expr); + if (auto it = cache_.find(expr_str); it != cache_.end()) { + co_return it->second; + } + + struct Executor { + Value operator()(const Tuple& source, const AttributesInfo& source_attrs) { + Value result; + compiled_expr(&result, source.data(), source_attrs.data()); + return result; + } + + JITCompiler::CompiledExpression compiled_expr; + llvm::orc::ResourceTrackerSP guard; + }; + auto [compiled_expr, guard] = co_await compiler_.CompileExpression(expr, attrs); + auto executor = Executor{std::move(compiled_expr), std::move(guard)}; + cache_[expr_str] = executor; + co_return executor; +} + +CachedJitCompiledExpressionExecutor::CachedJitCompiledExpressionExecutor(boost::asio::any_io_executor executor) : compiler_(executor) {} + } // namespace stewkk::sql diff --git a/src/stewkk/sql/logic/executor/executor_test.cpp b/src/stewkk/sql/logic/executor/executor_test.cpp index 3bccad6..73e182e 100644 --- a/src/stewkk/sql/logic/executor/executor_test.cpp +++ b/src/stewkk/sql/logic/executor/executor_test.cpp @@ -280,7 +280,35 @@ TYPED_TEST_P(ExecutorTest, RightJoin) { pool.join(); } -REGISTER_TYPED_TEST_SUITE_P(ExecutorTest, Projection, Filter, FilterMany, CrossJoin, InnerJoin, LeftJoin, RightJoin); +TYPED_TEST_P(ExecutorTest, ComplexJoin) { + boost::asio::thread_pool pool{4}; + boost::asio::co_spawn( + pool, + []() -> boost::asio::awaitable> { + std::stringstream s{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2 < 30;"}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); + + auto got = co_await executor.Execute(op); + + co_return got; + }(), + [](std::exception_ptr p, Result got) { + if (p) std::rethrow_exception(p); + + ASSERT_THAT(got.value().attributes, Eq(AttributesInfo{ + {"", "", Type::kInt}, + {"", "", Type::kInt}, + })); + ASSERT_THAT(got.value().tuples.size(), Eq(5)); + ASSERT_THAT(ToString(got.value()), Eq(ReadFromFile(kProjectDir+"/test/static/executor/expected_complex_join.txt"))); + }); + + pool.join(); +} + +REGISTER_TYPED_TEST_SUITE_P(ExecutorTest, Projection, Filter, FilterMany, CrossJoin, InnerJoin, LeftJoin, RightJoin, ComplexJoin); using ExecutorTypes = ::testing::Types; INSTANTIATE_TYPED_TEST_SUITE_P(TypedExecutorTest, ExecutorTest, ExecutorTypes); diff --git a/src/stewkk/sql/logic/executor/llvm.cpp b/src/stewkk/sql/logic/executor/llvm.cpp index b4dccba..c807ff0 100644 --- a/src/stewkk/sql/logic/executor/llvm.cpp +++ b/src/stewkk/sql/logic/executor/llvm.cpp @@ -167,49 +167,55 @@ llvm::Function* JITCompiler::GenerateIR( case BinaryOp::kGt: { auto* tmp = builder.CreateICmpSGT(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kLt: { auto* tmp = builder.CreateICmpSLT(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kLe: { auto* tmp = builder.CreateICmpSLE(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kGe: { auto* tmp = builder.CreateICmpSGE(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kNotEq: { auto* tmp = builder.CreateICmpNE(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kEq: { auto* tmp = builder.CreateICmpEQ(value_lhs, value_rhs); - llvm::Type* i64_type = llvm::Type::getInt64Ty(builder.getContext()); - res_value = builder.CreateZExt(tmp, i64_type, "i1_to_i64_zext"); + res_value = builder.CreateZExt(tmp, builder.getInt64Ty(), "i1_to_i64_zext"); break; } case BinaryOp::kOr: - throw std::logic_error{"or is not supported in llvm codegen"}; + { + auto* trunc_lhs = builder.CreateTrunc(value_lhs, builder.getInt1Ty(), "i64_to_i1_trunc"); + auto* trunc_rhs = builder.CreateTrunc(value_rhs, builder.getInt1Ty(), "i64_to_i1_trunc"); + res_value = builder.CreateLogicalOr(trunc_lhs, trunc_rhs); + res_value = builder.CreateZExt(res_value, builder.getInt64Ty(), "i1_to_i64_zext"); + break; + } case BinaryOp::kAnd: - throw std::logic_error{"and is not supported in llvm codegen"}; + { + auto* trunc_lhs = builder.CreateTrunc(value_lhs, builder.getInt1Ty(), "i64_to_i1_trunc"); + auto* trunc_rhs = builder.CreateTrunc(value_rhs, builder.getInt1Ty(), "i64_to_i1_trunc"); + res_value = builder.CreateLogicalAnd(trunc_lhs, trunc_rhs); + res_value = builder.CreateZExt(res_value, builder.getInt64Ty(), "i1_to_i64_zext"); + break; + } case BinaryOp::kPlus: res_value = builder.CreateAdd(value_lhs, value_rhs); break; @@ -304,7 +310,7 @@ llvm::Function* JITCompiler::GenerateIR( }); // NOTE: already checked in GetExpressionType auto index = it - attrs.begin(); - auto* index_const = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm_module.getContext()), index); + auto* index_const = llvm::ConstantInt::get(builder.getInt32Ty(), index); auto* struct_ptr = builder.CreateInBoundsGEP( value_type, diff --git a/src/stewkk/sql/models/executor/tuple.cpp b/src/stewkk/sql/models/executor/tuple.cpp index 0c0d032..463cbb3 100644 --- a/src/stewkk/sql/models/executor/tuple.cpp +++ b/src/stewkk/sql/models/executor/tuple.cpp @@ -5,6 +5,16 @@ namespace stewkk::sql { +std::string ToString(Type type) { + switch (type) { + case Type::kInt: + return "int"; + case Type::kBool: + return "bool"; + } + std::unreachable(); +} + std::string ToString(bool v) { if (v) { return "TRUE "; diff --git a/test/static/executor/README.md b/test/static/executor/README.md new file mode 100644 index 0000000..6c71820 --- /dev/null +++ b/test/static/executor/README.md @@ -0,0 +1,29 @@ +# Test script + +``` sql +CREATE TABLE departments (id INT); +CREATE TABLE employees (id INT, department_id INT); + +INSERT INTO departments (id) VALUES +(1), +(2), +(3), +(4), +(5); + +INSERT INTO employees (id, department_id) VALUES +(1,3), +(2,6), +(3,12), +(4,11), +(5,1), +(6,5), +(33,31), +(66,32), +(67,33), +(68,34), +(69,35); + +SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2 < 30; +``` + diff --git a/test/static/executor/expected_complex_join.txt b/test/static/executor/expected_complex_join.txt new file mode 100644 index 0000000..58b4105 --- /dev/null +++ b/test/static/executor/expected_complex_join.txt @@ -0,0 +1,5 @@ +10 7 +2 NULL +4 NULL +6 NULL +8 NULL From 561f6e394b59057828d0397c186ca8cd8f3da08c Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 10 Jan 2026 21:09:36 +0300 Subject: [PATCH 37/43] Add more benchmarks --- benchmarks/main.cpp | 31 +- .../executor/test_data/departments_1000.csv | 1001 + .../executor/test_data/departments_16000.csv | 16001 ++++++++++++++++ .../executor/test_data/departments_2000.csv | 2001 ++ .../executor/test_data/departments_4000.csv | 4001 ++++ .../executor/test_data/departments_500.csv | 1001 + .../executor/test_data/departments_8000.csv | 8001 ++++++++ .../executor/test_data/employees_200.csv | 201 + 8 files changed, 32234 insertions(+), 4 deletions(-) create mode 100644 test/static/executor/test_data/departments_1000.csv create mode 100644 test/static/executor/test_data/departments_16000.csv create mode 100644 test/static/executor/test_data/departments_2000.csv create mode 100644 test/static/executor/test_data/departments_4000.csv create mode 100644 test/static/executor/test_data/departments_500.csv create mode 100644 test/static/executor/test_data/departments_8000.csv create mode 100644 test/static/executor/test_data/employees_200.csv diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index f0f898c..a231158 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -18,7 +18,13 @@ const static std::string kProjectDir = std::getenv("PWD"); static constexpr char kSimpleSelectSmall[]{"SELECT users.id FROM users;"}; static constexpr char kJoinSmall[]{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; -static constexpr char kComplexSmall[]{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2 < 30;"}; +static constexpr char kComplexSmall[]{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex500[]{"SELECT departments_500.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_500 ON employees_200.department_id = departments_500.id AND departments_500.id > 3 AND departments_500.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex1000[]{"SELECT departments_1000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_1000 ON employees_200.department_id = departments_1000.id AND departments_1000.id > 3 AND departments_1000.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex2000[]{"SELECT departments_2000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_2000 ON employees_200.department_id = departments_2000.id AND departments_2000.id > 3 AND departments_2000.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex4000[]{"SELECT departments_4000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_4000 ON employees_200.department_id = departments_4000.id AND departments_4000.id > 3 AND departments_4000.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex8000[]{"SELECT departments_8000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_8000 ON employees_200.department_id = departments_8000.id AND departments_8000.id > 3 AND departments_8000.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex16000[]{"SELECT departments_16000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_16000 ON employees_200.department_id = departments_16000.id AND departments_16000.id > 3 AND departments_16000.id*2*2/2/2*2 < 30;"}; template void BM_SQL(benchmark::State& state) { @@ -44,15 +50,32 @@ void BM_SQL(benchmark::State& state) { } BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); + BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); + BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); + } // namespace stewkk::sql BENCHMARK_MAIN(); diff --git a/test/static/executor/test_data/departments_1000.csv b/test/static/executor/test_data/departments_1000.csv new file mode 100644 index 0000000..14dcc8c --- /dev/null +++ b/test/static/executor/test_data/departments_1000.csv @@ -0,0 +1,1001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 diff --git a/test/static/executor/test_data/departments_16000.csv b/test/static/executor/test_data/departments_16000.csv new file mode 100644 index 0000000..1b181a1 --- /dev/null +++ b/test/static/executor/test_data/departments_16000.csv @@ -0,0 +1,16001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +1143 +1144 +1145 +1146 +1147 +1148 +1149 +1150 +1151 +1152 +1153 +1154 +1155 +1156 +1157 +1158 +1159 +1160 +1161 +1162 +1163 +1164 +1165 +1166 +1167 +1168 +1169 +1170 +1171 +1172 +1173 +1174 +1175 +1176 +1177 +1178 +1179 +1180 +1181 +1182 +1183 +1184 +1185 +1186 +1187 +1188 +1189 +1190 +1191 +1192 +1193 +1194 +1195 +1196 +1197 +1198 +1199 +1200 +1201 +1202 +1203 +1204 +1205 +1206 +1207 +1208 +1209 +1210 +1211 +1212 +1213 +1214 +1215 +1216 +1217 +1218 +1219 +1220 +1221 +1222 +1223 +1224 +1225 +1226 +1227 +1228 +1229 +1230 +1231 +1232 +1233 +1234 +1235 +1236 +1237 +1238 +1239 +1240 +1241 +1242 +1243 +1244 +1245 +1246 +1247 +1248 +1249 +1250 +1251 +1252 +1253 +1254 +1255 +1256 +1257 +1258 +1259 +1260 +1261 +1262 +1263 +1264 +1265 +1266 +1267 +1268 +1269 +1270 +1271 +1272 +1273 +1274 +1275 +1276 +1277 +1278 +1279 +1280 +1281 +1282 +1283 +1284 +1285 +1286 +1287 +1288 +1289 +1290 +1291 +1292 +1293 +1294 +1295 +1296 +1297 +1298 +1299 +1300 +1301 +1302 +1303 +1304 +1305 +1306 +1307 +1308 +1309 +1310 +1311 +1312 +1313 +1314 +1315 +1316 +1317 +1318 +1319 +1320 +1321 +1322 +1323 +1324 +1325 +1326 +1327 +1328 +1329 +1330 +1331 +1332 +1333 +1334 +1335 +1336 +1337 +1338 +1339 +1340 +1341 +1342 +1343 +1344 +1345 +1346 +1347 +1348 +1349 +1350 +1351 +1352 +1353 +1354 +1355 +1356 +1357 +1358 +1359 +1360 +1361 +1362 +1363 +1364 +1365 +1366 +1367 +1368 +1369 +1370 +1371 +1372 +1373 +1374 +1375 +1376 +1377 +1378 +1379 +1380 +1381 +1382 +1383 +1384 +1385 +1386 +1387 +1388 +1389 +1390 +1391 +1392 +1393 +1394 +1395 +1396 +1397 +1398 +1399 +1400 +1401 +1402 +1403 +1404 +1405 +1406 +1407 +1408 +1409 +1410 +1411 +1412 +1413 +1414 +1415 +1416 +1417 +1418 +1419 +1420 +1421 +1422 +1423 +1424 +1425 +1426 +1427 +1428 +1429 +1430 +1431 +1432 +1433 +1434 +1435 +1436 +1437 +1438 +1439 +1440 +1441 +1442 +1443 +1444 +1445 +1446 +1447 +1448 +1449 +1450 +1451 +1452 +1453 +1454 +1455 +1456 +1457 +1458 +1459 +1460 +1461 +1462 +1463 +1464 +1465 +1466 +1467 +1468 +1469 +1470 +1471 +1472 +1473 +1474 +1475 +1476 +1477 +1478 +1479 +1480 +1481 +1482 +1483 +1484 +1485 +1486 +1487 +1488 +1489 +1490 +1491 +1492 +1493 +1494 +1495 +1496 +1497 +1498 +1499 +1500 +1501 +1502 +1503 +1504 +1505 +1506 +1507 +1508 +1509 +1510 +1511 +1512 +1513 +1514 +1515 +1516 +1517 +1518 +1519 +1520 +1521 +1522 +1523 +1524 +1525 +1526 +1527 +1528 +1529 +1530 +1531 +1532 +1533 +1534 +1535 +1536 +1537 +1538 +1539 +1540 +1541 +1542 +1543 +1544 +1545 +1546 +1547 +1548 +1549 +1550 +1551 +1552 +1553 +1554 +1555 +1556 +1557 +1558 +1559 +1560 +1561 +1562 +1563 +1564 +1565 +1566 +1567 +1568 +1569 +1570 +1571 +1572 +1573 +1574 +1575 +1576 +1577 +1578 +1579 +1580 +1581 +1582 +1583 +1584 +1585 +1586 +1587 +1588 +1589 +1590 +1591 +1592 +1593 +1594 +1595 +1596 +1597 +1598 +1599 +1600 +1601 +1602 +1603 +1604 +1605 +1606 +1607 +1608 +1609 +1610 +1611 +1612 +1613 +1614 +1615 +1616 +1617 +1618 +1619 +1620 +1621 +1622 +1623 +1624 +1625 +1626 +1627 +1628 +1629 +1630 +1631 +1632 +1633 +1634 +1635 +1636 +1637 +1638 +1639 +1640 +1641 +1642 +1643 +1644 +1645 +1646 +1647 +1648 +1649 +1650 +1651 +1652 +1653 +1654 +1655 +1656 +1657 +1658 +1659 +1660 +1661 +1662 +1663 +1664 +1665 +1666 +1667 +1668 +1669 +1670 +1671 +1672 +1673 +1674 +1675 +1676 +1677 +1678 +1679 +1680 +1681 +1682 +1683 +1684 +1685 +1686 +1687 +1688 +1689 +1690 +1691 +1692 +1693 +1694 +1695 +1696 +1697 +1698 +1699 +1700 +1701 +1702 +1703 +1704 +1705 +1706 +1707 +1708 +1709 +1710 +1711 +1712 +1713 +1714 +1715 +1716 +1717 +1718 +1719 +1720 +1721 +1722 +1723 +1724 +1725 +1726 +1727 +1728 +1729 +1730 +1731 +1732 +1733 +1734 +1735 +1736 +1737 +1738 +1739 +1740 +1741 +1742 +1743 +1744 +1745 +1746 +1747 +1748 +1749 +1750 +1751 +1752 +1753 +1754 +1755 +1756 +1757 +1758 +1759 +1760 +1761 +1762 +1763 +1764 +1765 +1766 +1767 +1768 +1769 +1770 +1771 +1772 +1773 +1774 +1775 +1776 +1777 +1778 +1779 +1780 +1781 +1782 +1783 +1784 +1785 +1786 +1787 +1788 +1789 +1790 +1791 +1792 +1793 +1794 +1795 +1796 +1797 +1798 +1799 +1800 +1801 +1802 +1803 +1804 +1805 +1806 +1807 +1808 +1809 +1810 +1811 +1812 +1813 +1814 +1815 +1816 +1817 +1818 +1819 +1820 +1821 +1822 +1823 +1824 +1825 +1826 +1827 +1828 +1829 +1830 +1831 +1832 +1833 +1834 +1835 +1836 +1837 +1838 +1839 +1840 +1841 +1842 +1843 +1844 +1845 +1846 +1847 +1848 +1849 +1850 +1851 +1852 +1853 +1854 +1855 +1856 +1857 +1858 +1859 +1860 +1861 +1862 +1863 +1864 +1865 +1866 +1867 +1868 +1869 +1870 +1871 +1872 +1873 +1874 +1875 +1876 +1877 +1878 +1879 +1880 +1881 +1882 +1883 +1884 +1885 +1886 +1887 +1888 +1889 +1890 +1891 +1892 +1893 +1894 +1895 +1896 +1897 +1898 +1899 +1900 +1901 +1902 +1903 +1904 +1905 +1906 +1907 +1908 +1909 +1910 +1911 +1912 +1913 +1914 +1915 +1916 +1917 +1918 +1919 +1920 +1921 +1922 +1923 +1924 +1925 +1926 +1927 +1928 +1929 +1930 +1931 +1932 +1933 +1934 +1935 +1936 +1937 +1938 +1939 +1940 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +1965 +1966 +1967 +1968 +1969 +1970 +1971 +1972 +1973 +1974 +1975 +1976 +1977 +1978 +1979 +1980 +1981 +1982 +1983 +1984 +1985 +1986 +1987 +1988 +1989 +1990 +1991 +1992 +1993 +1994 +1995 +1996 +1997 +1998 +1999 +2000 +2001 +2002 +2003 +2004 +2005 +2006 +2007 +2008 +2009 +2010 +2011 +2012 +2013 +2014 +2015 +2016 +2017 +2018 +2019 +2020 +2021 +2022 +2023 +2024 +2025 +2026 +2027 +2028 +2029 +2030 +2031 +2032 +2033 +2034 +2035 +2036 +2037 +2038 +2039 +2040 +2041 +2042 +2043 +2044 +2045 +2046 +2047 +2048 +2049 +2050 +2051 +2052 +2053 +2054 +2055 +2056 +2057 +2058 +2059 +2060 +2061 +2062 +2063 +2064 +2065 +2066 +2067 +2068 +2069 +2070 +2071 +2072 +2073 +2074 +2075 +2076 +2077 +2078 +2079 +2080 +2081 +2082 +2083 +2084 +2085 +2086 +2087 +2088 +2089 +2090 +2091 +2092 +2093 +2094 +2095 +2096 +2097 +2098 +2099 +2100 +2101 +2102 +2103 +2104 +2105 +2106 +2107 +2108 +2109 +2110 +2111 +2112 +2113 +2114 +2115 +2116 +2117 +2118 +2119 +2120 +2121 +2122 +2123 +2124 +2125 +2126 +2127 +2128 +2129 +2130 +2131 +2132 +2133 +2134 +2135 +2136 +2137 +2138 +2139 +2140 +2141 +2142 +2143 +2144 +2145 +2146 +2147 +2148 +2149 +2150 +2151 +2152 +2153 +2154 +2155 +2156 +2157 +2158 +2159 +2160 +2161 +2162 +2163 +2164 +2165 +2166 +2167 +2168 +2169 +2170 +2171 +2172 +2173 +2174 +2175 +2176 +2177 +2178 +2179 +2180 +2181 +2182 +2183 +2184 +2185 +2186 +2187 +2188 +2189 +2190 +2191 +2192 +2193 +2194 +2195 +2196 +2197 +2198 +2199 +2200 +2201 +2202 +2203 +2204 +2205 +2206 +2207 +2208 +2209 +2210 +2211 +2212 +2213 +2214 +2215 +2216 +2217 +2218 +2219 +2220 +2221 +2222 +2223 +2224 +2225 +2226 +2227 +2228 +2229 +2230 +2231 +2232 +2233 +2234 +2235 +2236 +2237 +2238 +2239 +2240 +2241 +2242 +2243 +2244 +2245 +2246 +2247 +2248 +2249 +2250 +2251 +2252 +2253 +2254 +2255 +2256 +2257 +2258 +2259 +2260 +2261 +2262 +2263 +2264 +2265 +2266 +2267 +2268 +2269 +2270 +2271 +2272 +2273 +2274 +2275 +2276 +2277 +2278 +2279 +2280 +2281 +2282 +2283 +2284 +2285 +2286 +2287 +2288 +2289 +2290 +2291 +2292 +2293 +2294 +2295 +2296 +2297 +2298 +2299 +2300 +2301 +2302 +2303 +2304 +2305 +2306 +2307 +2308 +2309 +2310 +2311 +2312 +2313 +2314 +2315 +2316 +2317 +2318 +2319 +2320 +2321 +2322 +2323 +2324 +2325 +2326 +2327 +2328 +2329 +2330 +2331 +2332 +2333 +2334 +2335 +2336 +2337 +2338 +2339 +2340 +2341 +2342 +2343 +2344 +2345 +2346 +2347 +2348 +2349 +2350 +2351 +2352 +2353 +2354 +2355 +2356 +2357 +2358 +2359 +2360 +2361 +2362 +2363 +2364 +2365 +2366 +2367 +2368 +2369 +2370 +2371 +2372 +2373 +2374 +2375 +2376 +2377 +2378 +2379 +2380 +2381 +2382 +2383 +2384 +2385 +2386 +2387 +2388 +2389 +2390 +2391 +2392 +2393 +2394 +2395 +2396 +2397 +2398 +2399 +2400 +2401 +2402 +2403 +2404 +2405 +2406 +2407 +2408 +2409 +2410 +2411 +2412 +2413 +2414 +2415 +2416 +2417 +2418 +2419 +2420 +2421 +2422 +2423 +2424 +2425 +2426 +2427 +2428 +2429 +2430 +2431 +2432 +2433 +2434 +2435 +2436 +2437 +2438 +2439 +2440 +2441 +2442 +2443 +2444 +2445 +2446 +2447 +2448 +2449 +2450 +2451 +2452 +2453 +2454 +2455 +2456 +2457 +2458 +2459 +2460 +2461 +2462 +2463 +2464 +2465 +2466 +2467 +2468 +2469 +2470 +2471 +2472 +2473 +2474 +2475 +2476 +2477 +2478 +2479 +2480 +2481 +2482 +2483 +2484 +2485 +2486 +2487 +2488 +2489 +2490 +2491 +2492 +2493 +2494 +2495 +2496 +2497 +2498 +2499 +2500 +2501 +2502 +2503 +2504 +2505 +2506 +2507 +2508 +2509 +2510 +2511 +2512 +2513 +2514 +2515 +2516 +2517 +2518 +2519 +2520 +2521 +2522 +2523 +2524 +2525 +2526 +2527 +2528 +2529 +2530 +2531 +2532 +2533 +2534 +2535 +2536 +2537 +2538 +2539 +2540 +2541 +2542 +2543 +2544 +2545 +2546 +2547 +2548 +2549 +2550 +2551 +2552 +2553 +2554 +2555 +2556 +2557 +2558 +2559 +2560 +2561 +2562 +2563 +2564 +2565 +2566 +2567 +2568 +2569 +2570 +2571 +2572 +2573 +2574 +2575 +2576 +2577 +2578 +2579 +2580 +2581 +2582 +2583 +2584 +2585 +2586 +2587 +2588 +2589 +2590 +2591 +2592 +2593 +2594 +2595 +2596 +2597 +2598 +2599 +2600 +2601 +2602 +2603 +2604 +2605 +2606 +2607 +2608 +2609 +2610 +2611 +2612 +2613 +2614 +2615 +2616 +2617 +2618 +2619 +2620 +2621 +2622 +2623 +2624 +2625 +2626 +2627 +2628 +2629 +2630 +2631 +2632 +2633 +2634 +2635 +2636 +2637 +2638 +2639 +2640 +2641 +2642 +2643 +2644 +2645 +2646 +2647 +2648 +2649 +2650 +2651 +2652 +2653 +2654 +2655 +2656 +2657 +2658 +2659 +2660 +2661 +2662 +2663 +2664 +2665 +2666 +2667 +2668 +2669 +2670 +2671 +2672 +2673 +2674 +2675 +2676 +2677 +2678 +2679 +2680 +2681 +2682 +2683 +2684 +2685 +2686 +2687 +2688 +2689 +2690 +2691 +2692 +2693 +2694 +2695 +2696 +2697 +2698 +2699 +2700 +2701 +2702 +2703 +2704 +2705 +2706 +2707 +2708 +2709 +2710 +2711 +2712 +2713 +2714 +2715 +2716 +2717 +2718 +2719 +2720 +2721 +2722 +2723 +2724 +2725 +2726 +2727 +2728 +2729 +2730 +2731 +2732 +2733 +2734 +2735 +2736 +2737 +2738 +2739 +2740 +2741 +2742 +2743 +2744 +2745 +2746 +2747 +2748 +2749 +2750 +2751 +2752 +2753 +2754 +2755 +2756 +2757 +2758 +2759 +2760 +2761 +2762 +2763 +2764 +2765 +2766 +2767 +2768 +2769 +2770 +2771 +2772 +2773 +2774 +2775 +2776 +2777 +2778 +2779 +2780 +2781 +2782 +2783 +2784 +2785 +2786 +2787 +2788 +2789 +2790 +2791 +2792 +2793 +2794 +2795 +2796 +2797 +2798 +2799 +2800 +2801 +2802 +2803 +2804 +2805 +2806 +2807 +2808 +2809 +2810 +2811 +2812 +2813 +2814 +2815 +2816 +2817 +2818 +2819 +2820 +2821 +2822 +2823 +2824 +2825 +2826 +2827 +2828 +2829 +2830 +2831 +2832 +2833 +2834 +2835 +2836 +2837 +2838 +2839 +2840 +2841 +2842 +2843 +2844 +2845 +2846 +2847 +2848 +2849 +2850 +2851 +2852 +2853 +2854 +2855 +2856 +2857 +2858 +2859 +2860 +2861 +2862 +2863 +2864 +2865 +2866 +2867 +2868 +2869 +2870 +2871 +2872 +2873 +2874 +2875 +2876 +2877 +2878 +2879 +2880 +2881 +2882 +2883 +2884 +2885 +2886 +2887 +2888 +2889 +2890 +2891 +2892 +2893 +2894 +2895 +2896 +2897 +2898 +2899 +2900 +2901 +2902 +2903 +2904 +2905 +2906 +2907 +2908 +2909 +2910 +2911 +2912 +2913 +2914 +2915 +2916 +2917 +2918 +2919 +2920 +2921 +2922 +2923 +2924 +2925 +2926 +2927 +2928 +2929 +2930 +2931 +2932 +2933 +2934 +2935 +2936 +2937 +2938 +2939 +2940 +2941 +2942 +2943 +2944 +2945 +2946 +2947 +2948 +2949 +2950 +2951 +2952 +2953 +2954 +2955 +2956 +2957 +2958 +2959 +2960 +2961 +2962 +2963 +2964 +2965 +2966 +2967 +2968 +2969 +2970 +2971 +2972 +2973 +2974 +2975 +2976 +2977 +2978 +2979 +2980 +2981 +2982 +2983 +2984 +2985 +2986 +2987 +2988 +2989 +2990 +2991 +2992 +2993 +2994 +2995 +2996 +2997 +2998 +2999 +3000 +3001 +3002 +3003 +3004 +3005 +3006 +3007 +3008 +3009 +3010 +3011 +3012 +3013 +3014 +3015 +3016 +3017 +3018 +3019 +3020 +3021 +3022 +3023 +3024 +3025 +3026 +3027 +3028 +3029 +3030 +3031 +3032 +3033 +3034 +3035 +3036 +3037 +3038 +3039 +3040 +3041 +3042 +3043 +3044 +3045 +3046 +3047 +3048 +3049 +3050 +3051 +3052 +3053 +3054 +3055 +3056 +3057 +3058 +3059 +3060 +3061 +3062 +3063 +3064 +3065 +3066 +3067 +3068 +3069 +3070 +3071 +3072 +3073 +3074 +3075 +3076 +3077 +3078 +3079 +3080 +3081 +3082 +3083 +3084 +3085 +3086 +3087 +3088 +3089 +3090 +3091 +3092 +3093 +3094 +3095 +3096 +3097 +3098 +3099 +3100 +3101 +3102 +3103 +3104 +3105 +3106 +3107 +3108 +3109 +3110 +3111 +3112 +3113 +3114 +3115 +3116 +3117 +3118 +3119 +3120 +3121 +3122 +3123 +3124 +3125 +3126 +3127 +3128 +3129 +3130 +3131 +3132 +3133 +3134 +3135 +3136 +3137 +3138 +3139 +3140 +3141 +3142 +3143 +3144 +3145 +3146 +3147 +3148 +3149 +3150 +3151 +3152 +3153 +3154 +3155 +3156 +3157 +3158 +3159 +3160 +3161 +3162 +3163 +3164 +3165 +3166 +3167 +3168 +3169 +3170 +3171 +3172 +3173 +3174 +3175 +3176 +3177 +3178 +3179 +3180 +3181 +3182 +3183 +3184 +3185 +3186 +3187 +3188 +3189 +3190 +3191 +3192 +3193 +3194 +3195 +3196 +3197 +3198 +3199 +3200 +3201 +3202 +3203 +3204 +3205 +3206 +3207 +3208 +3209 +3210 +3211 +3212 +3213 +3214 +3215 +3216 +3217 +3218 +3219 +3220 +3221 +3222 +3223 +3224 +3225 +3226 +3227 +3228 +3229 +3230 +3231 +3232 +3233 +3234 +3235 +3236 +3237 +3238 +3239 +3240 +3241 +3242 +3243 +3244 +3245 +3246 +3247 +3248 +3249 +3250 +3251 +3252 +3253 +3254 +3255 +3256 +3257 +3258 +3259 +3260 +3261 +3262 +3263 +3264 +3265 +3266 +3267 +3268 +3269 +3270 +3271 +3272 +3273 +3274 +3275 +3276 +3277 +3278 +3279 +3280 +3281 +3282 +3283 +3284 +3285 +3286 +3287 +3288 +3289 +3290 +3291 +3292 +3293 +3294 +3295 +3296 +3297 +3298 +3299 +3300 +3301 +3302 +3303 +3304 +3305 +3306 +3307 +3308 +3309 +3310 +3311 +3312 +3313 +3314 +3315 +3316 +3317 +3318 +3319 +3320 +3321 +3322 +3323 +3324 +3325 +3326 +3327 +3328 +3329 +3330 +3331 +3332 +3333 +3334 +3335 +3336 +3337 +3338 +3339 +3340 +3341 +3342 +3343 +3344 +3345 +3346 +3347 +3348 +3349 +3350 +3351 +3352 +3353 +3354 +3355 +3356 +3357 +3358 +3359 +3360 +3361 +3362 +3363 +3364 +3365 +3366 +3367 +3368 +3369 +3370 +3371 +3372 +3373 +3374 +3375 +3376 +3377 +3378 +3379 +3380 +3381 +3382 +3383 +3384 +3385 +3386 +3387 +3388 +3389 +3390 +3391 +3392 +3393 +3394 +3395 +3396 +3397 +3398 +3399 +3400 +3401 +3402 +3403 +3404 +3405 +3406 +3407 +3408 +3409 +3410 +3411 +3412 +3413 +3414 +3415 +3416 +3417 +3418 +3419 +3420 +3421 +3422 +3423 +3424 +3425 +3426 +3427 +3428 +3429 +3430 +3431 +3432 +3433 +3434 +3435 +3436 +3437 +3438 +3439 +3440 +3441 +3442 +3443 +3444 +3445 +3446 +3447 +3448 +3449 +3450 +3451 +3452 +3453 +3454 +3455 +3456 +3457 +3458 +3459 +3460 +3461 +3462 +3463 +3464 +3465 +3466 +3467 +3468 +3469 +3470 +3471 +3472 +3473 +3474 +3475 +3476 +3477 +3478 +3479 +3480 +3481 +3482 +3483 +3484 +3485 +3486 +3487 +3488 +3489 +3490 +3491 +3492 +3493 +3494 +3495 +3496 +3497 +3498 +3499 +3500 +3501 +3502 +3503 +3504 +3505 +3506 +3507 +3508 +3509 +3510 +3511 +3512 +3513 +3514 +3515 +3516 +3517 +3518 +3519 +3520 +3521 +3522 +3523 +3524 +3525 +3526 +3527 +3528 +3529 +3530 +3531 +3532 +3533 +3534 +3535 +3536 +3537 +3538 +3539 +3540 +3541 +3542 +3543 +3544 +3545 +3546 +3547 +3548 +3549 +3550 +3551 +3552 +3553 +3554 +3555 +3556 +3557 +3558 +3559 +3560 +3561 +3562 +3563 +3564 +3565 +3566 +3567 +3568 +3569 +3570 +3571 +3572 +3573 +3574 +3575 +3576 +3577 +3578 +3579 +3580 +3581 +3582 +3583 +3584 +3585 +3586 +3587 +3588 +3589 +3590 +3591 +3592 +3593 +3594 +3595 +3596 +3597 +3598 +3599 +3600 +3601 +3602 +3603 +3604 +3605 +3606 +3607 +3608 +3609 +3610 +3611 +3612 +3613 +3614 +3615 +3616 +3617 +3618 +3619 +3620 +3621 +3622 +3623 +3624 +3625 +3626 +3627 +3628 +3629 +3630 +3631 +3632 +3633 +3634 +3635 +3636 +3637 +3638 +3639 +3640 +3641 +3642 +3643 +3644 +3645 +3646 +3647 +3648 +3649 +3650 +3651 +3652 +3653 +3654 +3655 +3656 +3657 +3658 +3659 +3660 +3661 +3662 +3663 +3664 +3665 +3666 +3667 +3668 +3669 +3670 +3671 +3672 +3673 +3674 +3675 +3676 +3677 +3678 +3679 +3680 +3681 +3682 +3683 +3684 +3685 +3686 +3687 +3688 +3689 +3690 +3691 +3692 +3693 +3694 +3695 +3696 +3697 +3698 +3699 +3700 +3701 +3702 +3703 +3704 +3705 +3706 +3707 +3708 +3709 +3710 +3711 +3712 +3713 +3714 +3715 +3716 +3717 +3718 +3719 +3720 +3721 +3722 +3723 +3724 +3725 +3726 +3727 +3728 +3729 +3730 +3731 +3732 +3733 +3734 +3735 +3736 +3737 +3738 +3739 +3740 +3741 +3742 +3743 +3744 +3745 +3746 +3747 +3748 +3749 +3750 +3751 +3752 +3753 +3754 +3755 +3756 +3757 +3758 +3759 +3760 +3761 +3762 +3763 +3764 +3765 +3766 +3767 +3768 +3769 +3770 +3771 +3772 +3773 +3774 +3775 +3776 +3777 +3778 +3779 +3780 +3781 +3782 +3783 +3784 +3785 +3786 +3787 +3788 +3789 +3790 +3791 +3792 +3793 +3794 +3795 +3796 +3797 +3798 +3799 +3800 +3801 +3802 +3803 +3804 +3805 +3806 +3807 +3808 +3809 +3810 +3811 +3812 +3813 +3814 +3815 +3816 +3817 +3818 +3819 +3820 +3821 +3822 +3823 +3824 +3825 +3826 +3827 +3828 +3829 +3830 +3831 +3832 +3833 +3834 +3835 +3836 +3837 +3838 +3839 +3840 +3841 +3842 +3843 +3844 +3845 +3846 +3847 +3848 +3849 +3850 +3851 +3852 +3853 +3854 +3855 +3856 +3857 +3858 +3859 +3860 +3861 +3862 +3863 +3864 +3865 +3866 +3867 +3868 +3869 +3870 +3871 +3872 +3873 +3874 +3875 +3876 +3877 +3878 +3879 +3880 +3881 +3882 +3883 +3884 +3885 +3886 +3887 +3888 +3889 +3890 +3891 +3892 +3893 +3894 +3895 +3896 +3897 +3898 +3899 +3900 +3901 +3902 +3903 +3904 +3905 +3906 +3907 +3908 +3909 +3910 +3911 +3912 +3913 +3914 +3915 +3916 +3917 +3918 +3919 +3920 +3921 +3922 +3923 +3924 +3925 +3926 +3927 +3928 +3929 +3930 +3931 +3932 +3933 +3934 +3935 +3936 +3937 +3938 +3939 +3940 +3941 +3942 +3943 +3944 +3945 +3946 +3947 +3948 +3949 +3950 +3951 +3952 +3953 +3954 +3955 +3956 +3957 +3958 +3959 +3960 +3961 +3962 +3963 +3964 +3965 +3966 +3967 +3968 +3969 +3970 +3971 +3972 +3973 +3974 +3975 +3976 +3977 +3978 +3979 +3980 +3981 +3982 +3983 +3984 +3985 +3986 +3987 +3988 +3989 +3990 +3991 +3992 +3993 +3994 +3995 +3996 +3997 +3998 +3999 +4000 +4001 +4002 +4003 +4004 +4005 +4006 +4007 +4008 +4009 +4010 +4011 +4012 +4013 +4014 +4015 +4016 +4017 +4018 +4019 +4020 +4021 +4022 +4023 +4024 +4025 +4026 +4027 +4028 +4029 +4030 +4031 +4032 +4033 +4034 +4035 +4036 +4037 +4038 +4039 +4040 +4041 +4042 +4043 +4044 +4045 +4046 +4047 +4048 +4049 +4050 +4051 +4052 +4053 +4054 +4055 +4056 +4057 +4058 +4059 +4060 +4061 +4062 +4063 +4064 +4065 +4066 +4067 +4068 +4069 +4070 +4071 +4072 +4073 +4074 +4075 +4076 +4077 +4078 +4079 +4080 +4081 +4082 +4083 +4084 +4085 +4086 +4087 +4088 +4089 +4090 +4091 +4092 +4093 +4094 +4095 +4096 +4097 +4098 +4099 +4100 +4101 +4102 +4103 +4104 +4105 +4106 +4107 +4108 +4109 +4110 +4111 +4112 +4113 +4114 +4115 +4116 +4117 +4118 +4119 +4120 +4121 +4122 +4123 +4124 +4125 +4126 +4127 +4128 +4129 +4130 +4131 +4132 +4133 +4134 +4135 +4136 +4137 +4138 +4139 +4140 +4141 +4142 +4143 +4144 +4145 +4146 +4147 +4148 +4149 +4150 +4151 +4152 +4153 +4154 +4155 +4156 +4157 +4158 +4159 +4160 +4161 +4162 +4163 +4164 +4165 +4166 +4167 +4168 +4169 +4170 +4171 +4172 +4173 +4174 +4175 +4176 +4177 +4178 +4179 +4180 +4181 +4182 +4183 +4184 +4185 +4186 +4187 +4188 +4189 +4190 +4191 +4192 +4193 +4194 +4195 +4196 +4197 +4198 +4199 +4200 +4201 +4202 +4203 +4204 +4205 +4206 +4207 +4208 +4209 +4210 +4211 +4212 +4213 +4214 +4215 +4216 +4217 +4218 +4219 +4220 +4221 +4222 +4223 +4224 +4225 +4226 +4227 +4228 +4229 +4230 +4231 +4232 +4233 +4234 +4235 +4236 +4237 +4238 +4239 +4240 +4241 +4242 +4243 +4244 +4245 +4246 +4247 +4248 +4249 +4250 +4251 +4252 +4253 +4254 +4255 +4256 +4257 +4258 +4259 +4260 +4261 +4262 +4263 +4264 +4265 +4266 +4267 +4268 +4269 +4270 +4271 +4272 +4273 +4274 +4275 +4276 +4277 +4278 +4279 +4280 +4281 +4282 +4283 +4284 +4285 +4286 +4287 +4288 +4289 +4290 +4291 +4292 +4293 +4294 +4295 +4296 +4297 +4298 +4299 +4300 +4301 +4302 +4303 +4304 +4305 +4306 +4307 +4308 +4309 +4310 +4311 +4312 +4313 +4314 +4315 +4316 +4317 +4318 +4319 +4320 +4321 +4322 +4323 +4324 +4325 +4326 +4327 +4328 +4329 +4330 +4331 +4332 +4333 +4334 +4335 +4336 +4337 +4338 +4339 +4340 +4341 +4342 +4343 +4344 +4345 +4346 +4347 +4348 +4349 +4350 +4351 +4352 +4353 +4354 +4355 +4356 +4357 +4358 +4359 +4360 +4361 +4362 +4363 +4364 +4365 +4366 +4367 +4368 +4369 +4370 +4371 +4372 +4373 +4374 +4375 +4376 +4377 +4378 +4379 +4380 +4381 +4382 +4383 +4384 +4385 +4386 +4387 +4388 +4389 +4390 +4391 +4392 +4393 +4394 +4395 +4396 +4397 +4398 +4399 +4400 +4401 +4402 +4403 +4404 +4405 +4406 +4407 +4408 +4409 +4410 +4411 +4412 +4413 +4414 +4415 +4416 +4417 +4418 +4419 +4420 +4421 +4422 +4423 +4424 +4425 +4426 +4427 +4428 +4429 +4430 +4431 +4432 +4433 +4434 +4435 +4436 +4437 +4438 +4439 +4440 +4441 +4442 +4443 +4444 +4445 +4446 +4447 +4448 +4449 +4450 +4451 +4452 +4453 +4454 +4455 +4456 +4457 +4458 +4459 +4460 +4461 +4462 +4463 +4464 +4465 +4466 +4467 +4468 +4469 +4470 +4471 +4472 +4473 +4474 +4475 +4476 +4477 +4478 +4479 +4480 +4481 +4482 +4483 +4484 +4485 +4486 +4487 +4488 +4489 +4490 +4491 +4492 +4493 +4494 +4495 +4496 +4497 +4498 +4499 +4500 +4501 +4502 +4503 +4504 +4505 +4506 +4507 +4508 +4509 +4510 +4511 +4512 +4513 +4514 +4515 +4516 +4517 +4518 +4519 +4520 +4521 +4522 +4523 +4524 +4525 +4526 +4527 +4528 +4529 +4530 +4531 +4532 +4533 +4534 +4535 +4536 +4537 +4538 +4539 +4540 +4541 +4542 +4543 +4544 +4545 +4546 +4547 +4548 +4549 +4550 +4551 +4552 +4553 +4554 +4555 +4556 +4557 +4558 +4559 +4560 +4561 +4562 +4563 +4564 +4565 +4566 +4567 +4568 +4569 +4570 +4571 +4572 +4573 +4574 +4575 +4576 +4577 +4578 +4579 +4580 +4581 +4582 +4583 +4584 +4585 +4586 +4587 +4588 +4589 +4590 +4591 +4592 +4593 +4594 +4595 +4596 +4597 +4598 +4599 +4600 +4601 +4602 +4603 +4604 +4605 +4606 +4607 +4608 +4609 +4610 +4611 +4612 +4613 +4614 +4615 +4616 +4617 +4618 +4619 +4620 +4621 +4622 +4623 +4624 +4625 +4626 +4627 +4628 +4629 +4630 +4631 +4632 +4633 +4634 +4635 +4636 +4637 +4638 +4639 +4640 +4641 +4642 +4643 +4644 +4645 +4646 +4647 +4648 +4649 +4650 +4651 +4652 +4653 +4654 +4655 +4656 +4657 +4658 +4659 +4660 +4661 +4662 +4663 +4664 +4665 +4666 +4667 +4668 +4669 +4670 +4671 +4672 +4673 +4674 +4675 +4676 +4677 +4678 +4679 +4680 +4681 +4682 +4683 +4684 +4685 +4686 +4687 +4688 +4689 +4690 +4691 +4692 +4693 +4694 +4695 +4696 +4697 +4698 +4699 +4700 +4701 +4702 +4703 +4704 +4705 +4706 +4707 +4708 +4709 +4710 +4711 +4712 +4713 +4714 +4715 +4716 +4717 +4718 +4719 +4720 +4721 +4722 +4723 +4724 +4725 +4726 +4727 +4728 +4729 +4730 +4731 +4732 +4733 +4734 +4735 +4736 +4737 +4738 +4739 +4740 +4741 +4742 +4743 +4744 +4745 +4746 +4747 +4748 +4749 +4750 +4751 +4752 +4753 +4754 +4755 +4756 +4757 +4758 +4759 +4760 +4761 +4762 +4763 +4764 +4765 +4766 +4767 +4768 +4769 +4770 +4771 +4772 +4773 +4774 +4775 +4776 +4777 +4778 +4779 +4780 +4781 +4782 +4783 +4784 +4785 +4786 +4787 +4788 +4789 +4790 +4791 +4792 +4793 +4794 +4795 +4796 +4797 +4798 +4799 +4800 +4801 +4802 +4803 +4804 +4805 +4806 +4807 +4808 +4809 +4810 +4811 +4812 +4813 +4814 +4815 +4816 +4817 +4818 +4819 +4820 +4821 +4822 +4823 +4824 +4825 +4826 +4827 +4828 +4829 +4830 +4831 +4832 +4833 +4834 +4835 +4836 +4837 +4838 +4839 +4840 +4841 +4842 +4843 +4844 +4845 +4846 +4847 +4848 +4849 +4850 +4851 +4852 +4853 +4854 +4855 +4856 +4857 +4858 +4859 +4860 +4861 +4862 +4863 +4864 +4865 +4866 +4867 +4868 +4869 +4870 +4871 +4872 +4873 +4874 +4875 +4876 +4877 +4878 +4879 +4880 +4881 +4882 +4883 +4884 +4885 +4886 +4887 +4888 +4889 +4890 +4891 +4892 +4893 +4894 +4895 +4896 +4897 +4898 +4899 +4900 +4901 +4902 +4903 +4904 +4905 +4906 +4907 +4908 +4909 +4910 +4911 +4912 +4913 +4914 +4915 +4916 +4917 +4918 +4919 +4920 +4921 +4922 +4923 +4924 +4925 +4926 +4927 +4928 +4929 +4930 +4931 +4932 +4933 +4934 +4935 +4936 +4937 +4938 +4939 +4940 +4941 +4942 +4943 +4944 +4945 +4946 +4947 +4948 +4949 +4950 +4951 +4952 +4953 +4954 +4955 +4956 +4957 +4958 +4959 +4960 +4961 +4962 +4963 +4964 +4965 +4966 +4967 +4968 +4969 +4970 +4971 +4972 +4973 +4974 +4975 +4976 +4977 +4978 +4979 +4980 +4981 +4982 +4983 +4984 +4985 +4986 +4987 +4988 +4989 +4990 +4991 +4992 +4993 +4994 +4995 +4996 +4997 +4998 +4999 +5000 +5001 +5002 +5003 +5004 +5005 +5006 +5007 +5008 +5009 +5010 +5011 +5012 +5013 +5014 +5015 +5016 +5017 +5018 +5019 +5020 +5021 +5022 +5023 +5024 +5025 +5026 +5027 +5028 +5029 +5030 +5031 +5032 +5033 +5034 +5035 +5036 +5037 +5038 +5039 +5040 +5041 +5042 +5043 +5044 +5045 +5046 +5047 +5048 +5049 +5050 +5051 +5052 +5053 +5054 +5055 +5056 +5057 +5058 +5059 +5060 +5061 +5062 +5063 +5064 +5065 +5066 +5067 +5068 +5069 +5070 +5071 +5072 +5073 +5074 +5075 +5076 +5077 +5078 +5079 +5080 +5081 +5082 +5083 +5084 +5085 +5086 +5087 +5088 +5089 +5090 +5091 +5092 +5093 +5094 +5095 +5096 +5097 +5098 +5099 +5100 +5101 +5102 +5103 +5104 +5105 +5106 +5107 +5108 +5109 +5110 +5111 +5112 +5113 +5114 +5115 +5116 +5117 +5118 +5119 +5120 +5121 +5122 +5123 +5124 +5125 +5126 +5127 +5128 +5129 +5130 +5131 +5132 +5133 +5134 +5135 +5136 +5137 +5138 +5139 +5140 +5141 +5142 +5143 +5144 +5145 +5146 +5147 +5148 +5149 +5150 +5151 +5152 +5153 +5154 +5155 +5156 +5157 +5158 +5159 +5160 +5161 +5162 +5163 +5164 +5165 +5166 +5167 +5168 +5169 +5170 +5171 +5172 +5173 +5174 +5175 +5176 +5177 +5178 +5179 +5180 +5181 +5182 +5183 +5184 +5185 +5186 +5187 +5188 +5189 +5190 +5191 +5192 +5193 +5194 +5195 +5196 +5197 +5198 +5199 +5200 +5201 +5202 +5203 +5204 +5205 +5206 +5207 +5208 +5209 +5210 +5211 +5212 +5213 +5214 +5215 +5216 +5217 +5218 +5219 +5220 +5221 +5222 +5223 +5224 +5225 +5226 +5227 +5228 +5229 +5230 +5231 +5232 +5233 +5234 +5235 +5236 +5237 +5238 +5239 +5240 +5241 +5242 +5243 +5244 +5245 +5246 +5247 +5248 +5249 +5250 +5251 +5252 +5253 +5254 +5255 +5256 +5257 +5258 +5259 +5260 +5261 +5262 +5263 +5264 +5265 +5266 +5267 +5268 +5269 +5270 +5271 +5272 +5273 +5274 +5275 +5276 +5277 +5278 +5279 +5280 +5281 +5282 +5283 +5284 +5285 +5286 +5287 +5288 +5289 +5290 +5291 +5292 +5293 +5294 +5295 +5296 +5297 +5298 +5299 +5300 +5301 +5302 +5303 +5304 +5305 +5306 +5307 +5308 +5309 +5310 +5311 +5312 +5313 +5314 +5315 +5316 +5317 +5318 +5319 +5320 +5321 +5322 +5323 +5324 +5325 +5326 +5327 +5328 +5329 +5330 +5331 +5332 +5333 +5334 +5335 +5336 +5337 +5338 +5339 +5340 +5341 +5342 +5343 +5344 +5345 +5346 +5347 +5348 +5349 +5350 +5351 +5352 +5353 +5354 +5355 +5356 +5357 +5358 +5359 +5360 +5361 +5362 +5363 +5364 +5365 +5366 +5367 +5368 +5369 +5370 +5371 +5372 +5373 +5374 +5375 +5376 +5377 +5378 +5379 +5380 +5381 +5382 +5383 +5384 +5385 +5386 +5387 +5388 +5389 +5390 +5391 +5392 +5393 +5394 +5395 +5396 +5397 +5398 +5399 +5400 +5401 +5402 +5403 +5404 +5405 +5406 +5407 +5408 +5409 +5410 +5411 +5412 +5413 +5414 +5415 +5416 +5417 +5418 +5419 +5420 +5421 +5422 +5423 +5424 +5425 +5426 +5427 +5428 +5429 +5430 +5431 +5432 +5433 +5434 +5435 +5436 +5437 +5438 +5439 +5440 +5441 +5442 +5443 +5444 +5445 +5446 +5447 +5448 +5449 +5450 +5451 +5452 +5453 +5454 +5455 +5456 +5457 +5458 +5459 +5460 +5461 +5462 +5463 +5464 +5465 +5466 +5467 +5468 +5469 +5470 +5471 +5472 +5473 +5474 +5475 +5476 +5477 +5478 +5479 +5480 +5481 +5482 +5483 +5484 +5485 +5486 +5487 +5488 +5489 +5490 +5491 +5492 +5493 +5494 +5495 +5496 +5497 +5498 +5499 +5500 +5501 +5502 +5503 +5504 +5505 +5506 +5507 +5508 +5509 +5510 +5511 +5512 +5513 +5514 +5515 +5516 +5517 +5518 +5519 +5520 +5521 +5522 +5523 +5524 +5525 +5526 +5527 +5528 +5529 +5530 +5531 +5532 +5533 +5534 +5535 +5536 +5537 +5538 +5539 +5540 +5541 +5542 +5543 +5544 +5545 +5546 +5547 +5548 +5549 +5550 +5551 +5552 +5553 +5554 +5555 +5556 +5557 +5558 +5559 +5560 +5561 +5562 +5563 +5564 +5565 +5566 +5567 +5568 +5569 +5570 +5571 +5572 +5573 +5574 +5575 +5576 +5577 +5578 +5579 +5580 +5581 +5582 +5583 +5584 +5585 +5586 +5587 +5588 +5589 +5590 +5591 +5592 +5593 +5594 +5595 +5596 +5597 +5598 +5599 +5600 +5601 +5602 +5603 +5604 +5605 +5606 +5607 +5608 +5609 +5610 +5611 +5612 +5613 +5614 +5615 +5616 +5617 +5618 +5619 +5620 +5621 +5622 +5623 +5624 +5625 +5626 +5627 +5628 +5629 +5630 +5631 +5632 +5633 +5634 +5635 +5636 +5637 +5638 +5639 +5640 +5641 +5642 +5643 +5644 +5645 +5646 +5647 +5648 +5649 +5650 +5651 +5652 +5653 +5654 +5655 +5656 +5657 +5658 +5659 +5660 +5661 +5662 +5663 +5664 +5665 +5666 +5667 +5668 +5669 +5670 +5671 +5672 +5673 +5674 +5675 +5676 +5677 +5678 +5679 +5680 +5681 +5682 +5683 +5684 +5685 +5686 +5687 +5688 +5689 +5690 +5691 +5692 +5693 +5694 +5695 +5696 +5697 +5698 +5699 +5700 +5701 +5702 +5703 +5704 +5705 +5706 +5707 +5708 +5709 +5710 +5711 +5712 +5713 +5714 +5715 +5716 +5717 +5718 +5719 +5720 +5721 +5722 +5723 +5724 +5725 +5726 +5727 +5728 +5729 +5730 +5731 +5732 +5733 +5734 +5735 +5736 +5737 +5738 +5739 +5740 +5741 +5742 +5743 +5744 +5745 +5746 +5747 +5748 +5749 +5750 +5751 +5752 +5753 +5754 +5755 +5756 +5757 +5758 +5759 +5760 +5761 +5762 +5763 +5764 +5765 +5766 +5767 +5768 +5769 +5770 +5771 +5772 +5773 +5774 +5775 +5776 +5777 +5778 +5779 +5780 +5781 +5782 +5783 +5784 +5785 +5786 +5787 +5788 +5789 +5790 +5791 +5792 +5793 +5794 +5795 +5796 +5797 +5798 +5799 +5800 +5801 +5802 +5803 +5804 +5805 +5806 +5807 +5808 +5809 +5810 +5811 +5812 +5813 +5814 +5815 +5816 +5817 +5818 +5819 +5820 +5821 +5822 +5823 +5824 +5825 +5826 +5827 +5828 +5829 +5830 +5831 +5832 +5833 +5834 +5835 +5836 +5837 +5838 +5839 +5840 +5841 +5842 +5843 +5844 +5845 +5846 +5847 +5848 +5849 +5850 +5851 +5852 +5853 +5854 +5855 +5856 +5857 +5858 +5859 +5860 +5861 +5862 +5863 +5864 +5865 +5866 +5867 +5868 +5869 +5870 +5871 +5872 +5873 +5874 +5875 +5876 +5877 +5878 +5879 +5880 +5881 +5882 +5883 +5884 +5885 +5886 +5887 +5888 +5889 +5890 +5891 +5892 +5893 +5894 +5895 +5896 +5897 +5898 +5899 +5900 +5901 +5902 +5903 +5904 +5905 +5906 +5907 +5908 +5909 +5910 +5911 +5912 +5913 +5914 +5915 +5916 +5917 +5918 +5919 +5920 +5921 +5922 +5923 +5924 +5925 +5926 +5927 +5928 +5929 +5930 +5931 +5932 +5933 +5934 +5935 +5936 +5937 +5938 +5939 +5940 +5941 +5942 +5943 +5944 +5945 +5946 +5947 +5948 +5949 +5950 +5951 +5952 +5953 +5954 +5955 +5956 +5957 +5958 +5959 +5960 +5961 +5962 +5963 +5964 +5965 +5966 +5967 +5968 +5969 +5970 +5971 +5972 +5973 +5974 +5975 +5976 +5977 +5978 +5979 +5980 +5981 +5982 +5983 +5984 +5985 +5986 +5987 +5988 +5989 +5990 +5991 +5992 +5993 +5994 +5995 +5996 +5997 +5998 +5999 +6000 +6001 +6002 +6003 +6004 +6005 +6006 +6007 +6008 +6009 +6010 +6011 +6012 +6013 +6014 +6015 +6016 +6017 +6018 +6019 +6020 +6021 +6022 +6023 +6024 +6025 +6026 +6027 +6028 +6029 +6030 +6031 +6032 +6033 +6034 +6035 +6036 +6037 +6038 +6039 +6040 +6041 +6042 +6043 +6044 +6045 +6046 +6047 +6048 +6049 +6050 +6051 +6052 +6053 +6054 +6055 +6056 +6057 +6058 +6059 +6060 +6061 +6062 +6063 +6064 +6065 +6066 +6067 +6068 +6069 +6070 +6071 +6072 +6073 +6074 +6075 +6076 +6077 +6078 +6079 +6080 +6081 +6082 +6083 +6084 +6085 +6086 +6087 +6088 +6089 +6090 +6091 +6092 +6093 +6094 +6095 +6096 +6097 +6098 +6099 +6100 +6101 +6102 +6103 +6104 +6105 +6106 +6107 +6108 +6109 +6110 +6111 +6112 +6113 +6114 +6115 +6116 +6117 +6118 +6119 +6120 +6121 +6122 +6123 +6124 +6125 +6126 +6127 +6128 +6129 +6130 +6131 +6132 +6133 +6134 +6135 +6136 +6137 +6138 +6139 +6140 +6141 +6142 +6143 +6144 +6145 +6146 +6147 +6148 +6149 +6150 +6151 +6152 +6153 +6154 +6155 +6156 +6157 +6158 +6159 +6160 +6161 +6162 +6163 +6164 +6165 +6166 +6167 +6168 +6169 +6170 +6171 +6172 +6173 +6174 +6175 +6176 +6177 +6178 +6179 +6180 +6181 +6182 +6183 +6184 +6185 +6186 +6187 +6188 +6189 +6190 +6191 +6192 +6193 +6194 +6195 +6196 +6197 +6198 +6199 +6200 +6201 +6202 +6203 +6204 +6205 +6206 +6207 +6208 +6209 +6210 +6211 +6212 +6213 +6214 +6215 +6216 +6217 +6218 +6219 +6220 +6221 +6222 +6223 +6224 +6225 +6226 +6227 +6228 +6229 +6230 +6231 +6232 +6233 +6234 +6235 +6236 +6237 +6238 +6239 +6240 +6241 +6242 +6243 +6244 +6245 +6246 +6247 +6248 +6249 +6250 +6251 +6252 +6253 +6254 +6255 +6256 +6257 +6258 +6259 +6260 +6261 +6262 +6263 +6264 +6265 +6266 +6267 +6268 +6269 +6270 +6271 +6272 +6273 +6274 +6275 +6276 +6277 +6278 +6279 +6280 +6281 +6282 +6283 +6284 +6285 +6286 +6287 +6288 +6289 +6290 +6291 +6292 +6293 +6294 +6295 +6296 +6297 +6298 +6299 +6300 +6301 +6302 +6303 +6304 +6305 +6306 +6307 +6308 +6309 +6310 +6311 +6312 +6313 +6314 +6315 +6316 +6317 +6318 +6319 +6320 +6321 +6322 +6323 +6324 +6325 +6326 +6327 +6328 +6329 +6330 +6331 +6332 +6333 +6334 +6335 +6336 +6337 +6338 +6339 +6340 +6341 +6342 +6343 +6344 +6345 +6346 +6347 +6348 +6349 +6350 +6351 +6352 +6353 +6354 +6355 +6356 +6357 +6358 +6359 +6360 +6361 +6362 +6363 +6364 +6365 +6366 +6367 +6368 +6369 +6370 +6371 +6372 +6373 +6374 +6375 +6376 +6377 +6378 +6379 +6380 +6381 +6382 +6383 +6384 +6385 +6386 +6387 +6388 +6389 +6390 +6391 +6392 +6393 +6394 +6395 +6396 +6397 +6398 +6399 +6400 +6401 +6402 +6403 +6404 +6405 +6406 +6407 +6408 +6409 +6410 +6411 +6412 +6413 +6414 +6415 +6416 +6417 +6418 +6419 +6420 +6421 +6422 +6423 +6424 +6425 +6426 +6427 +6428 +6429 +6430 +6431 +6432 +6433 +6434 +6435 +6436 +6437 +6438 +6439 +6440 +6441 +6442 +6443 +6444 +6445 +6446 +6447 +6448 +6449 +6450 +6451 +6452 +6453 +6454 +6455 +6456 +6457 +6458 +6459 +6460 +6461 +6462 +6463 +6464 +6465 +6466 +6467 +6468 +6469 +6470 +6471 +6472 +6473 +6474 +6475 +6476 +6477 +6478 +6479 +6480 +6481 +6482 +6483 +6484 +6485 +6486 +6487 +6488 +6489 +6490 +6491 +6492 +6493 +6494 +6495 +6496 +6497 +6498 +6499 +6500 +6501 +6502 +6503 +6504 +6505 +6506 +6507 +6508 +6509 +6510 +6511 +6512 +6513 +6514 +6515 +6516 +6517 +6518 +6519 +6520 +6521 +6522 +6523 +6524 +6525 +6526 +6527 +6528 +6529 +6530 +6531 +6532 +6533 +6534 +6535 +6536 +6537 +6538 +6539 +6540 +6541 +6542 +6543 +6544 +6545 +6546 +6547 +6548 +6549 +6550 +6551 +6552 +6553 +6554 +6555 +6556 +6557 +6558 +6559 +6560 +6561 +6562 +6563 +6564 +6565 +6566 +6567 +6568 +6569 +6570 +6571 +6572 +6573 +6574 +6575 +6576 +6577 +6578 +6579 +6580 +6581 +6582 +6583 +6584 +6585 +6586 +6587 +6588 +6589 +6590 +6591 +6592 +6593 +6594 +6595 +6596 +6597 +6598 +6599 +6600 +6601 +6602 +6603 +6604 +6605 +6606 +6607 +6608 +6609 +6610 +6611 +6612 +6613 +6614 +6615 +6616 +6617 +6618 +6619 +6620 +6621 +6622 +6623 +6624 +6625 +6626 +6627 +6628 +6629 +6630 +6631 +6632 +6633 +6634 +6635 +6636 +6637 +6638 +6639 +6640 +6641 +6642 +6643 +6644 +6645 +6646 +6647 +6648 +6649 +6650 +6651 +6652 +6653 +6654 +6655 +6656 +6657 +6658 +6659 +6660 +6661 +6662 +6663 +6664 +6665 +6666 +6667 +6668 +6669 +6670 +6671 +6672 +6673 +6674 +6675 +6676 +6677 +6678 +6679 +6680 +6681 +6682 +6683 +6684 +6685 +6686 +6687 +6688 +6689 +6690 +6691 +6692 +6693 +6694 +6695 +6696 +6697 +6698 +6699 +6700 +6701 +6702 +6703 +6704 +6705 +6706 +6707 +6708 +6709 +6710 +6711 +6712 +6713 +6714 +6715 +6716 +6717 +6718 +6719 +6720 +6721 +6722 +6723 +6724 +6725 +6726 +6727 +6728 +6729 +6730 +6731 +6732 +6733 +6734 +6735 +6736 +6737 +6738 +6739 +6740 +6741 +6742 +6743 +6744 +6745 +6746 +6747 +6748 +6749 +6750 +6751 +6752 +6753 +6754 +6755 +6756 +6757 +6758 +6759 +6760 +6761 +6762 +6763 +6764 +6765 +6766 +6767 +6768 +6769 +6770 +6771 +6772 +6773 +6774 +6775 +6776 +6777 +6778 +6779 +6780 +6781 +6782 +6783 +6784 +6785 +6786 +6787 +6788 +6789 +6790 +6791 +6792 +6793 +6794 +6795 +6796 +6797 +6798 +6799 +6800 +6801 +6802 +6803 +6804 +6805 +6806 +6807 +6808 +6809 +6810 +6811 +6812 +6813 +6814 +6815 +6816 +6817 +6818 +6819 +6820 +6821 +6822 +6823 +6824 +6825 +6826 +6827 +6828 +6829 +6830 +6831 +6832 +6833 +6834 +6835 +6836 +6837 +6838 +6839 +6840 +6841 +6842 +6843 +6844 +6845 +6846 +6847 +6848 +6849 +6850 +6851 +6852 +6853 +6854 +6855 +6856 +6857 +6858 +6859 +6860 +6861 +6862 +6863 +6864 +6865 +6866 +6867 +6868 +6869 +6870 +6871 +6872 +6873 +6874 +6875 +6876 +6877 +6878 +6879 +6880 +6881 +6882 +6883 +6884 +6885 +6886 +6887 +6888 +6889 +6890 +6891 +6892 +6893 +6894 +6895 +6896 +6897 +6898 +6899 +6900 +6901 +6902 +6903 +6904 +6905 +6906 +6907 +6908 +6909 +6910 +6911 +6912 +6913 +6914 +6915 +6916 +6917 +6918 +6919 +6920 +6921 +6922 +6923 +6924 +6925 +6926 +6927 +6928 +6929 +6930 +6931 +6932 +6933 +6934 +6935 +6936 +6937 +6938 +6939 +6940 +6941 +6942 +6943 +6944 +6945 +6946 +6947 +6948 +6949 +6950 +6951 +6952 +6953 +6954 +6955 +6956 +6957 +6958 +6959 +6960 +6961 +6962 +6963 +6964 +6965 +6966 +6967 +6968 +6969 +6970 +6971 +6972 +6973 +6974 +6975 +6976 +6977 +6978 +6979 +6980 +6981 +6982 +6983 +6984 +6985 +6986 +6987 +6988 +6989 +6990 +6991 +6992 +6993 +6994 +6995 +6996 +6997 +6998 +6999 +7000 +7001 +7002 +7003 +7004 +7005 +7006 +7007 +7008 +7009 +7010 +7011 +7012 +7013 +7014 +7015 +7016 +7017 +7018 +7019 +7020 +7021 +7022 +7023 +7024 +7025 +7026 +7027 +7028 +7029 +7030 +7031 +7032 +7033 +7034 +7035 +7036 +7037 +7038 +7039 +7040 +7041 +7042 +7043 +7044 +7045 +7046 +7047 +7048 +7049 +7050 +7051 +7052 +7053 +7054 +7055 +7056 +7057 +7058 +7059 +7060 +7061 +7062 +7063 +7064 +7065 +7066 +7067 +7068 +7069 +7070 +7071 +7072 +7073 +7074 +7075 +7076 +7077 +7078 +7079 +7080 +7081 +7082 +7083 +7084 +7085 +7086 +7087 +7088 +7089 +7090 +7091 +7092 +7093 +7094 +7095 +7096 +7097 +7098 +7099 +7100 +7101 +7102 +7103 +7104 +7105 +7106 +7107 +7108 +7109 +7110 +7111 +7112 +7113 +7114 +7115 +7116 +7117 +7118 +7119 +7120 +7121 +7122 +7123 +7124 +7125 +7126 +7127 +7128 +7129 +7130 +7131 +7132 +7133 +7134 +7135 +7136 +7137 +7138 +7139 +7140 +7141 +7142 +7143 +7144 +7145 +7146 +7147 +7148 +7149 +7150 +7151 +7152 +7153 +7154 +7155 +7156 +7157 +7158 +7159 +7160 +7161 +7162 +7163 +7164 +7165 +7166 +7167 +7168 +7169 +7170 +7171 +7172 +7173 +7174 +7175 +7176 +7177 +7178 +7179 +7180 +7181 +7182 +7183 +7184 +7185 +7186 +7187 +7188 +7189 +7190 +7191 +7192 +7193 +7194 +7195 +7196 +7197 +7198 +7199 +7200 +7201 +7202 +7203 +7204 +7205 +7206 +7207 +7208 +7209 +7210 +7211 +7212 +7213 +7214 +7215 +7216 +7217 +7218 +7219 +7220 +7221 +7222 +7223 +7224 +7225 +7226 +7227 +7228 +7229 +7230 +7231 +7232 +7233 +7234 +7235 +7236 +7237 +7238 +7239 +7240 +7241 +7242 +7243 +7244 +7245 +7246 +7247 +7248 +7249 +7250 +7251 +7252 +7253 +7254 +7255 +7256 +7257 +7258 +7259 +7260 +7261 +7262 +7263 +7264 +7265 +7266 +7267 +7268 +7269 +7270 +7271 +7272 +7273 +7274 +7275 +7276 +7277 +7278 +7279 +7280 +7281 +7282 +7283 +7284 +7285 +7286 +7287 +7288 +7289 +7290 +7291 +7292 +7293 +7294 +7295 +7296 +7297 +7298 +7299 +7300 +7301 +7302 +7303 +7304 +7305 +7306 +7307 +7308 +7309 +7310 +7311 +7312 +7313 +7314 +7315 +7316 +7317 +7318 +7319 +7320 +7321 +7322 +7323 +7324 +7325 +7326 +7327 +7328 +7329 +7330 +7331 +7332 +7333 +7334 +7335 +7336 +7337 +7338 +7339 +7340 +7341 +7342 +7343 +7344 +7345 +7346 +7347 +7348 +7349 +7350 +7351 +7352 +7353 +7354 +7355 +7356 +7357 +7358 +7359 +7360 +7361 +7362 +7363 +7364 +7365 +7366 +7367 +7368 +7369 +7370 +7371 +7372 +7373 +7374 +7375 +7376 +7377 +7378 +7379 +7380 +7381 +7382 +7383 +7384 +7385 +7386 +7387 +7388 +7389 +7390 +7391 +7392 +7393 +7394 +7395 +7396 +7397 +7398 +7399 +7400 +7401 +7402 +7403 +7404 +7405 +7406 +7407 +7408 +7409 +7410 +7411 +7412 +7413 +7414 +7415 +7416 +7417 +7418 +7419 +7420 +7421 +7422 +7423 +7424 +7425 +7426 +7427 +7428 +7429 +7430 +7431 +7432 +7433 +7434 +7435 +7436 +7437 +7438 +7439 +7440 +7441 +7442 +7443 +7444 +7445 +7446 +7447 +7448 +7449 +7450 +7451 +7452 +7453 +7454 +7455 +7456 +7457 +7458 +7459 +7460 +7461 +7462 +7463 +7464 +7465 +7466 +7467 +7468 +7469 +7470 +7471 +7472 +7473 +7474 +7475 +7476 +7477 +7478 +7479 +7480 +7481 +7482 +7483 +7484 +7485 +7486 +7487 +7488 +7489 +7490 +7491 +7492 +7493 +7494 +7495 +7496 +7497 +7498 +7499 +7500 +7501 +7502 +7503 +7504 +7505 +7506 +7507 +7508 +7509 +7510 +7511 +7512 +7513 +7514 +7515 +7516 +7517 +7518 +7519 +7520 +7521 +7522 +7523 +7524 +7525 +7526 +7527 +7528 +7529 +7530 +7531 +7532 +7533 +7534 +7535 +7536 +7537 +7538 +7539 +7540 +7541 +7542 +7543 +7544 +7545 +7546 +7547 +7548 +7549 +7550 +7551 +7552 +7553 +7554 +7555 +7556 +7557 +7558 +7559 +7560 +7561 +7562 +7563 +7564 +7565 +7566 +7567 +7568 +7569 +7570 +7571 +7572 +7573 +7574 +7575 +7576 +7577 +7578 +7579 +7580 +7581 +7582 +7583 +7584 +7585 +7586 +7587 +7588 +7589 +7590 +7591 +7592 +7593 +7594 +7595 +7596 +7597 +7598 +7599 +7600 +7601 +7602 +7603 +7604 +7605 +7606 +7607 +7608 +7609 +7610 +7611 +7612 +7613 +7614 +7615 +7616 +7617 +7618 +7619 +7620 +7621 +7622 +7623 +7624 +7625 +7626 +7627 +7628 +7629 +7630 +7631 +7632 +7633 +7634 +7635 +7636 +7637 +7638 +7639 +7640 +7641 +7642 +7643 +7644 +7645 +7646 +7647 +7648 +7649 +7650 +7651 +7652 +7653 +7654 +7655 +7656 +7657 +7658 +7659 +7660 +7661 +7662 +7663 +7664 +7665 +7666 +7667 +7668 +7669 +7670 +7671 +7672 +7673 +7674 +7675 +7676 +7677 +7678 +7679 +7680 +7681 +7682 +7683 +7684 +7685 +7686 +7687 +7688 +7689 +7690 +7691 +7692 +7693 +7694 +7695 +7696 +7697 +7698 +7699 +7700 +7701 +7702 +7703 +7704 +7705 +7706 +7707 +7708 +7709 +7710 +7711 +7712 +7713 +7714 +7715 +7716 +7717 +7718 +7719 +7720 +7721 +7722 +7723 +7724 +7725 +7726 +7727 +7728 +7729 +7730 +7731 +7732 +7733 +7734 +7735 +7736 +7737 +7738 +7739 +7740 +7741 +7742 +7743 +7744 +7745 +7746 +7747 +7748 +7749 +7750 +7751 +7752 +7753 +7754 +7755 +7756 +7757 +7758 +7759 +7760 +7761 +7762 +7763 +7764 +7765 +7766 +7767 +7768 +7769 +7770 +7771 +7772 +7773 +7774 +7775 +7776 +7777 +7778 +7779 +7780 +7781 +7782 +7783 +7784 +7785 +7786 +7787 +7788 +7789 +7790 +7791 +7792 +7793 +7794 +7795 +7796 +7797 +7798 +7799 +7800 +7801 +7802 +7803 +7804 +7805 +7806 +7807 +7808 +7809 +7810 +7811 +7812 +7813 +7814 +7815 +7816 +7817 +7818 +7819 +7820 +7821 +7822 +7823 +7824 +7825 +7826 +7827 +7828 +7829 +7830 +7831 +7832 +7833 +7834 +7835 +7836 +7837 +7838 +7839 +7840 +7841 +7842 +7843 +7844 +7845 +7846 +7847 +7848 +7849 +7850 +7851 +7852 +7853 +7854 +7855 +7856 +7857 +7858 +7859 +7860 +7861 +7862 +7863 +7864 +7865 +7866 +7867 +7868 +7869 +7870 +7871 +7872 +7873 +7874 +7875 +7876 +7877 +7878 +7879 +7880 +7881 +7882 +7883 +7884 +7885 +7886 +7887 +7888 +7889 +7890 +7891 +7892 +7893 +7894 +7895 +7896 +7897 +7898 +7899 +7900 +7901 +7902 +7903 +7904 +7905 +7906 +7907 +7908 +7909 +7910 +7911 +7912 +7913 +7914 +7915 +7916 +7917 +7918 +7919 +7920 +7921 +7922 +7923 +7924 +7925 +7926 +7927 +7928 +7929 +7930 +7931 +7932 +7933 +7934 +7935 +7936 +7937 +7938 +7939 +7940 +7941 +7942 +7943 +7944 +7945 +7946 +7947 +7948 +7949 +7950 +7951 +7952 +7953 +7954 +7955 +7956 +7957 +7958 +7959 +7960 +7961 +7962 +7963 +7964 +7965 +7966 +7967 +7968 +7969 +7970 +7971 +7972 +7973 +7974 +7975 +7976 +7977 +7978 +7979 +7980 +7981 +7982 +7983 +7984 +7985 +7986 +7987 +7988 +7989 +7990 +7991 +7992 +7993 +7994 +7995 +7996 +7997 +7998 +7999 +8000 +8001 +8002 +8003 +8004 +8005 +8006 +8007 +8008 +8009 +8010 +8011 +8012 +8013 +8014 +8015 +8016 +8017 +8018 +8019 +8020 +8021 +8022 +8023 +8024 +8025 +8026 +8027 +8028 +8029 +8030 +8031 +8032 +8033 +8034 +8035 +8036 +8037 +8038 +8039 +8040 +8041 +8042 +8043 +8044 +8045 +8046 +8047 +8048 +8049 +8050 +8051 +8052 +8053 +8054 +8055 +8056 +8057 +8058 +8059 +8060 +8061 +8062 +8063 +8064 +8065 +8066 +8067 +8068 +8069 +8070 +8071 +8072 +8073 +8074 +8075 +8076 +8077 +8078 +8079 +8080 +8081 +8082 +8083 +8084 +8085 +8086 +8087 +8088 +8089 +8090 +8091 +8092 +8093 +8094 +8095 +8096 +8097 +8098 +8099 +8100 +8101 +8102 +8103 +8104 +8105 +8106 +8107 +8108 +8109 +8110 +8111 +8112 +8113 +8114 +8115 +8116 +8117 +8118 +8119 +8120 +8121 +8122 +8123 +8124 +8125 +8126 +8127 +8128 +8129 +8130 +8131 +8132 +8133 +8134 +8135 +8136 +8137 +8138 +8139 +8140 +8141 +8142 +8143 +8144 +8145 +8146 +8147 +8148 +8149 +8150 +8151 +8152 +8153 +8154 +8155 +8156 +8157 +8158 +8159 +8160 +8161 +8162 +8163 +8164 +8165 +8166 +8167 +8168 +8169 +8170 +8171 +8172 +8173 +8174 +8175 +8176 +8177 +8178 +8179 +8180 +8181 +8182 +8183 +8184 +8185 +8186 +8187 +8188 +8189 +8190 +8191 +8192 +8193 +8194 +8195 +8196 +8197 +8198 +8199 +8200 +8201 +8202 +8203 +8204 +8205 +8206 +8207 +8208 +8209 +8210 +8211 +8212 +8213 +8214 +8215 +8216 +8217 +8218 +8219 +8220 +8221 +8222 +8223 +8224 +8225 +8226 +8227 +8228 +8229 +8230 +8231 +8232 +8233 +8234 +8235 +8236 +8237 +8238 +8239 +8240 +8241 +8242 +8243 +8244 +8245 +8246 +8247 +8248 +8249 +8250 +8251 +8252 +8253 +8254 +8255 +8256 +8257 +8258 +8259 +8260 +8261 +8262 +8263 +8264 +8265 +8266 +8267 +8268 +8269 +8270 +8271 +8272 +8273 +8274 +8275 +8276 +8277 +8278 +8279 +8280 +8281 +8282 +8283 +8284 +8285 +8286 +8287 +8288 +8289 +8290 +8291 +8292 +8293 +8294 +8295 +8296 +8297 +8298 +8299 +8300 +8301 +8302 +8303 +8304 +8305 +8306 +8307 +8308 +8309 +8310 +8311 +8312 +8313 +8314 +8315 +8316 +8317 +8318 +8319 +8320 +8321 +8322 +8323 +8324 +8325 +8326 +8327 +8328 +8329 +8330 +8331 +8332 +8333 +8334 +8335 +8336 +8337 +8338 +8339 +8340 +8341 +8342 +8343 +8344 +8345 +8346 +8347 +8348 +8349 +8350 +8351 +8352 +8353 +8354 +8355 +8356 +8357 +8358 +8359 +8360 +8361 +8362 +8363 +8364 +8365 +8366 +8367 +8368 +8369 +8370 +8371 +8372 +8373 +8374 +8375 +8376 +8377 +8378 +8379 +8380 +8381 +8382 +8383 +8384 +8385 +8386 +8387 +8388 +8389 +8390 +8391 +8392 +8393 +8394 +8395 +8396 +8397 +8398 +8399 +8400 +8401 +8402 +8403 +8404 +8405 +8406 +8407 +8408 +8409 +8410 +8411 +8412 +8413 +8414 +8415 +8416 +8417 +8418 +8419 +8420 +8421 +8422 +8423 +8424 +8425 +8426 +8427 +8428 +8429 +8430 +8431 +8432 +8433 +8434 +8435 +8436 +8437 +8438 +8439 +8440 +8441 +8442 +8443 +8444 +8445 +8446 +8447 +8448 +8449 +8450 +8451 +8452 +8453 +8454 +8455 +8456 +8457 +8458 +8459 +8460 +8461 +8462 +8463 +8464 +8465 +8466 +8467 +8468 +8469 +8470 +8471 +8472 +8473 +8474 +8475 +8476 +8477 +8478 +8479 +8480 +8481 +8482 +8483 +8484 +8485 +8486 +8487 +8488 +8489 +8490 +8491 +8492 +8493 +8494 +8495 +8496 +8497 +8498 +8499 +8500 +8501 +8502 +8503 +8504 +8505 +8506 +8507 +8508 +8509 +8510 +8511 +8512 +8513 +8514 +8515 +8516 +8517 +8518 +8519 +8520 +8521 +8522 +8523 +8524 +8525 +8526 +8527 +8528 +8529 +8530 +8531 +8532 +8533 +8534 +8535 +8536 +8537 +8538 +8539 +8540 +8541 +8542 +8543 +8544 +8545 +8546 +8547 +8548 +8549 +8550 +8551 +8552 +8553 +8554 +8555 +8556 +8557 +8558 +8559 +8560 +8561 +8562 +8563 +8564 +8565 +8566 +8567 +8568 +8569 +8570 +8571 +8572 +8573 +8574 +8575 +8576 +8577 +8578 +8579 +8580 +8581 +8582 +8583 +8584 +8585 +8586 +8587 +8588 +8589 +8590 +8591 +8592 +8593 +8594 +8595 +8596 +8597 +8598 +8599 +8600 +8601 +8602 +8603 +8604 +8605 +8606 +8607 +8608 +8609 +8610 +8611 +8612 +8613 +8614 +8615 +8616 +8617 +8618 +8619 +8620 +8621 +8622 +8623 +8624 +8625 +8626 +8627 +8628 +8629 +8630 +8631 +8632 +8633 +8634 +8635 +8636 +8637 +8638 +8639 +8640 +8641 +8642 +8643 +8644 +8645 +8646 +8647 +8648 +8649 +8650 +8651 +8652 +8653 +8654 +8655 +8656 +8657 +8658 +8659 +8660 +8661 +8662 +8663 +8664 +8665 +8666 +8667 +8668 +8669 +8670 +8671 +8672 +8673 +8674 +8675 +8676 +8677 +8678 +8679 +8680 +8681 +8682 +8683 +8684 +8685 +8686 +8687 +8688 +8689 +8690 +8691 +8692 +8693 +8694 +8695 +8696 +8697 +8698 +8699 +8700 +8701 +8702 +8703 +8704 +8705 +8706 +8707 +8708 +8709 +8710 +8711 +8712 +8713 +8714 +8715 +8716 +8717 +8718 +8719 +8720 +8721 +8722 +8723 +8724 +8725 +8726 +8727 +8728 +8729 +8730 +8731 +8732 +8733 +8734 +8735 +8736 +8737 +8738 +8739 +8740 +8741 +8742 +8743 +8744 +8745 +8746 +8747 +8748 +8749 +8750 +8751 +8752 +8753 +8754 +8755 +8756 +8757 +8758 +8759 +8760 +8761 +8762 +8763 +8764 +8765 +8766 +8767 +8768 +8769 +8770 +8771 +8772 +8773 +8774 +8775 +8776 +8777 +8778 +8779 +8780 +8781 +8782 +8783 +8784 +8785 +8786 +8787 +8788 +8789 +8790 +8791 +8792 +8793 +8794 +8795 +8796 +8797 +8798 +8799 +8800 +8801 +8802 +8803 +8804 +8805 +8806 +8807 +8808 +8809 +8810 +8811 +8812 +8813 +8814 +8815 +8816 +8817 +8818 +8819 +8820 +8821 +8822 +8823 +8824 +8825 +8826 +8827 +8828 +8829 +8830 +8831 +8832 +8833 +8834 +8835 +8836 +8837 +8838 +8839 +8840 +8841 +8842 +8843 +8844 +8845 +8846 +8847 +8848 +8849 +8850 +8851 +8852 +8853 +8854 +8855 +8856 +8857 +8858 +8859 +8860 +8861 +8862 +8863 +8864 +8865 +8866 +8867 +8868 +8869 +8870 +8871 +8872 +8873 +8874 +8875 +8876 +8877 +8878 +8879 +8880 +8881 +8882 +8883 +8884 +8885 +8886 +8887 +8888 +8889 +8890 +8891 +8892 +8893 +8894 +8895 +8896 +8897 +8898 +8899 +8900 +8901 +8902 +8903 +8904 +8905 +8906 +8907 +8908 +8909 +8910 +8911 +8912 +8913 +8914 +8915 +8916 +8917 +8918 +8919 +8920 +8921 +8922 +8923 +8924 +8925 +8926 +8927 +8928 +8929 +8930 +8931 +8932 +8933 +8934 +8935 +8936 +8937 +8938 +8939 +8940 +8941 +8942 +8943 +8944 +8945 +8946 +8947 +8948 +8949 +8950 +8951 +8952 +8953 +8954 +8955 +8956 +8957 +8958 +8959 +8960 +8961 +8962 +8963 +8964 +8965 +8966 +8967 +8968 +8969 +8970 +8971 +8972 +8973 +8974 +8975 +8976 +8977 +8978 +8979 +8980 +8981 +8982 +8983 +8984 +8985 +8986 +8987 +8988 +8989 +8990 +8991 +8992 +8993 +8994 +8995 +8996 +8997 +8998 +8999 +9000 +9001 +9002 +9003 +9004 +9005 +9006 +9007 +9008 +9009 +9010 +9011 +9012 +9013 +9014 +9015 +9016 +9017 +9018 +9019 +9020 +9021 +9022 +9023 +9024 +9025 +9026 +9027 +9028 +9029 +9030 +9031 +9032 +9033 +9034 +9035 +9036 +9037 +9038 +9039 +9040 +9041 +9042 +9043 +9044 +9045 +9046 +9047 +9048 +9049 +9050 +9051 +9052 +9053 +9054 +9055 +9056 +9057 +9058 +9059 +9060 +9061 +9062 +9063 +9064 +9065 +9066 +9067 +9068 +9069 +9070 +9071 +9072 +9073 +9074 +9075 +9076 +9077 +9078 +9079 +9080 +9081 +9082 +9083 +9084 +9085 +9086 +9087 +9088 +9089 +9090 +9091 +9092 +9093 +9094 +9095 +9096 +9097 +9098 +9099 +9100 +9101 +9102 +9103 +9104 +9105 +9106 +9107 +9108 +9109 +9110 +9111 +9112 +9113 +9114 +9115 +9116 +9117 +9118 +9119 +9120 +9121 +9122 +9123 +9124 +9125 +9126 +9127 +9128 +9129 +9130 +9131 +9132 +9133 +9134 +9135 +9136 +9137 +9138 +9139 +9140 +9141 +9142 +9143 +9144 +9145 +9146 +9147 +9148 +9149 +9150 +9151 +9152 +9153 +9154 +9155 +9156 +9157 +9158 +9159 +9160 +9161 +9162 +9163 +9164 +9165 +9166 +9167 +9168 +9169 +9170 +9171 +9172 +9173 +9174 +9175 +9176 +9177 +9178 +9179 +9180 +9181 +9182 +9183 +9184 +9185 +9186 +9187 +9188 +9189 +9190 +9191 +9192 +9193 +9194 +9195 +9196 +9197 +9198 +9199 +9200 +9201 +9202 +9203 +9204 +9205 +9206 +9207 +9208 +9209 +9210 +9211 +9212 +9213 +9214 +9215 +9216 +9217 +9218 +9219 +9220 +9221 +9222 +9223 +9224 +9225 +9226 +9227 +9228 +9229 +9230 +9231 +9232 +9233 +9234 +9235 +9236 +9237 +9238 +9239 +9240 +9241 +9242 +9243 +9244 +9245 +9246 +9247 +9248 +9249 +9250 +9251 +9252 +9253 +9254 +9255 +9256 +9257 +9258 +9259 +9260 +9261 +9262 +9263 +9264 +9265 +9266 +9267 +9268 +9269 +9270 +9271 +9272 +9273 +9274 +9275 +9276 +9277 +9278 +9279 +9280 +9281 +9282 +9283 +9284 +9285 +9286 +9287 +9288 +9289 +9290 +9291 +9292 +9293 +9294 +9295 +9296 +9297 +9298 +9299 +9300 +9301 +9302 +9303 +9304 +9305 +9306 +9307 +9308 +9309 +9310 +9311 +9312 +9313 +9314 +9315 +9316 +9317 +9318 +9319 +9320 +9321 +9322 +9323 +9324 +9325 +9326 +9327 +9328 +9329 +9330 +9331 +9332 +9333 +9334 +9335 +9336 +9337 +9338 +9339 +9340 +9341 +9342 +9343 +9344 +9345 +9346 +9347 +9348 +9349 +9350 +9351 +9352 +9353 +9354 +9355 +9356 +9357 +9358 +9359 +9360 +9361 +9362 +9363 +9364 +9365 +9366 +9367 +9368 +9369 +9370 +9371 +9372 +9373 +9374 +9375 +9376 +9377 +9378 +9379 +9380 +9381 +9382 +9383 +9384 +9385 +9386 +9387 +9388 +9389 +9390 +9391 +9392 +9393 +9394 +9395 +9396 +9397 +9398 +9399 +9400 +9401 +9402 +9403 +9404 +9405 +9406 +9407 +9408 +9409 +9410 +9411 +9412 +9413 +9414 +9415 +9416 +9417 +9418 +9419 +9420 +9421 +9422 +9423 +9424 +9425 +9426 +9427 +9428 +9429 +9430 +9431 +9432 +9433 +9434 +9435 +9436 +9437 +9438 +9439 +9440 +9441 +9442 +9443 +9444 +9445 +9446 +9447 +9448 +9449 +9450 +9451 +9452 +9453 +9454 +9455 +9456 +9457 +9458 +9459 +9460 +9461 +9462 +9463 +9464 +9465 +9466 +9467 +9468 +9469 +9470 +9471 +9472 +9473 +9474 +9475 +9476 +9477 +9478 +9479 +9480 +9481 +9482 +9483 +9484 +9485 +9486 +9487 +9488 +9489 +9490 +9491 +9492 +9493 +9494 +9495 +9496 +9497 +9498 +9499 +9500 +9501 +9502 +9503 +9504 +9505 +9506 +9507 +9508 +9509 +9510 +9511 +9512 +9513 +9514 +9515 +9516 +9517 +9518 +9519 +9520 +9521 +9522 +9523 +9524 +9525 +9526 +9527 +9528 +9529 +9530 +9531 +9532 +9533 +9534 +9535 +9536 +9537 +9538 +9539 +9540 +9541 +9542 +9543 +9544 +9545 +9546 +9547 +9548 +9549 +9550 +9551 +9552 +9553 +9554 +9555 +9556 +9557 +9558 +9559 +9560 +9561 +9562 +9563 +9564 +9565 +9566 +9567 +9568 +9569 +9570 +9571 +9572 +9573 +9574 +9575 +9576 +9577 +9578 +9579 +9580 +9581 +9582 +9583 +9584 +9585 +9586 +9587 +9588 +9589 +9590 +9591 +9592 +9593 +9594 +9595 +9596 +9597 +9598 +9599 +9600 +9601 +9602 +9603 +9604 +9605 +9606 +9607 +9608 +9609 +9610 +9611 +9612 +9613 +9614 +9615 +9616 +9617 +9618 +9619 +9620 +9621 +9622 +9623 +9624 +9625 +9626 +9627 +9628 +9629 +9630 +9631 +9632 +9633 +9634 +9635 +9636 +9637 +9638 +9639 +9640 +9641 +9642 +9643 +9644 +9645 +9646 +9647 +9648 +9649 +9650 +9651 +9652 +9653 +9654 +9655 +9656 +9657 +9658 +9659 +9660 +9661 +9662 +9663 +9664 +9665 +9666 +9667 +9668 +9669 +9670 +9671 +9672 +9673 +9674 +9675 +9676 +9677 +9678 +9679 +9680 +9681 +9682 +9683 +9684 +9685 +9686 +9687 +9688 +9689 +9690 +9691 +9692 +9693 +9694 +9695 +9696 +9697 +9698 +9699 +9700 +9701 +9702 +9703 +9704 +9705 +9706 +9707 +9708 +9709 +9710 +9711 +9712 +9713 +9714 +9715 +9716 +9717 +9718 +9719 +9720 +9721 +9722 +9723 +9724 +9725 +9726 +9727 +9728 +9729 +9730 +9731 +9732 +9733 +9734 +9735 +9736 +9737 +9738 +9739 +9740 +9741 +9742 +9743 +9744 +9745 +9746 +9747 +9748 +9749 +9750 +9751 +9752 +9753 +9754 +9755 +9756 +9757 +9758 +9759 +9760 +9761 +9762 +9763 +9764 +9765 +9766 +9767 +9768 +9769 +9770 +9771 +9772 +9773 +9774 +9775 +9776 +9777 +9778 +9779 +9780 +9781 +9782 +9783 +9784 +9785 +9786 +9787 +9788 +9789 +9790 +9791 +9792 +9793 +9794 +9795 +9796 +9797 +9798 +9799 +9800 +9801 +9802 +9803 +9804 +9805 +9806 +9807 +9808 +9809 +9810 +9811 +9812 +9813 +9814 +9815 +9816 +9817 +9818 +9819 +9820 +9821 +9822 +9823 +9824 +9825 +9826 +9827 +9828 +9829 +9830 +9831 +9832 +9833 +9834 +9835 +9836 +9837 +9838 +9839 +9840 +9841 +9842 +9843 +9844 +9845 +9846 +9847 +9848 +9849 +9850 +9851 +9852 +9853 +9854 +9855 +9856 +9857 +9858 +9859 +9860 +9861 +9862 +9863 +9864 +9865 +9866 +9867 +9868 +9869 +9870 +9871 +9872 +9873 +9874 +9875 +9876 +9877 +9878 +9879 +9880 +9881 +9882 +9883 +9884 +9885 +9886 +9887 +9888 +9889 +9890 +9891 +9892 +9893 +9894 +9895 +9896 +9897 +9898 +9899 +9900 +9901 +9902 +9903 +9904 +9905 +9906 +9907 +9908 +9909 +9910 +9911 +9912 +9913 +9914 +9915 +9916 +9917 +9918 +9919 +9920 +9921 +9922 +9923 +9924 +9925 +9926 +9927 +9928 +9929 +9930 +9931 +9932 +9933 +9934 +9935 +9936 +9937 +9938 +9939 +9940 +9941 +9942 +9943 +9944 +9945 +9946 +9947 +9948 +9949 +9950 +9951 +9952 +9953 +9954 +9955 +9956 +9957 +9958 +9959 +9960 +9961 +9962 +9963 +9964 +9965 +9966 +9967 +9968 +9969 +9970 +9971 +9972 +9973 +9974 +9975 +9976 +9977 +9978 +9979 +9980 +9981 +9982 +9983 +9984 +9985 +9986 +9987 +9988 +9989 +9990 +9991 +9992 +9993 +9994 +9995 +9996 +9997 +9998 +9999 +10000 +10001 +10002 +10003 +10004 +10005 +10006 +10007 +10008 +10009 +10010 +10011 +10012 +10013 +10014 +10015 +10016 +10017 +10018 +10019 +10020 +10021 +10022 +10023 +10024 +10025 +10026 +10027 +10028 +10029 +10030 +10031 +10032 +10033 +10034 +10035 +10036 +10037 +10038 +10039 +10040 +10041 +10042 +10043 +10044 +10045 +10046 +10047 +10048 +10049 +10050 +10051 +10052 +10053 +10054 +10055 +10056 +10057 +10058 +10059 +10060 +10061 +10062 +10063 +10064 +10065 +10066 +10067 +10068 +10069 +10070 +10071 +10072 +10073 +10074 +10075 +10076 +10077 +10078 +10079 +10080 +10081 +10082 +10083 +10084 +10085 +10086 +10087 +10088 +10089 +10090 +10091 +10092 +10093 +10094 +10095 +10096 +10097 +10098 +10099 +10100 +10101 +10102 +10103 +10104 +10105 +10106 +10107 +10108 +10109 +10110 +10111 +10112 +10113 +10114 +10115 +10116 +10117 +10118 +10119 +10120 +10121 +10122 +10123 +10124 +10125 +10126 +10127 +10128 +10129 +10130 +10131 +10132 +10133 +10134 +10135 +10136 +10137 +10138 +10139 +10140 +10141 +10142 +10143 +10144 +10145 +10146 +10147 +10148 +10149 +10150 +10151 +10152 +10153 +10154 +10155 +10156 +10157 +10158 +10159 +10160 +10161 +10162 +10163 +10164 +10165 +10166 +10167 +10168 +10169 +10170 +10171 +10172 +10173 +10174 +10175 +10176 +10177 +10178 +10179 +10180 +10181 +10182 +10183 +10184 +10185 +10186 +10187 +10188 +10189 +10190 +10191 +10192 +10193 +10194 +10195 +10196 +10197 +10198 +10199 +10200 +10201 +10202 +10203 +10204 +10205 +10206 +10207 +10208 +10209 +10210 +10211 +10212 +10213 +10214 +10215 +10216 +10217 +10218 +10219 +10220 +10221 +10222 +10223 +10224 +10225 +10226 +10227 +10228 +10229 +10230 +10231 +10232 +10233 +10234 +10235 +10236 +10237 +10238 +10239 +10240 +10241 +10242 +10243 +10244 +10245 +10246 +10247 +10248 +10249 +10250 +10251 +10252 +10253 +10254 +10255 +10256 +10257 +10258 +10259 +10260 +10261 +10262 +10263 +10264 +10265 +10266 +10267 +10268 +10269 +10270 +10271 +10272 +10273 +10274 +10275 +10276 +10277 +10278 +10279 +10280 +10281 +10282 +10283 +10284 +10285 +10286 +10287 +10288 +10289 +10290 +10291 +10292 +10293 +10294 +10295 +10296 +10297 +10298 +10299 +10300 +10301 +10302 +10303 +10304 +10305 +10306 +10307 +10308 +10309 +10310 +10311 +10312 +10313 +10314 +10315 +10316 +10317 +10318 +10319 +10320 +10321 +10322 +10323 +10324 +10325 +10326 +10327 +10328 +10329 +10330 +10331 +10332 +10333 +10334 +10335 +10336 +10337 +10338 +10339 +10340 +10341 +10342 +10343 +10344 +10345 +10346 +10347 +10348 +10349 +10350 +10351 +10352 +10353 +10354 +10355 +10356 +10357 +10358 +10359 +10360 +10361 +10362 +10363 +10364 +10365 +10366 +10367 +10368 +10369 +10370 +10371 +10372 +10373 +10374 +10375 +10376 +10377 +10378 +10379 +10380 +10381 +10382 +10383 +10384 +10385 +10386 +10387 +10388 +10389 +10390 +10391 +10392 +10393 +10394 +10395 +10396 +10397 +10398 +10399 +10400 +10401 +10402 +10403 +10404 +10405 +10406 +10407 +10408 +10409 +10410 +10411 +10412 +10413 +10414 +10415 +10416 +10417 +10418 +10419 +10420 +10421 +10422 +10423 +10424 +10425 +10426 +10427 +10428 +10429 +10430 +10431 +10432 +10433 +10434 +10435 +10436 +10437 +10438 +10439 +10440 +10441 +10442 +10443 +10444 +10445 +10446 +10447 +10448 +10449 +10450 +10451 +10452 +10453 +10454 +10455 +10456 +10457 +10458 +10459 +10460 +10461 +10462 +10463 +10464 +10465 +10466 +10467 +10468 +10469 +10470 +10471 +10472 +10473 +10474 +10475 +10476 +10477 +10478 +10479 +10480 +10481 +10482 +10483 +10484 +10485 +10486 +10487 +10488 +10489 +10490 +10491 +10492 +10493 +10494 +10495 +10496 +10497 +10498 +10499 +10500 +10501 +10502 +10503 +10504 +10505 +10506 +10507 +10508 +10509 +10510 +10511 +10512 +10513 +10514 +10515 +10516 +10517 +10518 +10519 +10520 +10521 +10522 +10523 +10524 +10525 +10526 +10527 +10528 +10529 +10530 +10531 +10532 +10533 +10534 +10535 +10536 +10537 +10538 +10539 +10540 +10541 +10542 +10543 +10544 +10545 +10546 +10547 +10548 +10549 +10550 +10551 +10552 +10553 +10554 +10555 +10556 +10557 +10558 +10559 +10560 +10561 +10562 +10563 +10564 +10565 +10566 +10567 +10568 +10569 +10570 +10571 +10572 +10573 +10574 +10575 +10576 +10577 +10578 +10579 +10580 +10581 +10582 +10583 +10584 +10585 +10586 +10587 +10588 +10589 +10590 +10591 +10592 +10593 +10594 +10595 +10596 +10597 +10598 +10599 +10600 +10601 +10602 +10603 +10604 +10605 +10606 +10607 +10608 +10609 +10610 +10611 +10612 +10613 +10614 +10615 +10616 +10617 +10618 +10619 +10620 +10621 +10622 +10623 +10624 +10625 +10626 +10627 +10628 +10629 +10630 +10631 +10632 +10633 +10634 +10635 +10636 +10637 +10638 +10639 +10640 +10641 +10642 +10643 +10644 +10645 +10646 +10647 +10648 +10649 +10650 +10651 +10652 +10653 +10654 +10655 +10656 +10657 +10658 +10659 +10660 +10661 +10662 +10663 +10664 +10665 +10666 +10667 +10668 +10669 +10670 +10671 +10672 +10673 +10674 +10675 +10676 +10677 +10678 +10679 +10680 +10681 +10682 +10683 +10684 +10685 +10686 +10687 +10688 +10689 +10690 +10691 +10692 +10693 +10694 +10695 +10696 +10697 +10698 +10699 +10700 +10701 +10702 +10703 +10704 +10705 +10706 +10707 +10708 +10709 +10710 +10711 +10712 +10713 +10714 +10715 +10716 +10717 +10718 +10719 +10720 +10721 +10722 +10723 +10724 +10725 +10726 +10727 +10728 +10729 +10730 +10731 +10732 +10733 +10734 +10735 +10736 +10737 +10738 +10739 +10740 +10741 +10742 +10743 +10744 +10745 +10746 +10747 +10748 +10749 +10750 +10751 +10752 +10753 +10754 +10755 +10756 +10757 +10758 +10759 +10760 +10761 +10762 +10763 +10764 +10765 +10766 +10767 +10768 +10769 +10770 +10771 +10772 +10773 +10774 +10775 +10776 +10777 +10778 +10779 +10780 +10781 +10782 +10783 +10784 +10785 +10786 +10787 +10788 +10789 +10790 +10791 +10792 +10793 +10794 +10795 +10796 +10797 +10798 +10799 +10800 +10801 +10802 +10803 +10804 +10805 +10806 +10807 +10808 +10809 +10810 +10811 +10812 +10813 +10814 +10815 +10816 +10817 +10818 +10819 +10820 +10821 +10822 +10823 +10824 +10825 +10826 +10827 +10828 +10829 +10830 +10831 +10832 +10833 +10834 +10835 +10836 +10837 +10838 +10839 +10840 +10841 +10842 +10843 +10844 +10845 +10846 +10847 +10848 +10849 +10850 +10851 +10852 +10853 +10854 +10855 +10856 +10857 +10858 +10859 +10860 +10861 +10862 +10863 +10864 +10865 +10866 +10867 +10868 +10869 +10870 +10871 +10872 +10873 +10874 +10875 +10876 +10877 +10878 +10879 +10880 +10881 +10882 +10883 +10884 +10885 +10886 +10887 +10888 +10889 +10890 +10891 +10892 +10893 +10894 +10895 +10896 +10897 +10898 +10899 +10900 +10901 +10902 +10903 +10904 +10905 +10906 +10907 +10908 +10909 +10910 +10911 +10912 +10913 +10914 +10915 +10916 +10917 +10918 +10919 +10920 +10921 +10922 +10923 +10924 +10925 +10926 +10927 +10928 +10929 +10930 +10931 +10932 +10933 +10934 +10935 +10936 +10937 +10938 +10939 +10940 +10941 +10942 +10943 +10944 +10945 +10946 +10947 +10948 +10949 +10950 +10951 +10952 +10953 +10954 +10955 +10956 +10957 +10958 +10959 +10960 +10961 +10962 +10963 +10964 +10965 +10966 +10967 +10968 +10969 +10970 +10971 +10972 +10973 +10974 +10975 +10976 +10977 +10978 +10979 +10980 +10981 +10982 +10983 +10984 +10985 +10986 +10987 +10988 +10989 +10990 +10991 +10992 +10993 +10994 +10995 +10996 +10997 +10998 +10999 +11000 +11001 +11002 +11003 +11004 +11005 +11006 +11007 +11008 +11009 +11010 +11011 +11012 +11013 +11014 +11015 +11016 +11017 +11018 +11019 +11020 +11021 +11022 +11023 +11024 +11025 +11026 +11027 +11028 +11029 +11030 +11031 +11032 +11033 +11034 +11035 +11036 +11037 +11038 +11039 +11040 +11041 +11042 +11043 +11044 +11045 +11046 +11047 +11048 +11049 +11050 +11051 +11052 +11053 +11054 +11055 +11056 +11057 +11058 +11059 +11060 +11061 +11062 +11063 +11064 +11065 +11066 +11067 +11068 +11069 +11070 +11071 +11072 +11073 +11074 +11075 +11076 +11077 +11078 +11079 +11080 +11081 +11082 +11083 +11084 +11085 +11086 +11087 +11088 +11089 +11090 +11091 +11092 +11093 +11094 +11095 +11096 +11097 +11098 +11099 +11100 +11101 +11102 +11103 +11104 +11105 +11106 +11107 +11108 +11109 +11110 +11111 +11112 +11113 +11114 +11115 +11116 +11117 +11118 +11119 +11120 +11121 +11122 +11123 +11124 +11125 +11126 +11127 +11128 +11129 +11130 +11131 +11132 +11133 +11134 +11135 +11136 +11137 +11138 +11139 +11140 +11141 +11142 +11143 +11144 +11145 +11146 +11147 +11148 +11149 +11150 +11151 +11152 +11153 +11154 +11155 +11156 +11157 +11158 +11159 +11160 +11161 +11162 +11163 +11164 +11165 +11166 +11167 +11168 +11169 +11170 +11171 +11172 +11173 +11174 +11175 +11176 +11177 +11178 +11179 +11180 +11181 +11182 +11183 +11184 +11185 +11186 +11187 +11188 +11189 +11190 +11191 +11192 +11193 +11194 +11195 +11196 +11197 +11198 +11199 +11200 +11201 +11202 +11203 +11204 +11205 +11206 +11207 +11208 +11209 +11210 +11211 +11212 +11213 +11214 +11215 +11216 +11217 +11218 +11219 +11220 +11221 +11222 +11223 +11224 +11225 +11226 +11227 +11228 +11229 +11230 +11231 +11232 +11233 +11234 +11235 +11236 +11237 +11238 +11239 +11240 +11241 +11242 +11243 +11244 +11245 +11246 +11247 +11248 +11249 +11250 +11251 +11252 +11253 +11254 +11255 +11256 +11257 +11258 +11259 +11260 +11261 +11262 +11263 +11264 +11265 +11266 +11267 +11268 +11269 +11270 +11271 +11272 +11273 +11274 +11275 +11276 +11277 +11278 +11279 +11280 +11281 +11282 +11283 +11284 +11285 +11286 +11287 +11288 +11289 +11290 +11291 +11292 +11293 +11294 +11295 +11296 +11297 +11298 +11299 +11300 +11301 +11302 +11303 +11304 +11305 +11306 +11307 +11308 +11309 +11310 +11311 +11312 +11313 +11314 +11315 +11316 +11317 +11318 +11319 +11320 +11321 +11322 +11323 +11324 +11325 +11326 +11327 +11328 +11329 +11330 +11331 +11332 +11333 +11334 +11335 +11336 +11337 +11338 +11339 +11340 +11341 +11342 +11343 +11344 +11345 +11346 +11347 +11348 +11349 +11350 +11351 +11352 +11353 +11354 +11355 +11356 +11357 +11358 +11359 +11360 +11361 +11362 +11363 +11364 +11365 +11366 +11367 +11368 +11369 +11370 +11371 +11372 +11373 +11374 +11375 +11376 +11377 +11378 +11379 +11380 +11381 +11382 +11383 +11384 +11385 +11386 +11387 +11388 +11389 +11390 +11391 +11392 +11393 +11394 +11395 +11396 +11397 +11398 +11399 +11400 +11401 +11402 +11403 +11404 +11405 +11406 +11407 +11408 +11409 +11410 +11411 +11412 +11413 +11414 +11415 +11416 +11417 +11418 +11419 +11420 +11421 +11422 +11423 +11424 +11425 +11426 +11427 +11428 +11429 +11430 +11431 +11432 +11433 +11434 +11435 +11436 +11437 +11438 +11439 +11440 +11441 +11442 +11443 +11444 +11445 +11446 +11447 +11448 +11449 +11450 +11451 +11452 +11453 +11454 +11455 +11456 +11457 +11458 +11459 +11460 +11461 +11462 +11463 +11464 +11465 +11466 +11467 +11468 +11469 +11470 +11471 +11472 +11473 +11474 +11475 +11476 +11477 +11478 +11479 +11480 +11481 +11482 +11483 +11484 +11485 +11486 +11487 +11488 +11489 +11490 +11491 +11492 +11493 +11494 +11495 +11496 +11497 +11498 +11499 +11500 +11501 +11502 +11503 +11504 +11505 +11506 +11507 +11508 +11509 +11510 +11511 +11512 +11513 +11514 +11515 +11516 +11517 +11518 +11519 +11520 +11521 +11522 +11523 +11524 +11525 +11526 +11527 +11528 +11529 +11530 +11531 +11532 +11533 +11534 +11535 +11536 +11537 +11538 +11539 +11540 +11541 +11542 +11543 +11544 +11545 +11546 +11547 +11548 +11549 +11550 +11551 +11552 +11553 +11554 +11555 +11556 +11557 +11558 +11559 +11560 +11561 +11562 +11563 +11564 +11565 +11566 +11567 +11568 +11569 +11570 +11571 +11572 +11573 +11574 +11575 +11576 +11577 +11578 +11579 +11580 +11581 +11582 +11583 +11584 +11585 +11586 +11587 +11588 +11589 +11590 +11591 +11592 +11593 +11594 +11595 +11596 +11597 +11598 +11599 +11600 +11601 +11602 +11603 +11604 +11605 +11606 +11607 +11608 +11609 +11610 +11611 +11612 +11613 +11614 +11615 +11616 +11617 +11618 +11619 +11620 +11621 +11622 +11623 +11624 +11625 +11626 +11627 +11628 +11629 +11630 +11631 +11632 +11633 +11634 +11635 +11636 +11637 +11638 +11639 +11640 +11641 +11642 +11643 +11644 +11645 +11646 +11647 +11648 +11649 +11650 +11651 +11652 +11653 +11654 +11655 +11656 +11657 +11658 +11659 +11660 +11661 +11662 +11663 +11664 +11665 +11666 +11667 +11668 +11669 +11670 +11671 +11672 +11673 +11674 +11675 +11676 +11677 +11678 +11679 +11680 +11681 +11682 +11683 +11684 +11685 +11686 +11687 +11688 +11689 +11690 +11691 +11692 +11693 +11694 +11695 +11696 +11697 +11698 +11699 +11700 +11701 +11702 +11703 +11704 +11705 +11706 +11707 +11708 +11709 +11710 +11711 +11712 +11713 +11714 +11715 +11716 +11717 +11718 +11719 +11720 +11721 +11722 +11723 +11724 +11725 +11726 +11727 +11728 +11729 +11730 +11731 +11732 +11733 +11734 +11735 +11736 +11737 +11738 +11739 +11740 +11741 +11742 +11743 +11744 +11745 +11746 +11747 +11748 +11749 +11750 +11751 +11752 +11753 +11754 +11755 +11756 +11757 +11758 +11759 +11760 +11761 +11762 +11763 +11764 +11765 +11766 +11767 +11768 +11769 +11770 +11771 +11772 +11773 +11774 +11775 +11776 +11777 +11778 +11779 +11780 +11781 +11782 +11783 +11784 +11785 +11786 +11787 +11788 +11789 +11790 +11791 +11792 +11793 +11794 +11795 +11796 +11797 +11798 +11799 +11800 +11801 +11802 +11803 +11804 +11805 +11806 +11807 +11808 +11809 +11810 +11811 +11812 +11813 +11814 +11815 +11816 +11817 +11818 +11819 +11820 +11821 +11822 +11823 +11824 +11825 +11826 +11827 +11828 +11829 +11830 +11831 +11832 +11833 +11834 +11835 +11836 +11837 +11838 +11839 +11840 +11841 +11842 +11843 +11844 +11845 +11846 +11847 +11848 +11849 +11850 +11851 +11852 +11853 +11854 +11855 +11856 +11857 +11858 +11859 +11860 +11861 +11862 +11863 +11864 +11865 +11866 +11867 +11868 +11869 +11870 +11871 +11872 +11873 +11874 +11875 +11876 +11877 +11878 +11879 +11880 +11881 +11882 +11883 +11884 +11885 +11886 +11887 +11888 +11889 +11890 +11891 +11892 +11893 +11894 +11895 +11896 +11897 +11898 +11899 +11900 +11901 +11902 +11903 +11904 +11905 +11906 +11907 +11908 +11909 +11910 +11911 +11912 +11913 +11914 +11915 +11916 +11917 +11918 +11919 +11920 +11921 +11922 +11923 +11924 +11925 +11926 +11927 +11928 +11929 +11930 +11931 +11932 +11933 +11934 +11935 +11936 +11937 +11938 +11939 +11940 +11941 +11942 +11943 +11944 +11945 +11946 +11947 +11948 +11949 +11950 +11951 +11952 +11953 +11954 +11955 +11956 +11957 +11958 +11959 +11960 +11961 +11962 +11963 +11964 +11965 +11966 +11967 +11968 +11969 +11970 +11971 +11972 +11973 +11974 +11975 +11976 +11977 +11978 +11979 +11980 +11981 +11982 +11983 +11984 +11985 +11986 +11987 +11988 +11989 +11990 +11991 +11992 +11993 +11994 +11995 +11996 +11997 +11998 +11999 +12000 +12001 +12002 +12003 +12004 +12005 +12006 +12007 +12008 +12009 +12010 +12011 +12012 +12013 +12014 +12015 +12016 +12017 +12018 +12019 +12020 +12021 +12022 +12023 +12024 +12025 +12026 +12027 +12028 +12029 +12030 +12031 +12032 +12033 +12034 +12035 +12036 +12037 +12038 +12039 +12040 +12041 +12042 +12043 +12044 +12045 +12046 +12047 +12048 +12049 +12050 +12051 +12052 +12053 +12054 +12055 +12056 +12057 +12058 +12059 +12060 +12061 +12062 +12063 +12064 +12065 +12066 +12067 +12068 +12069 +12070 +12071 +12072 +12073 +12074 +12075 +12076 +12077 +12078 +12079 +12080 +12081 +12082 +12083 +12084 +12085 +12086 +12087 +12088 +12089 +12090 +12091 +12092 +12093 +12094 +12095 +12096 +12097 +12098 +12099 +12100 +12101 +12102 +12103 +12104 +12105 +12106 +12107 +12108 +12109 +12110 +12111 +12112 +12113 +12114 +12115 +12116 +12117 +12118 +12119 +12120 +12121 +12122 +12123 +12124 +12125 +12126 +12127 +12128 +12129 +12130 +12131 +12132 +12133 +12134 +12135 +12136 +12137 +12138 +12139 +12140 +12141 +12142 +12143 +12144 +12145 +12146 +12147 +12148 +12149 +12150 +12151 +12152 +12153 +12154 +12155 +12156 +12157 +12158 +12159 +12160 +12161 +12162 +12163 +12164 +12165 +12166 +12167 +12168 +12169 +12170 +12171 +12172 +12173 +12174 +12175 +12176 +12177 +12178 +12179 +12180 +12181 +12182 +12183 +12184 +12185 +12186 +12187 +12188 +12189 +12190 +12191 +12192 +12193 +12194 +12195 +12196 +12197 +12198 +12199 +12200 +12201 +12202 +12203 +12204 +12205 +12206 +12207 +12208 +12209 +12210 +12211 +12212 +12213 +12214 +12215 +12216 +12217 +12218 +12219 +12220 +12221 +12222 +12223 +12224 +12225 +12226 +12227 +12228 +12229 +12230 +12231 +12232 +12233 +12234 +12235 +12236 +12237 +12238 +12239 +12240 +12241 +12242 +12243 +12244 +12245 +12246 +12247 +12248 +12249 +12250 +12251 +12252 +12253 +12254 +12255 +12256 +12257 +12258 +12259 +12260 +12261 +12262 +12263 +12264 +12265 +12266 +12267 +12268 +12269 +12270 +12271 +12272 +12273 +12274 +12275 +12276 +12277 +12278 +12279 +12280 +12281 +12282 +12283 +12284 +12285 +12286 +12287 +12288 +12289 +12290 +12291 +12292 +12293 +12294 +12295 +12296 +12297 +12298 +12299 +12300 +12301 +12302 +12303 +12304 +12305 +12306 +12307 +12308 +12309 +12310 +12311 +12312 +12313 +12314 +12315 +12316 +12317 +12318 +12319 +12320 +12321 +12322 +12323 +12324 +12325 +12326 +12327 +12328 +12329 +12330 +12331 +12332 +12333 +12334 +12335 +12336 +12337 +12338 +12339 +12340 +12341 +12342 +12343 +12344 +12345 +12346 +12347 +12348 +12349 +12350 +12351 +12352 +12353 +12354 +12355 +12356 +12357 +12358 +12359 +12360 +12361 +12362 +12363 +12364 +12365 +12366 +12367 +12368 +12369 +12370 +12371 +12372 +12373 +12374 +12375 +12376 +12377 +12378 +12379 +12380 +12381 +12382 +12383 +12384 +12385 +12386 +12387 +12388 +12389 +12390 +12391 +12392 +12393 +12394 +12395 +12396 +12397 +12398 +12399 +12400 +12401 +12402 +12403 +12404 +12405 +12406 +12407 +12408 +12409 +12410 +12411 +12412 +12413 +12414 +12415 +12416 +12417 +12418 +12419 +12420 +12421 +12422 +12423 +12424 +12425 +12426 +12427 +12428 +12429 +12430 +12431 +12432 +12433 +12434 +12435 +12436 +12437 +12438 +12439 +12440 +12441 +12442 +12443 +12444 +12445 +12446 +12447 +12448 +12449 +12450 +12451 +12452 +12453 +12454 +12455 +12456 +12457 +12458 +12459 +12460 +12461 +12462 +12463 +12464 +12465 +12466 +12467 +12468 +12469 +12470 +12471 +12472 +12473 +12474 +12475 +12476 +12477 +12478 +12479 +12480 +12481 +12482 +12483 +12484 +12485 +12486 +12487 +12488 +12489 +12490 +12491 +12492 +12493 +12494 +12495 +12496 +12497 +12498 +12499 +12500 +12501 +12502 +12503 +12504 +12505 +12506 +12507 +12508 +12509 +12510 +12511 +12512 +12513 +12514 +12515 +12516 +12517 +12518 +12519 +12520 +12521 +12522 +12523 +12524 +12525 +12526 +12527 +12528 +12529 +12530 +12531 +12532 +12533 +12534 +12535 +12536 +12537 +12538 +12539 +12540 +12541 +12542 +12543 +12544 +12545 +12546 +12547 +12548 +12549 +12550 +12551 +12552 +12553 +12554 +12555 +12556 +12557 +12558 +12559 +12560 +12561 +12562 +12563 +12564 +12565 +12566 +12567 +12568 +12569 +12570 +12571 +12572 +12573 +12574 +12575 +12576 +12577 +12578 +12579 +12580 +12581 +12582 +12583 +12584 +12585 +12586 +12587 +12588 +12589 +12590 +12591 +12592 +12593 +12594 +12595 +12596 +12597 +12598 +12599 +12600 +12601 +12602 +12603 +12604 +12605 +12606 +12607 +12608 +12609 +12610 +12611 +12612 +12613 +12614 +12615 +12616 +12617 +12618 +12619 +12620 +12621 +12622 +12623 +12624 +12625 +12626 +12627 +12628 +12629 +12630 +12631 +12632 +12633 +12634 +12635 +12636 +12637 +12638 +12639 +12640 +12641 +12642 +12643 +12644 +12645 +12646 +12647 +12648 +12649 +12650 +12651 +12652 +12653 +12654 +12655 +12656 +12657 +12658 +12659 +12660 +12661 +12662 +12663 +12664 +12665 +12666 +12667 +12668 +12669 +12670 +12671 +12672 +12673 +12674 +12675 +12676 +12677 +12678 +12679 +12680 +12681 +12682 +12683 +12684 +12685 +12686 +12687 +12688 +12689 +12690 +12691 +12692 +12693 +12694 +12695 +12696 +12697 +12698 +12699 +12700 +12701 +12702 +12703 +12704 +12705 +12706 +12707 +12708 +12709 +12710 +12711 +12712 +12713 +12714 +12715 +12716 +12717 +12718 +12719 +12720 +12721 +12722 +12723 +12724 +12725 +12726 +12727 +12728 +12729 +12730 +12731 +12732 +12733 +12734 +12735 +12736 +12737 +12738 +12739 +12740 +12741 +12742 +12743 +12744 +12745 +12746 +12747 +12748 +12749 +12750 +12751 +12752 +12753 +12754 +12755 +12756 +12757 +12758 +12759 +12760 +12761 +12762 +12763 +12764 +12765 +12766 +12767 +12768 +12769 +12770 +12771 +12772 +12773 +12774 +12775 +12776 +12777 +12778 +12779 +12780 +12781 +12782 +12783 +12784 +12785 +12786 +12787 +12788 +12789 +12790 +12791 +12792 +12793 +12794 +12795 +12796 +12797 +12798 +12799 +12800 +12801 +12802 +12803 +12804 +12805 +12806 +12807 +12808 +12809 +12810 +12811 +12812 +12813 +12814 +12815 +12816 +12817 +12818 +12819 +12820 +12821 +12822 +12823 +12824 +12825 +12826 +12827 +12828 +12829 +12830 +12831 +12832 +12833 +12834 +12835 +12836 +12837 +12838 +12839 +12840 +12841 +12842 +12843 +12844 +12845 +12846 +12847 +12848 +12849 +12850 +12851 +12852 +12853 +12854 +12855 +12856 +12857 +12858 +12859 +12860 +12861 +12862 +12863 +12864 +12865 +12866 +12867 +12868 +12869 +12870 +12871 +12872 +12873 +12874 +12875 +12876 +12877 +12878 +12879 +12880 +12881 +12882 +12883 +12884 +12885 +12886 +12887 +12888 +12889 +12890 +12891 +12892 +12893 +12894 +12895 +12896 +12897 +12898 +12899 +12900 +12901 +12902 +12903 +12904 +12905 +12906 +12907 +12908 +12909 +12910 +12911 +12912 +12913 +12914 +12915 +12916 +12917 +12918 +12919 +12920 +12921 +12922 +12923 +12924 +12925 +12926 +12927 +12928 +12929 +12930 +12931 +12932 +12933 +12934 +12935 +12936 +12937 +12938 +12939 +12940 +12941 +12942 +12943 +12944 +12945 +12946 +12947 +12948 +12949 +12950 +12951 +12952 +12953 +12954 +12955 +12956 +12957 +12958 +12959 +12960 +12961 +12962 +12963 +12964 +12965 +12966 +12967 +12968 +12969 +12970 +12971 +12972 +12973 +12974 +12975 +12976 +12977 +12978 +12979 +12980 +12981 +12982 +12983 +12984 +12985 +12986 +12987 +12988 +12989 +12990 +12991 +12992 +12993 +12994 +12995 +12996 +12997 +12998 +12999 +13000 +13001 +13002 +13003 +13004 +13005 +13006 +13007 +13008 +13009 +13010 +13011 +13012 +13013 +13014 +13015 +13016 +13017 +13018 +13019 +13020 +13021 +13022 +13023 +13024 +13025 +13026 +13027 +13028 +13029 +13030 +13031 +13032 +13033 +13034 +13035 +13036 +13037 +13038 +13039 +13040 +13041 +13042 +13043 +13044 +13045 +13046 +13047 +13048 +13049 +13050 +13051 +13052 +13053 +13054 +13055 +13056 +13057 +13058 +13059 +13060 +13061 +13062 +13063 +13064 +13065 +13066 +13067 +13068 +13069 +13070 +13071 +13072 +13073 +13074 +13075 +13076 +13077 +13078 +13079 +13080 +13081 +13082 +13083 +13084 +13085 +13086 +13087 +13088 +13089 +13090 +13091 +13092 +13093 +13094 +13095 +13096 +13097 +13098 +13099 +13100 +13101 +13102 +13103 +13104 +13105 +13106 +13107 +13108 +13109 +13110 +13111 +13112 +13113 +13114 +13115 +13116 +13117 +13118 +13119 +13120 +13121 +13122 +13123 +13124 +13125 +13126 +13127 +13128 +13129 +13130 +13131 +13132 +13133 +13134 +13135 +13136 +13137 +13138 +13139 +13140 +13141 +13142 +13143 +13144 +13145 +13146 +13147 +13148 +13149 +13150 +13151 +13152 +13153 +13154 +13155 +13156 +13157 +13158 +13159 +13160 +13161 +13162 +13163 +13164 +13165 +13166 +13167 +13168 +13169 +13170 +13171 +13172 +13173 +13174 +13175 +13176 +13177 +13178 +13179 +13180 +13181 +13182 +13183 +13184 +13185 +13186 +13187 +13188 +13189 +13190 +13191 +13192 +13193 +13194 +13195 +13196 +13197 +13198 +13199 +13200 +13201 +13202 +13203 +13204 +13205 +13206 +13207 +13208 +13209 +13210 +13211 +13212 +13213 +13214 +13215 +13216 +13217 +13218 +13219 +13220 +13221 +13222 +13223 +13224 +13225 +13226 +13227 +13228 +13229 +13230 +13231 +13232 +13233 +13234 +13235 +13236 +13237 +13238 +13239 +13240 +13241 +13242 +13243 +13244 +13245 +13246 +13247 +13248 +13249 +13250 +13251 +13252 +13253 +13254 +13255 +13256 +13257 +13258 +13259 +13260 +13261 +13262 +13263 +13264 +13265 +13266 +13267 +13268 +13269 +13270 +13271 +13272 +13273 +13274 +13275 +13276 +13277 +13278 +13279 +13280 +13281 +13282 +13283 +13284 +13285 +13286 +13287 +13288 +13289 +13290 +13291 +13292 +13293 +13294 +13295 +13296 +13297 +13298 +13299 +13300 +13301 +13302 +13303 +13304 +13305 +13306 +13307 +13308 +13309 +13310 +13311 +13312 +13313 +13314 +13315 +13316 +13317 +13318 +13319 +13320 +13321 +13322 +13323 +13324 +13325 +13326 +13327 +13328 +13329 +13330 +13331 +13332 +13333 +13334 +13335 +13336 +13337 +13338 +13339 +13340 +13341 +13342 +13343 +13344 +13345 +13346 +13347 +13348 +13349 +13350 +13351 +13352 +13353 +13354 +13355 +13356 +13357 +13358 +13359 +13360 +13361 +13362 +13363 +13364 +13365 +13366 +13367 +13368 +13369 +13370 +13371 +13372 +13373 +13374 +13375 +13376 +13377 +13378 +13379 +13380 +13381 +13382 +13383 +13384 +13385 +13386 +13387 +13388 +13389 +13390 +13391 +13392 +13393 +13394 +13395 +13396 +13397 +13398 +13399 +13400 +13401 +13402 +13403 +13404 +13405 +13406 +13407 +13408 +13409 +13410 +13411 +13412 +13413 +13414 +13415 +13416 +13417 +13418 +13419 +13420 +13421 +13422 +13423 +13424 +13425 +13426 +13427 +13428 +13429 +13430 +13431 +13432 +13433 +13434 +13435 +13436 +13437 +13438 +13439 +13440 +13441 +13442 +13443 +13444 +13445 +13446 +13447 +13448 +13449 +13450 +13451 +13452 +13453 +13454 +13455 +13456 +13457 +13458 +13459 +13460 +13461 +13462 +13463 +13464 +13465 +13466 +13467 +13468 +13469 +13470 +13471 +13472 +13473 +13474 +13475 +13476 +13477 +13478 +13479 +13480 +13481 +13482 +13483 +13484 +13485 +13486 +13487 +13488 +13489 +13490 +13491 +13492 +13493 +13494 +13495 +13496 +13497 +13498 +13499 +13500 +13501 +13502 +13503 +13504 +13505 +13506 +13507 +13508 +13509 +13510 +13511 +13512 +13513 +13514 +13515 +13516 +13517 +13518 +13519 +13520 +13521 +13522 +13523 +13524 +13525 +13526 +13527 +13528 +13529 +13530 +13531 +13532 +13533 +13534 +13535 +13536 +13537 +13538 +13539 +13540 +13541 +13542 +13543 +13544 +13545 +13546 +13547 +13548 +13549 +13550 +13551 +13552 +13553 +13554 +13555 +13556 +13557 +13558 +13559 +13560 +13561 +13562 +13563 +13564 +13565 +13566 +13567 +13568 +13569 +13570 +13571 +13572 +13573 +13574 +13575 +13576 +13577 +13578 +13579 +13580 +13581 +13582 +13583 +13584 +13585 +13586 +13587 +13588 +13589 +13590 +13591 +13592 +13593 +13594 +13595 +13596 +13597 +13598 +13599 +13600 +13601 +13602 +13603 +13604 +13605 +13606 +13607 +13608 +13609 +13610 +13611 +13612 +13613 +13614 +13615 +13616 +13617 +13618 +13619 +13620 +13621 +13622 +13623 +13624 +13625 +13626 +13627 +13628 +13629 +13630 +13631 +13632 +13633 +13634 +13635 +13636 +13637 +13638 +13639 +13640 +13641 +13642 +13643 +13644 +13645 +13646 +13647 +13648 +13649 +13650 +13651 +13652 +13653 +13654 +13655 +13656 +13657 +13658 +13659 +13660 +13661 +13662 +13663 +13664 +13665 +13666 +13667 +13668 +13669 +13670 +13671 +13672 +13673 +13674 +13675 +13676 +13677 +13678 +13679 +13680 +13681 +13682 +13683 +13684 +13685 +13686 +13687 +13688 +13689 +13690 +13691 +13692 +13693 +13694 +13695 +13696 +13697 +13698 +13699 +13700 +13701 +13702 +13703 +13704 +13705 +13706 +13707 +13708 +13709 +13710 +13711 +13712 +13713 +13714 +13715 +13716 +13717 +13718 +13719 +13720 +13721 +13722 +13723 +13724 +13725 +13726 +13727 +13728 +13729 +13730 +13731 +13732 +13733 +13734 +13735 +13736 +13737 +13738 +13739 +13740 +13741 +13742 +13743 +13744 +13745 +13746 +13747 +13748 +13749 +13750 +13751 +13752 +13753 +13754 +13755 +13756 +13757 +13758 +13759 +13760 +13761 +13762 +13763 +13764 +13765 +13766 +13767 +13768 +13769 +13770 +13771 +13772 +13773 +13774 +13775 +13776 +13777 +13778 +13779 +13780 +13781 +13782 +13783 +13784 +13785 +13786 +13787 +13788 +13789 +13790 +13791 +13792 +13793 +13794 +13795 +13796 +13797 +13798 +13799 +13800 +13801 +13802 +13803 +13804 +13805 +13806 +13807 +13808 +13809 +13810 +13811 +13812 +13813 +13814 +13815 +13816 +13817 +13818 +13819 +13820 +13821 +13822 +13823 +13824 +13825 +13826 +13827 +13828 +13829 +13830 +13831 +13832 +13833 +13834 +13835 +13836 +13837 +13838 +13839 +13840 +13841 +13842 +13843 +13844 +13845 +13846 +13847 +13848 +13849 +13850 +13851 +13852 +13853 +13854 +13855 +13856 +13857 +13858 +13859 +13860 +13861 +13862 +13863 +13864 +13865 +13866 +13867 +13868 +13869 +13870 +13871 +13872 +13873 +13874 +13875 +13876 +13877 +13878 +13879 +13880 +13881 +13882 +13883 +13884 +13885 +13886 +13887 +13888 +13889 +13890 +13891 +13892 +13893 +13894 +13895 +13896 +13897 +13898 +13899 +13900 +13901 +13902 +13903 +13904 +13905 +13906 +13907 +13908 +13909 +13910 +13911 +13912 +13913 +13914 +13915 +13916 +13917 +13918 +13919 +13920 +13921 +13922 +13923 +13924 +13925 +13926 +13927 +13928 +13929 +13930 +13931 +13932 +13933 +13934 +13935 +13936 +13937 +13938 +13939 +13940 +13941 +13942 +13943 +13944 +13945 +13946 +13947 +13948 +13949 +13950 +13951 +13952 +13953 +13954 +13955 +13956 +13957 +13958 +13959 +13960 +13961 +13962 +13963 +13964 +13965 +13966 +13967 +13968 +13969 +13970 +13971 +13972 +13973 +13974 +13975 +13976 +13977 +13978 +13979 +13980 +13981 +13982 +13983 +13984 +13985 +13986 +13987 +13988 +13989 +13990 +13991 +13992 +13993 +13994 +13995 +13996 +13997 +13998 +13999 +14000 +14001 +14002 +14003 +14004 +14005 +14006 +14007 +14008 +14009 +14010 +14011 +14012 +14013 +14014 +14015 +14016 +14017 +14018 +14019 +14020 +14021 +14022 +14023 +14024 +14025 +14026 +14027 +14028 +14029 +14030 +14031 +14032 +14033 +14034 +14035 +14036 +14037 +14038 +14039 +14040 +14041 +14042 +14043 +14044 +14045 +14046 +14047 +14048 +14049 +14050 +14051 +14052 +14053 +14054 +14055 +14056 +14057 +14058 +14059 +14060 +14061 +14062 +14063 +14064 +14065 +14066 +14067 +14068 +14069 +14070 +14071 +14072 +14073 +14074 +14075 +14076 +14077 +14078 +14079 +14080 +14081 +14082 +14083 +14084 +14085 +14086 +14087 +14088 +14089 +14090 +14091 +14092 +14093 +14094 +14095 +14096 +14097 +14098 +14099 +14100 +14101 +14102 +14103 +14104 +14105 +14106 +14107 +14108 +14109 +14110 +14111 +14112 +14113 +14114 +14115 +14116 +14117 +14118 +14119 +14120 +14121 +14122 +14123 +14124 +14125 +14126 +14127 +14128 +14129 +14130 +14131 +14132 +14133 +14134 +14135 +14136 +14137 +14138 +14139 +14140 +14141 +14142 +14143 +14144 +14145 +14146 +14147 +14148 +14149 +14150 +14151 +14152 +14153 +14154 +14155 +14156 +14157 +14158 +14159 +14160 +14161 +14162 +14163 +14164 +14165 +14166 +14167 +14168 +14169 +14170 +14171 +14172 +14173 +14174 +14175 +14176 +14177 +14178 +14179 +14180 +14181 +14182 +14183 +14184 +14185 +14186 +14187 +14188 +14189 +14190 +14191 +14192 +14193 +14194 +14195 +14196 +14197 +14198 +14199 +14200 +14201 +14202 +14203 +14204 +14205 +14206 +14207 +14208 +14209 +14210 +14211 +14212 +14213 +14214 +14215 +14216 +14217 +14218 +14219 +14220 +14221 +14222 +14223 +14224 +14225 +14226 +14227 +14228 +14229 +14230 +14231 +14232 +14233 +14234 +14235 +14236 +14237 +14238 +14239 +14240 +14241 +14242 +14243 +14244 +14245 +14246 +14247 +14248 +14249 +14250 +14251 +14252 +14253 +14254 +14255 +14256 +14257 +14258 +14259 +14260 +14261 +14262 +14263 +14264 +14265 +14266 +14267 +14268 +14269 +14270 +14271 +14272 +14273 +14274 +14275 +14276 +14277 +14278 +14279 +14280 +14281 +14282 +14283 +14284 +14285 +14286 +14287 +14288 +14289 +14290 +14291 +14292 +14293 +14294 +14295 +14296 +14297 +14298 +14299 +14300 +14301 +14302 +14303 +14304 +14305 +14306 +14307 +14308 +14309 +14310 +14311 +14312 +14313 +14314 +14315 +14316 +14317 +14318 +14319 +14320 +14321 +14322 +14323 +14324 +14325 +14326 +14327 +14328 +14329 +14330 +14331 +14332 +14333 +14334 +14335 +14336 +14337 +14338 +14339 +14340 +14341 +14342 +14343 +14344 +14345 +14346 +14347 +14348 +14349 +14350 +14351 +14352 +14353 +14354 +14355 +14356 +14357 +14358 +14359 +14360 +14361 +14362 +14363 +14364 +14365 +14366 +14367 +14368 +14369 +14370 +14371 +14372 +14373 +14374 +14375 +14376 +14377 +14378 +14379 +14380 +14381 +14382 +14383 +14384 +14385 +14386 +14387 +14388 +14389 +14390 +14391 +14392 +14393 +14394 +14395 +14396 +14397 +14398 +14399 +14400 +14401 +14402 +14403 +14404 +14405 +14406 +14407 +14408 +14409 +14410 +14411 +14412 +14413 +14414 +14415 +14416 +14417 +14418 +14419 +14420 +14421 +14422 +14423 +14424 +14425 +14426 +14427 +14428 +14429 +14430 +14431 +14432 +14433 +14434 +14435 +14436 +14437 +14438 +14439 +14440 +14441 +14442 +14443 +14444 +14445 +14446 +14447 +14448 +14449 +14450 +14451 +14452 +14453 +14454 +14455 +14456 +14457 +14458 +14459 +14460 +14461 +14462 +14463 +14464 +14465 +14466 +14467 +14468 +14469 +14470 +14471 +14472 +14473 +14474 +14475 +14476 +14477 +14478 +14479 +14480 +14481 +14482 +14483 +14484 +14485 +14486 +14487 +14488 +14489 +14490 +14491 +14492 +14493 +14494 +14495 +14496 +14497 +14498 +14499 +14500 +14501 +14502 +14503 +14504 +14505 +14506 +14507 +14508 +14509 +14510 +14511 +14512 +14513 +14514 +14515 +14516 +14517 +14518 +14519 +14520 +14521 +14522 +14523 +14524 +14525 +14526 +14527 +14528 +14529 +14530 +14531 +14532 +14533 +14534 +14535 +14536 +14537 +14538 +14539 +14540 +14541 +14542 +14543 +14544 +14545 +14546 +14547 +14548 +14549 +14550 +14551 +14552 +14553 +14554 +14555 +14556 +14557 +14558 +14559 +14560 +14561 +14562 +14563 +14564 +14565 +14566 +14567 +14568 +14569 +14570 +14571 +14572 +14573 +14574 +14575 +14576 +14577 +14578 +14579 +14580 +14581 +14582 +14583 +14584 +14585 +14586 +14587 +14588 +14589 +14590 +14591 +14592 +14593 +14594 +14595 +14596 +14597 +14598 +14599 +14600 +14601 +14602 +14603 +14604 +14605 +14606 +14607 +14608 +14609 +14610 +14611 +14612 +14613 +14614 +14615 +14616 +14617 +14618 +14619 +14620 +14621 +14622 +14623 +14624 +14625 +14626 +14627 +14628 +14629 +14630 +14631 +14632 +14633 +14634 +14635 +14636 +14637 +14638 +14639 +14640 +14641 +14642 +14643 +14644 +14645 +14646 +14647 +14648 +14649 +14650 +14651 +14652 +14653 +14654 +14655 +14656 +14657 +14658 +14659 +14660 +14661 +14662 +14663 +14664 +14665 +14666 +14667 +14668 +14669 +14670 +14671 +14672 +14673 +14674 +14675 +14676 +14677 +14678 +14679 +14680 +14681 +14682 +14683 +14684 +14685 +14686 +14687 +14688 +14689 +14690 +14691 +14692 +14693 +14694 +14695 +14696 +14697 +14698 +14699 +14700 +14701 +14702 +14703 +14704 +14705 +14706 +14707 +14708 +14709 +14710 +14711 +14712 +14713 +14714 +14715 +14716 +14717 +14718 +14719 +14720 +14721 +14722 +14723 +14724 +14725 +14726 +14727 +14728 +14729 +14730 +14731 +14732 +14733 +14734 +14735 +14736 +14737 +14738 +14739 +14740 +14741 +14742 +14743 +14744 +14745 +14746 +14747 +14748 +14749 +14750 +14751 +14752 +14753 +14754 +14755 +14756 +14757 +14758 +14759 +14760 +14761 +14762 +14763 +14764 +14765 +14766 +14767 +14768 +14769 +14770 +14771 +14772 +14773 +14774 +14775 +14776 +14777 +14778 +14779 +14780 +14781 +14782 +14783 +14784 +14785 +14786 +14787 +14788 +14789 +14790 +14791 +14792 +14793 +14794 +14795 +14796 +14797 +14798 +14799 +14800 +14801 +14802 +14803 +14804 +14805 +14806 +14807 +14808 +14809 +14810 +14811 +14812 +14813 +14814 +14815 +14816 +14817 +14818 +14819 +14820 +14821 +14822 +14823 +14824 +14825 +14826 +14827 +14828 +14829 +14830 +14831 +14832 +14833 +14834 +14835 +14836 +14837 +14838 +14839 +14840 +14841 +14842 +14843 +14844 +14845 +14846 +14847 +14848 +14849 +14850 +14851 +14852 +14853 +14854 +14855 +14856 +14857 +14858 +14859 +14860 +14861 +14862 +14863 +14864 +14865 +14866 +14867 +14868 +14869 +14870 +14871 +14872 +14873 +14874 +14875 +14876 +14877 +14878 +14879 +14880 +14881 +14882 +14883 +14884 +14885 +14886 +14887 +14888 +14889 +14890 +14891 +14892 +14893 +14894 +14895 +14896 +14897 +14898 +14899 +14900 +14901 +14902 +14903 +14904 +14905 +14906 +14907 +14908 +14909 +14910 +14911 +14912 +14913 +14914 +14915 +14916 +14917 +14918 +14919 +14920 +14921 +14922 +14923 +14924 +14925 +14926 +14927 +14928 +14929 +14930 +14931 +14932 +14933 +14934 +14935 +14936 +14937 +14938 +14939 +14940 +14941 +14942 +14943 +14944 +14945 +14946 +14947 +14948 +14949 +14950 +14951 +14952 +14953 +14954 +14955 +14956 +14957 +14958 +14959 +14960 +14961 +14962 +14963 +14964 +14965 +14966 +14967 +14968 +14969 +14970 +14971 +14972 +14973 +14974 +14975 +14976 +14977 +14978 +14979 +14980 +14981 +14982 +14983 +14984 +14985 +14986 +14987 +14988 +14989 +14990 +14991 +14992 +14993 +14994 +14995 +14996 +14997 +14998 +14999 +15000 +15001 +15002 +15003 +15004 +15005 +15006 +15007 +15008 +15009 +15010 +15011 +15012 +15013 +15014 +15015 +15016 +15017 +15018 +15019 +15020 +15021 +15022 +15023 +15024 +15025 +15026 +15027 +15028 +15029 +15030 +15031 +15032 +15033 +15034 +15035 +15036 +15037 +15038 +15039 +15040 +15041 +15042 +15043 +15044 +15045 +15046 +15047 +15048 +15049 +15050 +15051 +15052 +15053 +15054 +15055 +15056 +15057 +15058 +15059 +15060 +15061 +15062 +15063 +15064 +15065 +15066 +15067 +15068 +15069 +15070 +15071 +15072 +15073 +15074 +15075 +15076 +15077 +15078 +15079 +15080 +15081 +15082 +15083 +15084 +15085 +15086 +15087 +15088 +15089 +15090 +15091 +15092 +15093 +15094 +15095 +15096 +15097 +15098 +15099 +15100 +15101 +15102 +15103 +15104 +15105 +15106 +15107 +15108 +15109 +15110 +15111 +15112 +15113 +15114 +15115 +15116 +15117 +15118 +15119 +15120 +15121 +15122 +15123 +15124 +15125 +15126 +15127 +15128 +15129 +15130 +15131 +15132 +15133 +15134 +15135 +15136 +15137 +15138 +15139 +15140 +15141 +15142 +15143 +15144 +15145 +15146 +15147 +15148 +15149 +15150 +15151 +15152 +15153 +15154 +15155 +15156 +15157 +15158 +15159 +15160 +15161 +15162 +15163 +15164 +15165 +15166 +15167 +15168 +15169 +15170 +15171 +15172 +15173 +15174 +15175 +15176 +15177 +15178 +15179 +15180 +15181 +15182 +15183 +15184 +15185 +15186 +15187 +15188 +15189 +15190 +15191 +15192 +15193 +15194 +15195 +15196 +15197 +15198 +15199 +15200 +15201 +15202 +15203 +15204 +15205 +15206 +15207 +15208 +15209 +15210 +15211 +15212 +15213 +15214 +15215 +15216 +15217 +15218 +15219 +15220 +15221 +15222 +15223 +15224 +15225 +15226 +15227 +15228 +15229 +15230 +15231 +15232 +15233 +15234 +15235 +15236 +15237 +15238 +15239 +15240 +15241 +15242 +15243 +15244 +15245 +15246 +15247 +15248 +15249 +15250 +15251 +15252 +15253 +15254 +15255 +15256 +15257 +15258 +15259 +15260 +15261 +15262 +15263 +15264 +15265 +15266 +15267 +15268 +15269 +15270 +15271 +15272 +15273 +15274 +15275 +15276 +15277 +15278 +15279 +15280 +15281 +15282 +15283 +15284 +15285 +15286 +15287 +15288 +15289 +15290 +15291 +15292 +15293 +15294 +15295 +15296 +15297 +15298 +15299 +15300 +15301 +15302 +15303 +15304 +15305 +15306 +15307 +15308 +15309 +15310 +15311 +15312 +15313 +15314 +15315 +15316 +15317 +15318 +15319 +15320 +15321 +15322 +15323 +15324 +15325 +15326 +15327 +15328 +15329 +15330 +15331 +15332 +15333 +15334 +15335 +15336 +15337 +15338 +15339 +15340 +15341 +15342 +15343 +15344 +15345 +15346 +15347 +15348 +15349 +15350 +15351 +15352 +15353 +15354 +15355 +15356 +15357 +15358 +15359 +15360 +15361 +15362 +15363 +15364 +15365 +15366 +15367 +15368 +15369 +15370 +15371 +15372 +15373 +15374 +15375 +15376 +15377 +15378 +15379 +15380 +15381 +15382 +15383 +15384 +15385 +15386 +15387 +15388 +15389 +15390 +15391 +15392 +15393 +15394 +15395 +15396 +15397 +15398 +15399 +15400 +15401 +15402 +15403 +15404 +15405 +15406 +15407 +15408 +15409 +15410 +15411 +15412 +15413 +15414 +15415 +15416 +15417 +15418 +15419 +15420 +15421 +15422 +15423 +15424 +15425 +15426 +15427 +15428 +15429 +15430 +15431 +15432 +15433 +15434 +15435 +15436 +15437 +15438 +15439 +15440 +15441 +15442 +15443 +15444 +15445 +15446 +15447 +15448 +15449 +15450 +15451 +15452 +15453 +15454 +15455 +15456 +15457 +15458 +15459 +15460 +15461 +15462 +15463 +15464 +15465 +15466 +15467 +15468 +15469 +15470 +15471 +15472 +15473 +15474 +15475 +15476 +15477 +15478 +15479 +15480 +15481 +15482 +15483 +15484 +15485 +15486 +15487 +15488 +15489 +15490 +15491 +15492 +15493 +15494 +15495 +15496 +15497 +15498 +15499 +15500 +15501 +15502 +15503 +15504 +15505 +15506 +15507 +15508 +15509 +15510 +15511 +15512 +15513 +15514 +15515 +15516 +15517 +15518 +15519 +15520 +15521 +15522 +15523 +15524 +15525 +15526 +15527 +15528 +15529 +15530 +15531 +15532 +15533 +15534 +15535 +15536 +15537 +15538 +15539 +15540 +15541 +15542 +15543 +15544 +15545 +15546 +15547 +15548 +15549 +15550 +15551 +15552 +15553 +15554 +15555 +15556 +15557 +15558 +15559 +15560 +15561 +15562 +15563 +15564 +15565 +15566 +15567 +15568 +15569 +15570 +15571 +15572 +15573 +15574 +15575 +15576 +15577 +15578 +15579 +15580 +15581 +15582 +15583 +15584 +15585 +15586 +15587 +15588 +15589 +15590 +15591 +15592 +15593 +15594 +15595 +15596 +15597 +15598 +15599 +15600 +15601 +15602 +15603 +15604 +15605 +15606 +15607 +15608 +15609 +15610 +15611 +15612 +15613 +15614 +15615 +15616 +15617 +15618 +15619 +15620 +15621 +15622 +15623 +15624 +15625 +15626 +15627 +15628 +15629 +15630 +15631 +15632 +15633 +15634 +15635 +15636 +15637 +15638 +15639 +15640 +15641 +15642 +15643 +15644 +15645 +15646 +15647 +15648 +15649 +15650 +15651 +15652 +15653 +15654 +15655 +15656 +15657 +15658 +15659 +15660 +15661 +15662 +15663 +15664 +15665 +15666 +15667 +15668 +15669 +15670 +15671 +15672 +15673 +15674 +15675 +15676 +15677 +15678 +15679 +15680 +15681 +15682 +15683 +15684 +15685 +15686 +15687 +15688 +15689 +15690 +15691 +15692 +15693 +15694 +15695 +15696 +15697 +15698 +15699 +15700 +15701 +15702 +15703 +15704 +15705 +15706 +15707 +15708 +15709 +15710 +15711 +15712 +15713 +15714 +15715 +15716 +15717 +15718 +15719 +15720 +15721 +15722 +15723 +15724 +15725 +15726 +15727 +15728 +15729 +15730 +15731 +15732 +15733 +15734 +15735 +15736 +15737 +15738 +15739 +15740 +15741 +15742 +15743 +15744 +15745 +15746 +15747 +15748 +15749 +15750 +15751 +15752 +15753 +15754 +15755 +15756 +15757 +15758 +15759 +15760 +15761 +15762 +15763 +15764 +15765 +15766 +15767 +15768 +15769 +15770 +15771 +15772 +15773 +15774 +15775 +15776 +15777 +15778 +15779 +15780 +15781 +15782 +15783 +15784 +15785 +15786 +15787 +15788 +15789 +15790 +15791 +15792 +15793 +15794 +15795 +15796 +15797 +15798 +15799 +15800 +15801 +15802 +15803 +15804 +15805 +15806 +15807 +15808 +15809 +15810 +15811 +15812 +15813 +15814 +15815 +15816 +15817 +15818 +15819 +15820 +15821 +15822 +15823 +15824 +15825 +15826 +15827 +15828 +15829 +15830 +15831 +15832 +15833 +15834 +15835 +15836 +15837 +15838 +15839 +15840 +15841 +15842 +15843 +15844 +15845 +15846 +15847 +15848 +15849 +15850 +15851 +15852 +15853 +15854 +15855 +15856 +15857 +15858 +15859 +15860 +15861 +15862 +15863 +15864 +15865 +15866 +15867 +15868 +15869 +15870 +15871 +15872 +15873 +15874 +15875 +15876 +15877 +15878 +15879 +15880 +15881 +15882 +15883 +15884 +15885 +15886 +15887 +15888 +15889 +15890 +15891 +15892 +15893 +15894 +15895 +15896 +15897 +15898 +15899 +15900 +15901 +15902 +15903 +15904 +15905 +15906 +15907 +15908 +15909 +15910 +15911 +15912 +15913 +15914 +15915 +15916 +15917 +15918 +15919 +15920 +15921 +15922 +15923 +15924 +15925 +15926 +15927 +15928 +15929 +15930 +15931 +15932 +15933 +15934 +15935 +15936 +15937 +15938 +15939 +15940 +15941 +15942 +15943 +15944 +15945 +15946 +15947 +15948 +15949 +15950 +15951 +15952 +15953 +15954 +15955 +15956 +15957 +15958 +15959 +15960 +15961 +15962 +15963 +15964 +15965 +15966 +15967 +15968 +15969 +15970 +15971 +15972 +15973 +15974 +15975 +15976 +15977 +15978 +15979 +15980 +15981 +15982 +15983 +15984 +15985 +15986 +15987 +15988 +15989 +15990 +15991 +15992 +15993 +15994 +15995 +15996 +15997 +15998 +15999 +16000 diff --git a/test/static/executor/test_data/departments_2000.csv b/test/static/executor/test_data/departments_2000.csv new file mode 100644 index 0000000..44c9617 --- /dev/null +++ b/test/static/executor/test_data/departments_2000.csv @@ -0,0 +1,2001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +1143 +1144 +1145 +1146 +1147 +1148 +1149 +1150 +1151 +1152 +1153 +1154 +1155 +1156 +1157 +1158 +1159 +1160 +1161 +1162 +1163 +1164 +1165 +1166 +1167 +1168 +1169 +1170 +1171 +1172 +1173 +1174 +1175 +1176 +1177 +1178 +1179 +1180 +1181 +1182 +1183 +1184 +1185 +1186 +1187 +1188 +1189 +1190 +1191 +1192 +1193 +1194 +1195 +1196 +1197 +1198 +1199 +1200 +1201 +1202 +1203 +1204 +1205 +1206 +1207 +1208 +1209 +1210 +1211 +1212 +1213 +1214 +1215 +1216 +1217 +1218 +1219 +1220 +1221 +1222 +1223 +1224 +1225 +1226 +1227 +1228 +1229 +1230 +1231 +1232 +1233 +1234 +1235 +1236 +1237 +1238 +1239 +1240 +1241 +1242 +1243 +1244 +1245 +1246 +1247 +1248 +1249 +1250 +1251 +1252 +1253 +1254 +1255 +1256 +1257 +1258 +1259 +1260 +1261 +1262 +1263 +1264 +1265 +1266 +1267 +1268 +1269 +1270 +1271 +1272 +1273 +1274 +1275 +1276 +1277 +1278 +1279 +1280 +1281 +1282 +1283 +1284 +1285 +1286 +1287 +1288 +1289 +1290 +1291 +1292 +1293 +1294 +1295 +1296 +1297 +1298 +1299 +1300 +1301 +1302 +1303 +1304 +1305 +1306 +1307 +1308 +1309 +1310 +1311 +1312 +1313 +1314 +1315 +1316 +1317 +1318 +1319 +1320 +1321 +1322 +1323 +1324 +1325 +1326 +1327 +1328 +1329 +1330 +1331 +1332 +1333 +1334 +1335 +1336 +1337 +1338 +1339 +1340 +1341 +1342 +1343 +1344 +1345 +1346 +1347 +1348 +1349 +1350 +1351 +1352 +1353 +1354 +1355 +1356 +1357 +1358 +1359 +1360 +1361 +1362 +1363 +1364 +1365 +1366 +1367 +1368 +1369 +1370 +1371 +1372 +1373 +1374 +1375 +1376 +1377 +1378 +1379 +1380 +1381 +1382 +1383 +1384 +1385 +1386 +1387 +1388 +1389 +1390 +1391 +1392 +1393 +1394 +1395 +1396 +1397 +1398 +1399 +1400 +1401 +1402 +1403 +1404 +1405 +1406 +1407 +1408 +1409 +1410 +1411 +1412 +1413 +1414 +1415 +1416 +1417 +1418 +1419 +1420 +1421 +1422 +1423 +1424 +1425 +1426 +1427 +1428 +1429 +1430 +1431 +1432 +1433 +1434 +1435 +1436 +1437 +1438 +1439 +1440 +1441 +1442 +1443 +1444 +1445 +1446 +1447 +1448 +1449 +1450 +1451 +1452 +1453 +1454 +1455 +1456 +1457 +1458 +1459 +1460 +1461 +1462 +1463 +1464 +1465 +1466 +1467 +1468 +1469 +1470 +1471 +1472 +1473 +1474 +1475 +1476 +1477 +1478 +1479 +1480 +1481 +1482 +1483 +1484 +1485 +1486 +1487 +1488 +1489 +1490 +1491 +1492 +1493 +1494 +1495 +1496 +1497 +1498 +1499 +1500 +1501 +1502 +1503 +1504 +1505 +1506 +1507 +1508 +1509 +1510 +1511 +1512 +1513 +1514 +1515 +1516 +1517 +1518 +1519 +1520 +1521 +1522 +1523 +1524 +1525 +1526 +1527 +1528 +1529 +1530 +1531 +1532 +1533 +1534 +1535 +1536 +1537 +1538 +1539 +1540 +1541 +1542 +1543 +1544 +1545 +1546 +1547 +1548 +1549 +1550 +1551 +1552 +1553 +1554 +1555 +1556 +1557 +1558 +1559 +1560 +1561 +1562 +1563 +1564 +1565 +1566 +1567 +1568 +1569 +1570 +1571 +1572 +1573 +1574 +1575 +1576 +1577 +1578 +1579 +1580 +1581 +1582 +1583 +1584 +1585 +1586 +1587 +1588 +1589 +1590 +1591 +1592 +1593 +1594 +1595 +1596 +1597 +1598 +1599 +1600 +1601 +1602 +1603 +1604 +1605 +1606 +1607 +1608 +1609 +1610 +1611 +1612 +1613 +1614 +1615 +1616 +1617 +1618 +1619 +1620 +1621 +1622 +1623 +1624 +1625 +1626 +1627 +1628 +1629 +1630 +1631 +1632 +1633 +1634 +1635 +1636 +1637 +1638 +1639 +1640 +1641 +1642 +1643 +1644 +1645 +1646 +1647 +1648 +1649 +1650 +1651 +1652 +1653 +1654 +1655 +1656 +1657 +1658 +1659 +1660 +1661 +1662 +1663 +1664 +1665 +1666 +1667 +1668 +1669 +1670 +1671 +1672 +1673 +1674 +1675 +1676 +1677 +1678 +1679 +1680 +1681 +1682 +1683 +1684 +1685 +1686 +1687 +1688 +1689 +1690 +1691 +1692 +1693 +1694 +1695 +1696 +1697 +1698 +1699 +1700 +1701 +1702 +1703 +1704 +1705 +1706 +1707 +1708 +1709 +1710 +1711 +1712 +1713 +1714 +1715 +1716 +1717 +1718 +1719 +1720 +1721 +1722 +1723 +1724 +1725 +1726 +1727 +1728 +1729 +1730 +1731 +1732 +1733 +1734 +1735 +1736 +1737 +1738 +1739 +1740 +1741 +1742 +1743 +1744 +1745 +1746 +1747 +1748 +1749 +1750 +1751 +1752 +1753 +1754 +1755 +1756 +1757 +1758 +1759 +1760 +1761 +1762 +1763 +1764 +1765 +1766 +1767 +1768 +1769 +1770 +1771 +1772 +1773 +1774 +1775 +1776 +1777 +1778 +1779 +1780 +1781 +1782 +1783 +1784 +1785 +1786 +1787 +1788 +1789 +1790 +1791 +1792 +1793 +1794 +1795 +1796 +1797 +1798 +1799 +1800 +1801 +1802 +1803 +1804 +1805 +1806 +1807 +1808 +1809 +1810 +1811 +1812 +1813 +1814 +1815 +1816 +1817 +1818 +1819 +1820 +1821 +1822 +1823 +1824 +1825 +1826 +1827 +1828 +1829 +1830 +1831 +1832 +1833 +1834 +1835 +1836 +1837 +1838 +1839 +1840 +1841 +1842 +1843 +1844 +1845 +1846 +1847 +1848 +1849 +1850 +1851 +1852 +1853 +1854 +1855 +1856 +1857 +1858 +1859 +1860 +1861 +1862 +1863 +1864 +1865 +1866 +1867 +1868 +1869 +1870 +1871 +1872 +1873 +1874 +1875 +1876 +1877 +1878 +1879 +1880 +1881 +1882 +1883 +1884 +1885 +1886 +1887 +1888 +1889 +1890 +1891 +1892 +1893 +1894 +1895 +1896 +1897 +1898 +1899 +1900 +1901 +1902 +1903 +1904 +1905 +1906 +1907 +1908 +1909 +1910 +1911 +1912 +1913 +1914 +1915 +1916 +1917 +1918 +1919 +1920 +1921 +1922 +1923 +1924 +1925 +1926 +1927 +1928 +1929 +1930 +1931 +1932 +1933 +1934 +1935 +1936 +1937 +1938 +1939 +1940 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +1965 +1966 +1967 +1968 +1969 +1970 +1971 +1972 +1973 +1974 +1975 +1976 +1977 +1978 +1979 +1980 +1981 +1982 +1983 +1984 +1985 +1986 +1987 +1988 +1989 +1990 +1991 +1992 +1993 +1994 +1995 +1996 +1997 +1998 +1999 +2000 diff --git a/test/static/executor/test_data/departments_4000.csv b/test/static/executor/test_data/departments_4000.csv new file mode 100644 index 0000000..8c9b3d1 --- /dev/null +++ b/test/static/executor/test_data/departments_4000.csv @@ -0,0 +1,4001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +1143 +1144 +1145 +1146 +1147 +1148 +1149 +1150 +1151 +1152 +1153 +1154 +1155 +1156 +1157 +1158 +1159 +1160 +1161 +1162 +1163 +1164 +1165 +1166 +1167 +1168 +1169 +1170 +1171 +1172 +1173 +1174 +1175 +1176 +1177 +1178 +1179 +1180 +1181 +1182 +1183 +1184 +1185 +1186 +1187 +1188 +1189 +1190 +1191 +1192 +1193 +1194 +1195 +1196 +1197 +1198 +1199 +1200 +1201 +1202 +1203 +1204 +1205 +1206 +1207 +1208 +1209 +1210 +1211 +1212 +1213 +1214 +1215 +1216 +1217 +1218 +1219 +1220 +1221 +1222 +1223 +1224 +1225 +1226 +1227 +1228 +1229 +1230 +1231 +1232 +1233 +1234 +1235 +1236 +1237 +1238 +1239 +1240 +1241 +1242 +1243 +1244 +1245 +1246 +1247 +1248 +1249 +1250 +1251 +1252 +1253 +1254 +1255 +1256 +1257 +1258 +1259 +1260 +1261 +1262 +1263 +1264 +1265 +1266 +1267 +1268 +1269 +1270 +1271 +1272 +1273 +1274 +1275 +1276 +1277 +1278 +1279 +1280 +1281 +1282 +1283 +1284 +1285 +1286 +1287 +1288 +1289 +1290 +1291 +1292 +1293 +1294 +1295 +1296 +1297 +1298 +1299 +1300 +1301 +1302 +1303 +1304 +1305 +1306 +1307 +1308 +1309 +1310 +1311 +1312 +1313 +1314 +1315 +1316 +1317 +1318 +1319 +1320 +1321 +1322 +1323 +1324 +1325 +1326 +1327 +1328 +1329 +1330 +1331 +1332 +1333 +1334 +1335 +1336 +1337 +1338 +1339 +1340 +1341 +1342 +1343 +1344 +1345 +1346 +1347 +1348 +1349 +1350 +1351 +1352 +1353 +1354 +1355 +1356 +1357 +1358 +1359 +1360 +1361 +1362 +1363 +1364 +1365 +1366 +1367 +1368 +1369 +1370 +1371 +1372 +1373 +1374 +1375 +1376 +1377 +1378 +1379 +1380 +1381 +1382 +1383 +1384 +1385 +1386 +1387 +1388 +1389 +1390 +1391 +1392 +1393 +1394 +1395 +1396 +1397 +1398 +1399 +1400 +1401 +1402 +1403 +1404 +1405 +1406 +1407 +1408 +1409 +1410 +1411 +1412 +1413 +1414 +1415 +1416 +1417 +1418 +1419 +1420 +1421 +1422 +1423 +1424 +1425 +1426 +1427 +1428 +1429 +1430 +1431 +1432 +1433 +1434 +1435 +1436 +1437 +1438 +1439 +1440 +1441 +1442 +1443 +1444 +1445 +1446 +1447 +1448 +1449 +1450 +1451 +1452 +1453 +1454 +1455 +1456 +1457 +1458 +1459 +1460 +1461 +1462 +1463 +1464 +1465 +1466 +1467 +1468 +1469 +1470 +1471 +1472 +1473 +1474 +1475 +1476 +1477 +1478 +1479 +1480 +1481 +1482 +1483 +1484 +1485 +1486 +1487 +1488 +1489 +1490 +1491 +1492 +1493 +1494 +1495 +1496 +1497 +1498 +1499 +1500 +1501 +1502 +1503 +1504 +1505 +1506 +1507 +1508 +1509 +1510 +1511 +1512 +1513 +1514 +1515 +1516 +1517 +1518 +1519 +1520 +1521 +1522 +1523 +1524 +1525 +1526 +1527 +1528 +1529 +1530 +1531 +1532 +1533 +1534 +1535 +1536 +1537 +1538 +1539 +1540 +1541 +1542 +1543 +1544 +1545 +1546 +1547 +1548 +1549 +1550 +1551 +1552 +1553 +1554 +1555 +1556 +1557 +1558 +1559 +1560 +1561 +1562 +1563 +1564 +1565 +1566 +1567 +1568 +1569 +1570 +1571 +1572 +1573 +1574 +1575 +1576 +1577 +1578 +1579 +1580 +1581 +1582 +1583 +1584 +1585 +1586 +1587 +1588 +1589 +1590 +1591 +1592 +1593 +1594 +1595 +1596 +1597 +1598 +1599 +1600 +1601 +1602 +1603 +1604 +1605 +1606 +1607 +1608 +1609 +1610 +1611 +1612 +1613 +1614 +1615 +1616 +1617 +1618 +1619 +1620 +1621 +1622 +1623 +1624 +1625 +1626 +1627 +1628 +1629 +1630 +1631 +1632 +1633 +1634 +1635 +1636 +1637 +1638 +1639 +1640 +1641 +1642 +1643 +1644 +1645 +1646 +1647 +1648 +1649 +1650 +1651 +1652 +1653 +1654 +1655 +1656 +1657 +1658 +1659 +1660 +1661 +1662 +1663 +1664 +1665 +1666 +1667 +1668 +1669 +1670 +1671 +1672 +1673 +1674 +1675 +1676 +1677 +1678 +1679 +1680 +1681 +1682 +1683 +1684 +1685 +1686 +1687 +1688 +1689 +1690 +1691 +1692 +1693 +1694 +1695 +1696 +1697 +1698 +1699 +1700 +1701 +1702 +1703 +1704 +1705 +1706 +1707 +1708 +1709 +1710 +1711 +1712 +1713 +1714 +1715 +1716 +1717 +1718 +1719 +1720 +1721 +1722 +1723 +1724 +1725 +1726 +1727 +1728 +1729 +1730 +1731 +1732 +1733 +1734 +1735 +1736 +1737 +1738 +1739 +1740 +1741 +1742 +1743 +1744 +1745 +1746 +1747 +1748 +1749 +1750 +1751 +1752 +1753 +1754 +1755 +1756 +1757 +1758 +1759 +1760 +1761 +1762 +1763 +1764 +1765 +1766 +1767 +1768 +1769 +1770 +1771 +1772 +1773 +1774 +1775 +1776 +1777 +1778 +1779 +1780 +1781 +1782 +1783 +1784 +1785 +1786 +1787 +1788 +1789 +1790 +1791 +1792 +1793 +1794 +1795 +1796 +1797 +1798 +1799 +1800 +1801 +1802 +1803 +1804 +1805 +1806 +1807 +1808 +1809 +1810 +1811 +1812 +1813 +1814 +1815 +1816 +1817 +1818 +1819 +1820 +1821 +1822 +1823 +1824 +1825 +1826 +1827 +1828 +1829 +1830 +1831 +1832 +1833 +1834 +1835 +1836 +1837 +1838 +1839 +1840 +1841 +1842 +1843 +1844 +1845 +1846 +1847 +1848 +1849 +1850 +1851 +1852 +1853 +1854 +1855 +1856 +1857 +1858 +1859 +1860 +1861 +1862 +1863 +1864 +1865 +1866 +1867 +1868 +1869 +1870 +1871 +1872 +1873 +1874 +1875 +1876 +1877 +1878 +1879 +1880 +1881 +1882 +1883 +1884 +1885 +1886 +1887 +1888 +1889 +1890 +1891 +1892 +1893 +1894 +1895 +1896 +1897 +1898 +1899 +1900 +1901 +1902 +1903 +1904 +1905 +1906 +1907 +1908 +1909 +1910 +1911 +1912 +1913 +1914 +1915 +1916 +1917 +1918 +1919 +1920 +1921 +1922 +1923 +1924 +1925 +1926 +1927 +1928 +1929 +1930 +1931 +1932 +1933 +1934 +1935 +1936 +1937 +1938 +1939 +1940 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +1965 +1966 +1967 +1968 +1969 +1970 +1971 +1972 +1973 +1974 +1975 +1976 +1977 +1978 +1979 +1980 +1981 +1982 +1983 +1984 +1985 +1986 +1987 +1988 +1989 +1990 +1991 +1992 +1993 +1994 +1995 +1996 +1997 +1998 +1999 +2000 +2001 +2002 +2003 +2004 +2005 +2006 +2007 +2008 +2009 +2010 +2011 +2012 +2013 +2014 +2015 +2016 +2017 +2018 +2019 +2020 +2021 +2022 +2023 +2024 +2025 +2026 +2027 +2028 +2029 +2030 +2031 +2032 +2033 +2034 +2035 +2036 +2037 +2038 +2039 +2040 +2041 +2042 +2043 +2044 +2045 +2046 +2047 +2048 +2049 +2050 +2051 +2052 +2053 +2054 +2055 +2056 +2057 +2058 +2059 +2060 +2061 +2062 +2063 +2064 +2065 +2066 +2067 +2068 +2069 +2070 +2071 +2072 +2073 +2074 +2075 +2076 +2077 +2078 +2079 +2080 +2081 +2082 +2083 +2084 +2085 +2086 +2087 +2088 +2089 +2090 +2091 +2092 +2093 +2094 +2095 +2096 +2097 +2098 +2099 +2100 +2101 +2102 +2103 +2104 +2105 +2106 +2107 +2108 +2109 +2110 +2111 +2112 +2113 +2114 +2115 +2116 +2117 +2118 +2119 +2120 +2121 +2122 +2123 +2124 +2125 +2126 +2127 +2128 +2129 +2130 +2131 +2132 +2133 +2134 +2135 +2136 +2137 +2138 +2139 +2140 +2141 +2142 +2143 +2144 +2145 +2146 +2147 +2148 +2149 +2150 +2151 +2152 +2153 +2154 +2155 +2156 +2157 +2158 +2159 +2160 +2161 +2162 +2163 +2164 +2165 +2166 +2167 +2168 +2169 +2170 +2171 +2172 +2173 +2174 +2175 +2176 +2177 +2178 +2179 +2180 +2181 +2182 +2183 +2184 +2185 +2186 +2187 +2188 +2189 +2190 +2191 +2192 +2193 +2194 +2195 +2196 +2197 +2198 +2199 +2200 +2201 +2202 +2203 +2204 +2205 +2206 +2207 +2208 +2209 +2210 +2211 +2212 +2213 +2214 +2215 +2216 +2217 +2218 +2219 +2220 +2221 +2222 +2223 +2224 +2225 +2226 +2227 +2228 +2229 +2230 +2231 +2232 +2233 +2234 +2235 +2236 +2237 +2238 +2239 +2240 +2241 +2242 +2243 +2244 +2245 +2246 +2247 +2248 +2249 +2250 +2251 +2252 +2253 +2254 +2255 +2256 +2257 +2258 +2259 +2260 +2261 +2262 +2263 +2264 +2265 +2266 +2267 +2268 +2269 +2270 +2271 +2272 +2273 +2274 +2275 +2276 +2277 +2278 +2279 +2280 +2281 +2282 +2283 +2284 +2285 +2286 +2287 +2288 +2289 +2290 +2291 +2292 +2293 +2294 +2295 +2296 +2297 +2298 +2299 +2300 +2301 +2302 +2303 +2304 +2305 +2306 +2307 +2308 +2309 +2310 +2311 +2312 +2313 +2314 +2315 +2316 +2317 +2318 +2319 +2320 +2321 +2322 +2323 +2324 +2325 +2326 +2327 +2328 +2329 +2330 +2331 +2332 +2333 +2334 +2335 +2336 +2337 +2338 +2339 +2340 +2341 +2342 +2343 +2344 +2345 +2346 +2347 +2348 +2349 +2350 +2351 +2352 +2353 +2354 +2355 +2356 +2357 +2358 +2359 +2360 +2361 +2362 +2363 +2364 +2365 +2366 +2367 +2368 +2369 +2370 +2371 +2372 +2373 +2374 +2375 +2376 +2377 +2378 +2379 +2380 +2381 +2382 +2383 +2384 +2385 +2386 +2387 +2388 +2389 +2390 +2391 +2392 +2393 +2394 +2395 +2396 +2397 +2398 +2399 +2400 +2401 +2402 +2403 +2404 +2405 +2406 +2407 +2408 +2409 +2410 +2411 +2412 +2413 +2414 +2415 +2416 +2417 +2418 +2419 +2420 +2421 +2422 +2423 +2424 +2425 +2426 +2427 +2428 +2429 +2430 +2431 +2432 +2433 +2434 +2435 +2436 +2437 +2438 +2439 +2440 +2441 +2442 +2443 +2444 +2445 +2446 +2447 +2448 +2449 +2450 +2451 +2452 +2453 +2454 +2455 +2456 +2457 +2458 +2459 +2460 +2461 +2462 +2463 +2464 +2465 +2466 +2467 +2468 +2469 +2470 +2471 +2472 +2473 +2474 +2475 +2476 +2477 +2478 +2479 +2480 +2481 +2482 +2483 +2484 +2485 +2486 +2487 +2488 +2489 +2490 +2491 +2492 +2493 +2494 +2495 +2496 +2497 +2498 +2499 +2500 +2501 +2502 +2503 +2504 +2505 +2506 +2507 +2508 +2509 +2510 +2511 +2512 +2513 +2514 +2515 +2516 +2517 +2518 +2519 +2520 +2521 +2522 +2523 +2524 +2525 +2526 +2527 +2528 +2529 +2530 +2531 +2532 +2533 +2534 +2535 +2536 +2537 +2538 +2539 +2540 +2541 +2542 +2543 +2544 +2545 +2546 +2547 +2548 +2549 +2550 +2551 +2552 +2553 +2554 +2555 +2556 +2557 +2558 +2559 +2560 +2561 +2562 +2563 +2564 +2565 +2566 +2567 +2568 +2569 +2570 +2571 +2572 +2573 +2574 +2575 +2576 +2577 +2578 +2579 +2580 +2581 +2582 +2583 +2584 +2585 +2586 +2587 +2588 +2589 +2590 +2591 +2592 +2593 +2594 +2595 +2596 +2597 +2598 +2599 +2600 +2601 +2602 +2603 +2604 +2605 +2606 +2607 +2608 +2609 +2610 +2611 +2612 +2613 +2614 +2615 +2616 +2617 +2618 +2619 +2620 +2621 +2622 +2623 +2624 +2625 +2626 +2627 +2628 +2629 +2630 +2631 +2632 +2633 +2634 +2635 +2636 +2637 +2638 +2639 +2640 +2641 +2642 +2643 +2644 +2645 +2646 +2647 +2648 +2649 +2650 +2651 +2652 +2653 +2654 +2655 +2656 +2657 +2658 +2659 +2660 +2661 +2662 +2663 +2664 +2665 +2666 +2667 +2668 +2669 +2670 +2671 +2672 +2673 +2674 +2675 +2676 +2677 +2678 +2679 +2680 +2681 +2682 +2683 +2684 +2685 +2686 +2687 +2688 +2689 +2690 +2691 +2692 +2693 +2694 +2695 +2696 +2697 +2698 +2699 +2700 +2701 +2702 +2703 +2704 +2705 +2706 +2707 +2708 +2709 +2710 +2711 +2712 +2713 +2714 +2715 +2716 +2717 +2718 +2719 +2720 +2721 +2722 +2723 +2724 +2725 +2726 +2727 +2728 +2729 +2730 +2731 +2732 +2733 +2734 +2735 +2736 +2737 +2738 +2739 +2740 +2741 +2742 +2743 +2744 +2745 +2746 +2747 +2748 +2749 +2750 +2751 +2752 +2753 +2754 +2755 +2756 +2757 +2758 +2759 +2760 +2761 +2762 +2763 +2764 +2765 +2766 +2767 +2768 +2769 +2770 +2771 +2772 +2773 +2774 +2775 +2776 +2777 +2778 +2779 +2780 +2781 +2782 +2783 +2784 +2785 +2786 +2787 +2788 +2789 +2790 +2791 +2792 +2793 +2794 +2795 +2796 +2797 +2798 +2799 +2800 +2801 +2802 +2803 +2804 +2805 +2806 +2807 +2808 +2809 +2810 +2811 +2812 +2813 +2814 +2815 +2816 +2817 +2818 +2819 +2820 +2821 +2822 +2823 +2824 +2825 +2826 +2827 +2828 +2829 +2830 +2831 +2832 +2833 +2834 +2835 +2836 +2837 +2838 +2839 +2840 +2841 +2842 +2843 +2844 +2845 +2846 +2847 +2848 +2849 +2850 +2851 +2852 +2853 +2854 +2855 +2856 +2857 +2858 +2859 +2860 +2861 +2862 +2863 +2864 +2865 +2866 +2867 +2868 +2869 +2870 +2871 +2872 +2873 +2874 +2875 +2876 +2877 +2878 +2879 +2880 +2881 +2882 +2883 +2884 +2885 +2886 +2887 +2888 +2889 +2890 +2891 +2892 +2893 +2894 +2895 +2896 +2897 +2898 +2899 +2900 +2901 +2902 +2903 +2904 +2905 +2906 +2907 +2908 +2909 +2910 +2911 +2912 +2913 +2914 +2915 +2916 +2917 +2918 +2919 +2920 +2921 +2922 +2923 +2924 +2925 +2926 +2927 +2928 +2929 +2930 +2931 +2932 +2933 +2934 +2935 +2936 +2937 +2938 +2939 +2940 +2941 +2942 +2943 +2944 +2945 +2946 +2947 +2948 +2949 +2950 +2951 +2952 +2953 +2954 +2955 +2956 +2957 +2958 +2959 +2960 +2961 +2962 +2963 +2964 +2965 +2966 +2967 +2968 +2969 +2970 +2971 +2972 +2973 +2974 +2975 +2976 +2977 +2978 +2979 +2980 +2981 +2982 +2983 +2984 +2985 +2986 +2987 +2988 +2989 +2990 +2991 +2992 +2993 +2994 +2995 +2996 +2997 +2998 +2999 +3000 +3001 +3002 +3003 +3004 +3005 +3006 +3007 +3008 +3009 +3010 +3011 +3012 +3013 +3014 +3015 +3016 +3017 +3018 +3019 +3020 +3021 +3022 +3023 +3024 +3025 +3026 +3027 +3028 +3029 +3030 +3031 +3032 +3033 +3034 +3035 +3036 +3037 +3038 +3039 +3040 +3041 +3042 +3043 +3044 +3045 +3046 +3047 +3048 +3049 +3050 +3051 +3052 +3053 +3054 +3055 +3056 +3057 +3058 +3059 +3060 +3061 +3062 +3063 +3064 +3065 +3066 +3067 +3068 +3069 +3070 +3071 +3072 +3073 +3074 +3075 +3076 +3077 +3078 +3079 +3080 +3081 +3082 +3083 +3084 +3085 +3086 +3087 +3088 +3089 +3090 +3091 +3092 +3093 +3094 +3095 +3096 +3097 +3098 +3099 +3100 +3101 +3102 +3103 +3104 +3105 +3106 +3107 +3108 +3109 +3110 +3111 +3112 +3113 +3114 +3115 +3116 +3117 +3118 +3119 +3120 +3121 +3122 +3123 +3124 +3125 +3126 +3127 +3128 +3129 +3130 +3131 +3132 +3133 +3134 +3135 +3136 +3137 +3138 +3139 +3140 +3141 +3142 +3143 +3144 +3145 +3146 +3147 +3148 +3149 +3150 +3151 +3152 +3153 +3154 +3155 +3156 +3157 +3158 +3159 +3160 +3161 +3162 +3163 +3164 +3165 +3166 +3167 +3168 +3169 +3170 +3171 +3172 +3173 +3174 +3175 +3176 +3177 +3178 +3179 +3180 +3181 +3182 +3183 +3184 +3185 +3186 +3187 +3188 +3189 +3190 +3191 +3192 +3193 +3194 +3195 +3196 +3197 +3198 +3199 +3200 +3201 +3202 +3203 +3204 +3205 +3206 +3207 +3208 +3209 +3210 +3211 +3212 +3213 +3214 +3215 +3216 +3217 +3218 +3219 +3220 +3221 +3222 +3223 +3224 +3225 +3226 +3227 +3228 +3229 +3230 +3231 +3232 +3233 +3234 +3235 +3236 +3237 +3238 +3239 +3240 +3241 +3242 +3243 +3244 +3245 +3246 +3247 +3248 +3249 +3250 +3251 +3252 +3253 +3254 +3255 +3256 +3257 +3258 +3259 +3260 +3261 +3262 +3263 +3264 +3265 +3266 +3267 +3268 +3269 +3270 +3271 +3272 +3273 +3274 +3275 +3276 +3277 +3278 +3279 +3280 +3281 +3282 +3283 +3284 +3285 +3286 +3287 +3288 +3289 +3290 +3291 +3292 +3293 +3294 +3295 +3296 +3297 +3298 +3299 +3300 +3301 +3302 +3303 +3304 +3305 +3306 +3307 +3308 +3309 +3310 +3311 +3312 +3313 +3314 +3315 +3316 +3317 +3318 +3319 +3320 +3321 +3322 +3323 +3324 +3325 +3326 +3327 +3328 +3329 +3330 +3331 +3332 +3333 +3334 +3335 +3336 +3337 +3338 +3339 +3340 +3341 +3342 +3343 +3344 +3345 +3346 +3347 +3348 +3349 +3350 +3351 +3352 +3353 +3354 +3355 +3356 +3357 +3358 +3359 +3360 +3361 +3362 +3363 +3364 +3365 +3366 +3367 +3368 +3369 +3370 +3371 +3372 +3373 +3374 +3375 +3376 +3377 +3378 +3379 +3380 +3381 +3382 +3383 +3384 +3385 +3386 +3387 +3388 +3389 +3390 +3391 +3392 +3393 +3394 +3395 +3396 +3397 +3398 +3399 +3400 +3401 +3402 +3403 +3404 +3405 +3406 +3407 +3408 +3409 +3410 +3411 +3412 +3413 +3414 +3415 +3416 +3417 +3418 +3419 +3420 +3421 +3422 +3423 +3424 +3425 +3426 +3427 +3428 +3429 +3430 +3431 +3432 +3433 +3434 +3435 +3436 +3437 +3438 +3439 +3440 +3441 +3442 +3443 +3444 +3445 +3446 +3447 +3448 +3449 +3450 +3451 +3452 +3453 +3454 +3455 +3456 +3457 +3458 +3459 +3460 +3461 +3462 +3463 +3464 +3465 +3466 +3467 +3468 +3469 +3470 +3471 +3472 +3473 +3474 +3475 +3476 +3477 +3478 +3479 +3480 +3481 +3482 +3483 +3484 +3485 +3486 +3487 +3488 +3489 +3490 +3491 +3492 +3493 +3494 +3495 +3496 +3497 +3498 +3499 +3500 +3501 +3502 +3503 +3504 +3505 +3506 +3507 +3508 +3509 +3510 +3511 +3512 +3513 +3514 +3515 +3516 +3517 +3518 +3519 +3520 +3521 +3522 +3523 +3524 +3525 +3526 +3527 +3528 +3529 +3530 +3531 +3532 +3533 +3534 +3535 +3536 +3537 +3538 +3539 +3540 +3541 +3542 +3543 +3544 +3545 +3546 +3547 +3548 +3549 +3550 +3551 +3552 +3553 +3554 +3555 +3556 +3557 +3558 +3559 +3560 +3561 +3562 +3563 +3564 +3565 +3566 +3567 +3568 +3569 +3570 +3571 +3572 +3573 +3574 +3575 +3576 +3577 +3578 +3579 +3580 +3581 +3582 +3583 +3584 +3585 +3586 +3587 +3588 +3589 +3590 +3591 +3592 +3593 +3594 +3595 +3596 +3597 +3598 +3599 +3600 +3601 +3602 +3603 +3604 +3605 +3606 +3607 +3608 +3609 +3610 +3611 +3612 +3613 +3614 +3615 +3616 +3617 +3618 +3619 +3620 +3621 +3622 +3623 +3624 +3625 +3626 +3627 +3628 +3629 +3630 +3631 +3632 +3633 +3634 +3635 +3636 +3637 +3638 +3639 +3640 +3641 +3642 +3643 +3644 +3645 +3646 +3647 +3648 +3649 +3650 +3651 +3652 +3653 +3654 +3655 +3656 +3657 +3658 +3659 +3660 +3661 +3662 +3663 +3664 +3665 +3666 +3667 +3668 +3669 +3670 +3671 +3672 +3673 +3674 +3675 +3676 +3677 +3678 +3679 +3680 +3681 +3682 +3683 +3684 +3685 +3686 +3687 +3688 +3689 +3690 +3691 +3692 +3693 +3694 +3695 +3696 +3697 +3698 +3699 +3700 +3701 +3702 +3703 +3704 +3705 +3706 +3707 +3708 +3709 +3710 +3711 +3712 +3713 +3714 +3715 +3716 +3717 +3718 +3719 +3720 +3721 +3722 +3723 +3724 +3725 +3726 +3727 +3728 +3729 +3730 +3731 +3732 +3733 +3734 +3735 +3736 +3737 +3738 +3739 +3740 +3741 +3742 +3743 +3744 +3745 +3746 +3747 +3748 +3749 +3750 +3751 +3752 +3753 +3754 +3755 +3756 +3757 +3758 +3759 +3760 +3761 +3762 +3763 +3764 +3765 +3766 +3767 +3768 +3769 +3770 +3771 +3772 +3773 +3774 +3775 +3776 +3777 +3778 +3779 +3780 +3781 +3782 +3783 +3784 +3785 +3786 +3787 +3788 +3789 +3790 +3791 +3792 +3793 +3794 +3795 +3796 +3797 +3798 +3799 +3800 +3801 +3802 +3803 +3804 +3805 +3806 +3807 +3808 +3809 +3810 +3811 +3812 +3813 +3814 +3815 +3816 +3817 +3818 +3819 +3820 +3821 +3822 +3823 +3824 +3825 +3826 +3827 +3828 +3829 +3830 +3831 +3832 +3833 +3834 +3835 +3836 +3837 +3838 +3839 +3840 +3841 +3842 +3843 +3844 +3845 +3846 +3847 +3848 +3849 +3850 +3851 +3852 +3853 +3854 +3855 +3856 +3857 +3858 +3859 +3860 +3861 +3862 +3863 +3864 +3865 +3866 +3867 +3868 +3869 +3870 +3871 +3872 +3873 +3874 +3875 +3876 +3877 +3878 +3879 +3880 +3881 +3882 +3883 +3884 +3885 +3886 +3887 +3888 +3889 +3890 +3891 +3892 +3893 +3894 +3895 +3896 +3897 +3898 +3899 +3900 +3901 +3902 +3903 +3904 +3905 +3906 +3907 +3908 +3909 +3910 +3911 +3912 +3913 +3914 +3915 +3916 +3917 +3918 +3919 +3920 +3921 +3922 +3923 +3924 +3925 +3926 +3927 +3928 +3929 +3930 +3931 +3932 +3933 +3934 +3935 +3936 +3937 +3938 +3939 +3940 +3941 +3942 +3943 +3944 +3945 +3946 +3947 +3948 +3949 +3950 +3951 +3952 +3953 +3954 +3955 +3956 +3957 +3958 +3959 +3960 +3961 +3962 +3963 +3964 +3965 +3966 +3967 +3968 +3969 +3970 +3971 +3972 +3973 +3974 +3975 +3976 +3977 +3978 +3979 +3980 +3981 +3982 +3983 +3984 +3985 +3986 +3987 +3988 +3989 +3990 +3991 +3992 +3993 +3994 +3995 +3996 +3997 +3998 +3999 +4000 diff --git a/test/static/executor/test_data/departments_500.csv b/test/static/executor/test_data/departments_500.csv new file mode 100644 index 0000000..14dcc8c --- /dev/null +++ b/test/static/executor/test_data/departments_500.csv @@ -0,0 +1,1001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 diff --git a/test/static/executor/test_data/departments_8000.csv b/test/static/executor/test_data/departments_8000.csv new file mode 100644 index 0000000..df7c787 --- /dev/null +++ b/test/static/executor/test_data/departments_8000.csv @@ -0,0 +1,8001 @@ +id:int +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507 +508 +509 +510 +511 +512 +513 +514 +515 +516 +517 +518 +519 +520 +521 +522 +523 +524 +525 +526 +527 +528 +529 +530 +531 +532 +533 +534 +535 +536 +537 +538 +539 +540 +541 +542 +543 +544 +545 +546 +547 +548 +549 +550 +551 +552 +553 +554 +555 +556 +557 +558 +559 +560 +561 +562 +563 +564 +565 +566 +567 +568 +569 +570 +571 +572 +573 +574 +575 +576 +577 +578 +579 +580 +581 +582 +583 +584 +585 +586 +587 +588 +589 +590 +591 +592 +593 +594 +595 +596 +597 +598 +599 +600 +601 +602 +603 +604 +605 +606 +607 +608 +609 +610 +611 +612 +613 +614 +615 +616 +617 +618 +619 +620 +621 +622 +623 +624 +625 +626 +627 +628 +629 +630 +631 +632 +633 +634 +635 +636 +637 +638 +639 +640 +641 +642 +643 +644 +645 +646 +647 +648 +649 +650 +651 +652 +653 +654 +655 +656 +657 +658 +659 +660 +661 +662 +663 +664 +665 +666 +667 +668 +669 +670 +671 +672 +673 +674 +675 +676 +677 +678 +679 +680 +681 +682 +683 +684 +685 +686 +687 +688 +689 +690 +691 +692 +693 +694 +695 +696 +697 +698 +699 +700 +701 +702 +703 +704 +705 +706 +707 +708 +709 +710 +711 +712 +713 +714 +715 +716 +717 +718 +719 +720 +721 +722 +723 +724 +725 +726 +727 +728 +729 +730 +731 +732 +733 +734 +735 +736 +737 +738 +739 +740 +741 +742 +743 +744 +745 +746 +747 +748 +749 +750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853 +854 +855 +856 +857 +858 +859 +860 +861 +862 +863 +864 +865 +866 +867 +868 +869 +870 +871 +872 +873 +874 +875 +876 +877 +878 +879 +880 +881 +882 +883 +884 +885 +886 +887 +888 +889 +890 +891 +892 +893 +894 +895 +896 +897 +898 +899 +900 +901 +902 +903 +904 +905 +906 +907 +908 +909 +910 +911 +912 +913 +914 +915 +916 +917 +918 +919 +920 +921 +922 +923 +924 +925 +926 +927 +928 +929 +930 +931 +932 +933 +934 +935 +936 +937 +938 +939 +940 +941 +942 +943 +944 +945 +946 +947 +948 +949 +950 +951 +952 +953 +954 +955 +956 +957 +958 +959 +960 +961 +962 +963 +964 +965 +966 +967 +968 +969 +970 +971 +972 +973 +974 +975 +976 +977 +978 +979 +980 +981 +982 +983 +984 +985 +986 +987 +988 +989 +990 +991 +992 +993 +994 +995 +996 +997 +998 +999 +1000 +1001 +1002 +1003 +1004 +1005 +1006 +1007 +1008 +1009 +1010 +1011 +1012 +1013 +1014 +1015 +1016 +1017 +1018 +1019 +1020 +1021 +1022 +1023 +1024 +1025 +1026 +1027 +1028 +1029 +1030 +1031 +1032 +1033 +1034 +1035 +1036 +1037 +1038 +1039 +1040 +1041 +1042 +1043 +1044 +1045 +1046 +1047 +1048 +1049 +1050 +1051 +1052 +1053 +1054 +1055 +1056 +1057 +1058 +1059 +1060 +1061 +1062 +1063 +1064 +1065 +1066 +1067 +1068 +1069 +1070 +1071 +1072 +1073 +1074 +1075 +1076 +1077 +1078 +1079 +1080 +1081 +1082 +1083 +1084 +1085 +1086 +1087 +1088 +1089 +1090 +1091 +1092 +1093 +1094 +1095 +1096 +1097 +1098 +1099 +1100 +1101 +1102 +1103 +1104 +1105 +1106 +1107 +1108 +1109 +1110 +1111 +1112 +1113 +1114 +1115 +1116 +1117 +1118 +1119 +1120 +1121 +1122 +1123 +1124 +1125 +1126 +1127 +1128 +1129 +1130 +1131 +1132 +1133 +1134 +1135 +1136 +1137 +1138 +1139 +1140 +1141 +1142 +1143 +1144 +1145 +1146 +1147 +1148 +1149 +1150 +1151 +1152 +1153 +1154 +1155 +1156 +1157 +1158 +1159 +1160 +1161 +1162 +1163 +1164 +1165 +1166 +1167 +1168 +1169 +1170 +1171 +1172 +1173 +1174 +1175 +1176 +1177 +1178 +1179 +1180 +1181 +1182 +1183 +1184 +1185 +1186 +1187 +1188 +1189 +1190 +1191 +1192 +1193 +1194 +1195 +1196 +1197 +1198 +1199 +1200 +1201 +1202 +1203 +1204 +1205 +1206 +1207 +1208 +1209 +1210 +1211 +1212 +1213 +1214 +1215 +1216 +1217 +1218 +1219 +1220 +1221 +1222 +1223 +1224 +1225 +1226 +1227 +1228 +1229 +1230 +1231 +1232 +1233 +1234 +1235 +1236 +1237 +1238 +1239 +1240 +1241 +1242 +1243 +1244 +1245 +1246 +1247 +1248 +1249 +1250 +1251 +1252 +1253 +1254 +1255 +1256 +1257 +1258 +1259 +1260 +1261 +1262 +1263 +1264 +1265 +1266 +1267 +1268 +1269 +1270 +1271 +1272 +1273 +1274 +1275 +1276 +1277 +1278 +1279 +1280 +1281 +1282 +1283 +1284 +1285 +1286 +1287 +1288 +1289 +1290 +1291 +1292 +1293 +1294 +1295 +1296 +1297 +1298 +1299 +1300 +1301 +1302 +1303 +1304 +1305 +1306 +1307 +1308 +1309 +1310 +1311 +1312 +1313 +1314 +1315 +1316 +1317 +1318 +1319 +1320 +1321 +1322 +1323 +1324 +1325 +1326 +1327 +1328 +1329 +1330 +1331 +1332 +1333 +1334 +1335 +1336 +1337 +1338 +1339 +1340 +1341 +1342 +1343 +1344 +1345 +1346 +1347 +1348 +1349 +1350 +1351 +1352 +1353 +1354 +1355 +1356 +1357 +1358 +1359 +1360 +1361 +1362 +1363 +1364 +1365 +1366 +1367 +1368 +1369 +1370 +1371 +1372 +1373 +1374 +1375 +1376 +1377 +1378 +1379 +1380 +1381 +1382 +1383 +1384 +1385 +1386 +1387 +1388 +1389 +1390 +1391 +1392 +1393 +1394 +1395 +1396 +1397 +1398 +1399 +1400 +1401 +1402 +1403 +1404 +1405 +1406 +1407 +1408 +1409 +1410 +1411 +1412 +1413 +1414 +1415 +1416 +1417 +1418 +1419 +1420 +1421 +1422 +1423 +1424 +1425 +1426 +1427 +1428 +1429 +1430 +1431 +1432 +1433 +1434 +1435 +1436 +1437 +1438 +1439 +1440 +1441 +1442 +1443 +1444 +1445 +1446 +1447 +1448 +1449 +1450 +1451 +1452 +1453 +1454 +1455 +1456 +1457 +1458 +1459 +1460 +1461 +1462 +1463 +1464 +1465 +1466 +1467 +1468 +1469 +1470 +1471 +1472 +1473 +1474 +1475 +1476 +1477 +1478 +1479 +1480 +1481 +1482 +1483 +1484 +1485 +1486 +1487 +1488 +1489 +1490 +1491 +1492 +1493 +1494 +1495 +1496 +1497 +1498 +1499 +1500 +1501 +1502 +1503 +1504 +1505 +1506 +1507 +1508 +1509 +1510 +1511 +1512 +1513 +1514 +1515 +1516 +1517 +1518 +1519 +1520 +1521 +1522 +1523 +1524 +1525 +1526 +1527 +1528 +1529 +1530 +1531 +1532 +1533 +1534 +1535 +1536 +1537 +1538 +1539 +1540 +1541 +1542 +1543 +1544 +1545 +1546 +1547 +1548 +1549 +1550 +1551 +1552 +1553 +1554 +1555 +1556 +1557 +1558 +1559 +1560 +1561 +1562 +1563 +1564 +1565 +1566 +1567 +1568 +1569 +1570 +1571 +1572 +1573 +1574 +1575 +1576 +1577 +1578 +1579 +1580 +1581 +1582 +1583 +1584 +1585 +1586 +1587 +1588 +1589 +1590 +1591 +1592 +1593 +1594 +1595 +1596 +1597 +1598 +1599 +1600 +1601 +1602 +1603 +1604 +1605 +1606 +1607 +1608 +1609 +1610 +1611 +1612 +1613 +1614 +1615 +1616 +1617 +1618 +1619 +1620 +1621 +1622 +1623 +1624 +1625 +1626 +1627 +1628 +1629 +1630 +1631 +1632 +1633 +1634 +1635 +1636 +1637 +1638 +1639 +1640 +1641 +1642 +1643 +1644 +1645 +1646 +1647 +1648 +1649 +1650 +1651 +1652 +1653 +1654 +1655 +1656 +1657 +1658 +1659 +1660 +1661 +1662 +1663 +1664 +1665 +1666 +1667 +1668 +1669 +1670 +1671 +1672 +1673 +1674 +1675 +1676 +1677 +1678 +1679 +1680 +1681 +1682 +1683 +1684 +1685 +1686 +1687 +1688 +1689 +1690 +1691 +1692 +1693 +1694 +1695 +1696 +1697 +1698 +1699 +1700 +1701 +1702 +1703 +1704 +1705 +1706 +1707 +1708 +1709 +1710 +1711 +1712 +1713 +1714 +1715 +1716 +1717 +1718 +1719 +1720 +1721 +1722 +1723 +1724 +1725 +1726 +1727 +1728 +1729 +1730 +1731 +1732 +1733 +1734 +1735 +1736 +1737 +1738 +1739 +1740 +1741 +1742 +1743 +1744 +1745 +1746 +1747 +1748 +1749 +1750 +1751 +1752 +1753 +1754 +1755 +1756 +1757 +1758 +1759 +1760 +1761 +1762 +1763 +1764 +1765 +1766 +1767 +1768 +1769 +1770 +1771 +1772 +1773 +1774 +1775 +1776 +1777 +1778 +1779 +1780 +1781 +1782 +1783 +1784 +1785 +1786 +1787 +1788 +1789 +1790 +1791 +1792 +1793 +1794 +1795 +1796 +1797 +1798 +1799 +1800 +1801 +1802 +1803 +1804 +1805 +1806 +1807 +1808 +1809 +1810 +1811 +1812 +1813 +1814 +1815 +1816 +1817 +1818 +1819 +1820 +1821 +1822 +1823 +1824 +1825 +1826 +1827 +1828 +1829 +1830 +1831 +1832 +1833 +1834 +1835 +1836 +1837 +1838 +1839 +1840 +1841 +1842 +1843 +1844 +1845 +1846 +1847 +1848 +1849 +1850 +1851 +1852 +1853 +1854 +1855 +1856 +1857 +1858 +1859 +1860 +1861 +1862 +1863 +1864 +1865 +1866 +1867 +1868 +1869 +1870 +1871 +1872 +1873 +1874 +1875 +1876 +1877 +1878 +1879 +1880 +1881 +1882 +1883 +1884 +1885 +1886 +1887 +1888 +1889 +1890 +1891 +1892 +1893 +1894 +1895 +1896 +1897 +1898 +1899 +1900 +1901 +1902 +1903 +1904 +1905 +1906 +1907 +1908 +1909 +1910 +1911 +1912 +1913 +1914 +1915 +1916 +1917 +1918 +1919 +1920 +1921 +1922 +1923 +1924 +1925 +1926 +1927 +1928 +1929 +1930 +1931 +1932 +1933 +1934 +1935 +1936 +1937 +1938 +1939 +1940 +1941 +1942 +1943 +1944 +1945 +1946 +1947 +1948 +1949 +1950 +1951 +1952 +1953 +1954 +1955 +1956 +1957 +1958 +1959 +1960 +1961 +1962 +1963 +1964 +1965 +1966 +1967 +1968 +1969 +1970 +1971 +1972 +1973 +1974 +1975 +1976 +1977 +1978 +1979 +1980 +1981 +1982 +1983 +1984 +1985 +1986 +1987 +1988 +1989 +1990 +1991 +1992 +1993 +1994 +1995 +1996 +1997 +1998 +1999 +2000 +2001 +2002 +2003 +2004 +2005 +2006 +2007 +2008 +2009 +2010 +2011 +2012 +2013 +2014 +2015 +2016 +2017 +2018 +2019 +2020 +2021 +2022 +2023 +2024 +2025 +2026 +2027 +2028 +2029 +2030 +2031 +2032 +2033 +2034 +2035 +2036 +2037 +2038 +2039 +2040 +2041 +2042 +2043 +2044 +2045 +2046 +2047 +2048 +2049 +2050 +2051 +2052 +2053 +2054 +2055 +2056 +2057 +2058 +2059 +2060 +2061 +2062 +2063 +2064 +2065 +2066 +2067 +2068 +2069 +2070 +2071 +2072 +2073 +2074 +2075 +2076 +2077 +2078 +2079 +2080 +2081 +2082 +2083 +2084 +2085 +2086 +2087 +2088 +2089 +2090 +2091 +2092 +2093 +2094 +2095 +2096 +2097 +2098 +2099 +2100 +2101 +2102 +2103 +2104 +2105 +2106 +2107 +2108 +2109 +2110 +2111 +2112 +2113 +2114 +2115 +2116 +2117 +2118 +2119 +2120 +2121 +2122 +2123 +2124 +2125 +2126 +2127 +2128 +2129 +2130 +2131 +2132 +2133 +2134 +2135 +2136 +2137 +2138 +2139 +2140 +2141 +2142 +2143 +2144 +2145 +2146 +2147 +2148 +2149 +2150 +2151 +2152 +2153 +2154 +2155 +2156 +2157 +2158 +2159 +2160 +2161 +2162 +2163 +2164 +2165 +2166 +2167 +2168 +2169 +2170 +2171 +2172 +2173 +2174 +2175 +2176 +2177 +2178 +2179 +2180 +2181 +2182 +2183 +2184 +2185 +2186 +2187 +2188 +2189 +2190 +2191 +2192 +2193 +2194 +2195 +2196 +2197 +2198 +2199 +2200 +2201 +2202 +2203 +2204 +2205 +2206 +2207 +2208 +2209 +2210 +2211 +2212 +2213 +2214 +2215 +2216 +2217 +2218 +2219 +2220 +2221 +2222 +2223 +2224 +2225 +2226 +2227 +2228 +2229 +2230 +2231 +2232 +2233 +2234 +2235 +2236 +2237 +2238 +2239 +2240 +2241 +2242 +2243 +2244 +2245 +2246 +2247 +2248 +2249 +2250 +2251 +2252 +2253 +2254 +2255 +2256 +2257 +2258 +2259 +2260 +2261 +2262 +2263 +2264 +2265 +2266 +2267 +2268 +2269 +2270 +2271 +2272 +2273 +2274 +2275 +2276 +2277 +2278 +2279 +2280 +2281 +2282 +2283 +2284 +2285 +2286 +2287 +2288 +2289 +2290 +2291 +2292 +2293 +2294 +2295 +2296 +2297 +2298 +2299 +2300 +2301 +2302 +2303 +2304 +2305 +2306 +2307 +2308 +2309 +2310 +2311 +2312 +2313 +2314 +2315 +2316 +2317 +2318 +2319 +2320 +2321 +2322 +2323 +2324 +2325 +2326 +2327 +2328 +2329 +2330 +2331 +2332 +2333 +2334 +2335 +2336 +2337 +2338 +2339 +2340 +2341 +2342 +2343 +2344 +2345 +2346 +2347 +2348 +2349 +2350 +2351 +2352 +2353 +2354 +2355 +2356 +2357 +2358 +2359 +2360 +2361 +2362 +2363 +2364 +2365 +2366 +2367 +2368 +2369 +2370 +2371 +2372 +2373 +2374 +2375 +2376 +2377 +2378 +2379 +2380 +2381 +2382 +2383 +2384 +2385 +2386 +2387 +2388 +2389 +2390 +2391 +2392 +2393 +2394 +2395 +2396 +2397 +2398 +2399 +2400 +2401 +2402 +2403 +2404 +2405 +2406 +2407 +2408 +2409 +2410 +2411 +2412 +2413 +2414 +2415 +2416 +2417 +2418 +2419 +2420 +2421 +2422 +2423 +2424 +2425 +2426 +2427 +2428 +2429 +2430 +2431 +2432 +2433 +2434 +2435 +2436 +2437 +2438 +2439 +2440 +2441 +2442 +2443 +2444 +2445 +2446 +2447 +2448 +2449 +2450 +2451 +2452 +2453 +2454 +2455 +2456 +2457 +2458 +2459 +2460 +2461 +2462 +2463 +2464 +2465 +2466 +2467 +2468 +2469 +2470 +2471 +2472 +2473 +2474 +2475 +2476 +2477 +2478 +2479 +2480 +2481 +2482 +2483 +2484 +2485 +2486 +2487 +2488 +2489 +2490 +2491 +2492 +2493 +2494 +2495 +2496 +2497 +2498 +2499 +2500 +2501 +2502 +2503 +2504 +2505 +2506 +2507 +2508 +2509 +2510 +2511 +2512 +2513 +2514 +2515 +2516 +2517 +2518 +2519 +2520 +2521 +2522 +2523 +2524 +2525 +2526 +2527 +2528 +2529 +2530 +2531 +2532 +2533 +2534 +2535 +2536 +2537 +2538 +2539 +2540 +2541 +2542 +2543 +2544 +2545 +2546 +2547 +2548 +2549 +2550 +2551 +2552 +2553 +2554 +2555 +2556 +2557 +2558 +2559 +2560 +2561 +2562 +2563 +2564 +2565 +2566 +2567 +2568 +2569 +2570 +2571 +2572 +2573 +2574 +2575 +2576 +2577 +2578 +2579 +2580 +2581 +2582 +2583 +2584 +2585 +2586 +2587 +2588 +2589 +2590 +2591 +2592 +2593 +2594 +2595 +2596 +2597 +2598 +2599 +2600 +2601 +2602 +2603 +2604 +2605 +2606 +2607 +2608 +2609 +2610 +2611 +2612 +2613 +2614 +2615 +2616 +2617 +2618 +2619 +2620 +2621 +2622 +2623 +2624 +2625 +2626 +2627 +2628 +2629 +2630 +2631 +2632 +2633 +2634 +2635 +2636 +2637 +2638 +2639 +2640 +2641 +2642 +2643 +2644 +2645 +2646 +2647 +2648 +2649 +2650 +2651 +2652 +2653 +2654 +2655 +2656 +2657 +2658 +2659 +2660 +2661 +2662 +2663 +2664 +2665 +2666 +2667 +2668 +2669 +2670 +2671 +2672 +2673 +2674 +2675 +2676 +2677 +2678 +2679 +2680 +2681 +2682 +2683 +2684 +2685 +2686 +2687 +2688 +2689 +2690 +2691 +2692 +2693 +2694 +2695 +2696 +2697 +2698 +2699 +2700 +2701 +2702 +2703 +2704 +2705 +2706 +2707 +2708 +2709 +2710 +2711 +2712 +2713 +2714 +2715 +2716 +2717 +2718 +2719 +2720 +2721 +2722 +2723 +2724 +2725 +2726 +2727 +2728 +2729 +2730 +2731 +2732 +2733 +2734 +2735 +2736 +2737 +2738 +2739 +2740 +2741 +2742 +2743 +2744 +2745 +2746 +2747 +2748 +2749 +2750 +2751 +2752 +2753 +2754 +2755 +2756 +2757 +2758 +2759 +2760 +2761 +2762 +2763 +2764 +2765 +2766 +2767 +2768 +2769 +2770 +2771 +2772 +2773 +2774 +2775 +2776 +2777 +2778 +2779 +2780 +2781 +2782 +2783 +2784 +2785 +2786 +2787 +2788 +2789 +2790 +2791 +2792 +2793 +2794 +2795 +2796 +2797 +2798 +2799 +2800 +2801 +2802 +2803 +2804 +2805 +2806 +2807 +2808 +2809 +2810 +2811 +2812 +2813 +2814 +2815 +2816 +2817 +2818 +2819 +2820 +2821 +2822 +2823 +2824 +2825 +2826 +2827 +2828 +2829 +2830 +2831 +2832 +2833 +2834 +2835 +2836 +2837 +2838 +2839 +2840 +2841 +2842 +2843 +2844 +2845 +2846 +2847 +2848 +2849 +2850 +2851 +2852 +2853 +2854 +2855 +2856 +2857 +2858 +2859 +2860 +2861 +2862 +2863 +2864 +2865 +2866 +2867 +2868 +2869 +2870 +2871 +2872 +2873 +2874 +2875 +2876 +2877 +2878 +2879 +2880 +2881 +2882 +2883 +2884 +2885 +2886 +2887 +2888 +2889 +2890 +2891 +2892 +2893 +2894 +2895 +2896 +2897 +2898 +2899 +2900 +2901 +2902 +2903 +2904 +2905 +2906 +2907 +2908 +2909 +2910 +2911 +2912 +2913 +2914 +2915 +2916 +2917 +2918 +2919 +2920 +2921 +2922 +2923 +2924 +2925 +2926 +2927 +2928 +2929 +2930 +2931 +2932 +2933 +2934 +2935 +2936 +2937 +2938 +2939 +2940 +2941 +2942 +2943 +2944 +2945 +2946 +2947 +2948 +2949 +2950 +2951 +2952 +2953 +2954 +2955 +2956 +2957 +2958 +2959 +2960 +2961 +2962 +2963 +2964 +2965 +2966 +2967 +2968 +2969 +2970 +2971 +2972 +2973 +2974 +2975 +2976 +2977 +2978 +2979 +2980 +2981 +2982 +2983 +2984 +2985 +2986 +2987 +2988 +2989 +2990 +2991 +2992 +2993 +2994 +2995 +2996 +2997 +2998 +2999 +3000 +3001 +3002 +3003 +3004 +3005 +3006 +3007 +3008 +3009 +3010 +3011 +3012 +3013 +3014 +3015 +3016 +3017 +3018 +3019 +3020 +3021 +3022 +3023 +3024 +3025 +3026 +3027 +3028 +3029 +3030 +3031 +3032 +3033 +3034 +3035 +3036 +3037 +3038 +3039 +3040 +3041 +3042 +3043 +3044 +3045 +3046 +3047 +3048 +3049 +3050 +3051 +3052 +3053 +3054 +3055 +3056 +3057 +3058 +3059 +3060 +3061 +3062 +3063 +3064 +3065 +3066 +3067 +3068 +3069 +3070 +3071 +3072 +3073 +3074 +3075 +3076 +3077 +3078 +3079 +3080 +3081 +3082 +3083 +3084 +3085 +3086 +3087 +3088 +3089 +3090 +3091 +3092 +3093 +3094 +3095 +3096 +3097 +3098 +3099 +3100 +3101 +3102 +3103 +3104 +3105 +3106 +3107 +3108 +3109 +3110 +3111 +3112 +3113 +3114 +3115 +3116 +3117 +3118 +3119 +3120 +3121 +3122 +3123 +3124 +3125 +3126 +3127 +3128 +3129 +3130 +3131 +3132 +3133 +3134 +3135 +3136 +3137 +3138 +3139 +3140 +3141 +3142 +3143 +3144 +3145 +3146 +3147 +3148 +3149 +3150 +3151 +3152 +3153 +3154 +3155 +3156 +3157 +3158 +3159 +3160 +3161 +3162 +3163 +3164 +3165 +3166 +3167 +3168 +3169 +3170 +3171 +3172 +3173 +3174 +3175 +3176 +3177 +3178 +3179 +3180 +3181 +3182 +3183 +3184 +3185 +3186 +3187 +3188 +3189 +3190 +3191 +3192 +3193 +3194 +3195 +3196 +3197 +3198 +3199 +3200 +3201 +3202 +3203 +3204 +3205 +3206 +3207 +3208 +3209 +3210 +3211 +3212 +3213 +3214 +3215 +3216 +3217 +3218 +3219 +3220 +3221 +3222 +3223 +3224 +3225 +3226 +3227 +3228 +3229 +3230 +3231 +3232 +3233 +3234 +3235 +3236 +3237 +3238 +3239 +3240 +3241 +3242 +3243 +3244 +3245 +3246 +3247 +3248 +3249 +3250 +3251 +3252 +3253 +3254 +3255 +3256 +3257 +3258 +3259 +3260 +3261 +3262 +3263 +3264 +3265 +3266 +3267 +3268 +3269 +3270 +3271 +3272 +3273 +3274 +3275 +3276 +3277 +3278 +3279 +3280 +3281 +3282 +3283 +3284 +3285 +3286 +3287 +3288 +3289 +3290 +3291 +3292 +3293 +3294 +3295 +3296 +3297 +3298 +3299 +3300 +3301 +3302 +3303 +3304 +3305 +3306 +3307 +3308 +3309 +3310 +3311 +3312 +3313 +3314 +3315 +3316 +3317 +3318 +3319 +3320 +3321 +3322 +3323 +3324 +3325 +3326 +3327 +3328 +3329 +3330 +3331 +3332 +3333 +3334 +3335 +3336 +3337 +3338 +3339 +3340 +3341 +3342 +3343 +3344 +3345 +3346 +3347 +3348 +3349 +3350 +3351 +3352 +3353 +3354 +3355 +3356 +3357 +3358 +3359 +3360 +3361 +3362 +3363 +3364 +3365 +3366 +3367 +3368 +3369 +3370 +3371 +3372 +3373 +3374 +3375 +3376 +3377 +3378 +3379 +3380 +3381 +3382 +3383 +3384 +3385 +3386 +3387 +3388 +3389 +3390 +3391 +3392 +3393 +3394 +3395 +3396 +3397 +3398 +3399 +3400 +3401 +3402 +3403 +3404 +3405 +3406 +3407 +3408 +3409 +3410 +3411 +3412 +3413 +3414 +3415 +3416 +3417 +3418 +3419 +3420 +3421 +3422 +3423 +3424 +3425 +3426 +3427 +3428 +3429 +3430 +3431 +3432 +3433 +3434 +3435 +3436 +3437 +3438 +3439 +3440 +3441 +3442 +3443 +3444 +3445 +3446 +3447 +3448 +3449 +3450 +3451 +3452 +3453 +3454 +3455 +3456 +3457 +3458 +3459 +3460 +3461 +3462 +3463 +3464 +3465 +3466 +3467 +3468 +3469 +3470 +3471 +3472 +3473 +3474 +3475 +3476 +3477 +3478 +3479 +3480 +3481 +3482 +3483 +3484 +3485 +3486 +3487 +3488 +3489 +3490 +3491 +3492 +3493 +3494 +3495 +3496 +3497 +3498 +3499 +3500 +3501 +3502 +3503 +3504 +3505 +3506 +3507 +3508 +3509 +3510 +3511 +3512 +3513 +3514 +3515 +3516 +3517 +3518 +3519 +3520 +3521 +3522 +3523 +3524 +3525 +3526 +3527 +3528 +3529 +3530 +3531 +3532 +3533 +3534 +3535 +3536 +3537 +3538 +3539 +3540 +3541 +3542 +3543 +3544 +3545 +3546 +3547 +3548 +3549 +3550 +3551 +3552 +3553 +3554 +3555 +3556 +3557 +3558 +3559 +3560 +3561 +3562 +3563 +3564 +3565 +3566 +3567 +3568 +3569 +3570 +3571 +3572 +3573 +3574 +3575 +3576 +3577 +3578 +3579 +3580 +3581 +3582 +3583 +3584 +3585 +3586 +3587 +3588 +3589 +3590 +3591 +3592 +3593 +3594 +3595 +3596 +3597 +3598 +3599 +3600 +3601 +3602 +3603 +3604 +3605 +3606 +3607 +3608 +3609 +3610 +3611 +3612 +3613 +3614 +3615 +3616 +3617 +3618 +3619 +3620 +3621 +3622 +3623 +3624 +3625 +3626 +3627 +3628 +3629 +3630 +3631 +3632 +3633 +3634 +3635 +3636 +3637 +3638 +3639 +3640 +3641 +3642 +3643 +3644 +3645 +3646 +3647 +3648 +3649 +3650 +3651 +3652 +3653 +3654 +3655 +3656 +3657 +3658 +3659 +3660 +3661 +3662 +3663 +3664 +3665 +3666 +3667 +3668 +3669 +3670 +3671 +3672 +3673 +3674 +3675 +3676 +3677 +3678 +3679 +3680 +3681 +3682 +3683 +3684 +3685 +3686 +3687 +3688 +3689 +3690 +3691 +3692 +3693 +3694 +3695 +3696 +3697 +3698 +3699 +3700 +3701 +3702 +3703 +3704 +3705 +3706 +3707 +3708 +3709 +3710 +3711 +3712 +3713 +3714 +3715 +3716 +3717 +3718 +3719 +3720 +3721 +3722 +3723 +3724 +3725 +3726 +3727 +3728 +3729 +3730 +3731 +3732 +3733 +3734 +3735 +3736 +3737 +3738 +3739 +3740 +3741 +3742 +3743 +3744 +3745 +3746 +3747 +3748 +3749 +3750 +3751 +3752 +3753 +3754 +3755 +3756 +3757 +3758 +3759 +3760 +3761 +3762 +3763 +3764 +3765 +3766 +3767 +3768 +3769 +3770 +3771 +3772 +3773 +3774 +3775 +3776 +3777 +3778 +3779 +3780 +3781 +3782 +3783 +3784 +3785 +3786 +3787 +3788 +3789 +3790 +3791 +3792 +3793 +3794 +3795 +3796 +3797 +3798 +3799 +3800 +3801 +3802 +3803 +3804 +3805 +3806 +3807 +3808 +3809 +3810 +3811 +3812 +3813 +3814 +3815 +3816 +3817 +3818 +3819 +3820 +3821 +3822 +3823 +3824 +3825 +3826 +3827 +3828 +3829 +3830 +3831 +3832 +3833 +3834 +3835 +3836 +3837 +3838 +3839 +3840 +3841 +3842 +3843 +3844 +3845 +3846 +3847 +3848 +3849 +3850 +3851 +3852 +3853 +3854 +3855 +3856 +3857 +3858 +3859 +3860 +3861 +3862 +3863 +3864 +3865 +3866 +3867 +3868 +3869 +3870 +3871 +3872 +3873 +3874 +3875 +3876 +3877 +3878 +3879 +3880 +3881 +3882 +3883 +3884 +3885 +3886 +3887 +3888 +3889 +3890 +3891 +3892 +3893 +3894 +3895 +3896 +3897 +3898 +3899 +3900 +3901 +3902 +3903 +3904 +3905 +3906 +3907 +3908 +3909 +3910 +3911 +3912 +3913 +3914 +3915 +3916 +3917 +3918 +3919 +3920 +3921 +3922 +3923 +3924 +3925 +3926 +3927 +3928 +3929 +3930 +3931 +3932 +3933 +3934 +3935 +3936 +3937 +3938 +3939 +3940 +3941 +3942 +3943 +3944 +3945 +3946 +3947 +3948 +3949 +3950 +3951 +3952 +3953 +3954 +3955 +3956 +3957 +3958 +3959 +3960 +3961 +3962 +3963 +3964 +3965 +3966 +3967 +3968 +3969 +3970 +3971 +3972 +3973 +3974 +3975 +3976 +3977 +3978 +3979 +3980 +3981 +3982 +3983 +3984 +3985 +3986 +3987 +3988 +3989 +3990 +3991 +3992 +3993 +3994 +3995 +3996 +3997 +3998 +3999 +4000 +4001 +4002 +4003 +4004 +4005 +4006 +4007 +4008 +4009 +4010 +4011 +4012 +4013 +4014 +4015 +4016 +4017 +4018 +4019 +4020 +4021 +4022 +4023 +4024 +4025 +4026 +4027 +4028 +4029 +4030 +4031 +4032 +4033 +4034 +4035 +4036 +4037 +4038 +4039 +4040 +4041 +4042 +4043 +4044 +4045 +4046 +4047 +4048 +4049 +4050 +4051 +4052 +4053 +4054 +4055 +4056 +4057 +4058 +4059 +4060 +4061 +4062 +4063 +4064 +4065 +4066 +4067 +4068 +4069 +4070 +4071 +4072 +4073 +4074 +4075 +4076 +4077 +4078 +4079 +4080 +4081 +4082 +4083 +4084 +4085 +4086 +4087 +4088 +4089 +4090 +4091 +4092 +4093 +4094 +4095 +4096 +4097 +4098 +4099 +4100 +4101 +4102 +4103 +4104 +4105 +4106 +4107 +4108 +4109 +4110 +4111 +4112 +4113 +4114 +4115 +4116 +4117 +4118 +4119 +4120 +4121 +4122 +4123 +4124 +4125 +4126 +4127 +4128 +4129 +4130 +4131 +4132 +4133 +4134 +4135 +4136 +4137 +4138 +4139 +4140 +4141 +4142 +4143 +4144 +4145 +4146 +4147 +4148 +4149 +4150 +4151 +4152 +4153 +4154 +4155 +4156 +4157 +4158 +4159 +4160 +4161 +4162 +4163 +4164 +4165 +4166 +4167 +4168 +4169 +4170 +4171 +4172 +4173 +4174 +4175 +4176 +4177 +4178 +4179 +4180 +4181 +4182 +4183 +4184 +4185 +4186 +4187 +4188 +4189 +4190 +4191 +4192 +4193 +4194 +4195 +4196 +4197 +4198 +4199 +4200 +4201 +4202 +4203 +4204 +4205 +4206 +4207 +4208 +4209 +4210 +4211 +4212 +4213 +4214 +4215 +4216 +4217 +4218 +4219 +4220 +4221 +4222 +4223 +4224 +4225 +4226 +4227 +4228 +4229 +4230 +4231 +4232 +4233 +4234 +4235 +4236 +4237 +4238 +4239 +4240 +4241 +4242 +4243 +4244 +4245 +4246 +4247 +4248 +4249 +4250 +4251 +4252 +4253 +4254 +4255 +4256 +4257 +4258 +4259 +4260 +4261 +4262 +4263 +4264 +4265 +4266 +4267 +4268 +4269 +4270 +4271 +4272 +4273 +4274 +4275 +4276 +4277 +4278 +4279 +4280 +4281 +4282 +4283 +4284 +4285 +4286 +4287 +4288 +4289 +4290 +4291 +4292 +4293 +4294 +4295 +4296 +4297 +4298 +4299 +4300 +4301 +4302 +4303 +4304 +4305 +4306 +4307 +4308 +4309 +4310 +4311 +4312 +4313 +4314 +4315 +4316 +4317 +4318 +4319 +4320 +4321 +4322 +4323 +4324 +4325 +4326 +4327 +4328 +4329 +4330 +4331 +4332 +4333 +4334 +4335 +4336 +4337 +4338 +4339 +4340 +4341 +4342 +4343 +4344 +4345 +4346 +4347 +4348 +4349 +4350 +4351 +4352 +4353 +4354 +4355 +4356 +4357 +4358 +4359 +4360 +4361 +4362 +4363 +4364 +4365 +4366 +4367 +4368 +4369 +4370 +4371 +4372 +4373 +4374 +4375 +4376 +4377 +4378 +4379 +4380 +4381 +4382 +4383 +4384 +4385 +4386 +4387 +4388 +4389 +4390 +4391 +4392 +4393 +4394 +4395 +4396 +4397 +4398 +4399 +4400 +4401 +4402 +4403 +4404 +4405 +4406 +4407 +4408 +4409 +4410 +4411 +4412 +4413 +4414 +4415 +4416 +4417 +4418 +4419 +4420 +4421 +4422 +4423 +4424 +4425 +4426 +4427 +4428 +4429 +4430 +4431 +4432 +4433 +4434 +4435 +4436 +4437 +4438 +4439 +4440 +4441 +4442 +4443 +4444 +4445 +4446 +4447 +4448 +4449 +4450 +4451 +4452 +4453 +4454 +4455 +4456 +4457 +4458 +4459 +4460 +4461 +4462 +4463 +4464 +4465 +4466 +4467 +4468 +4469 +4470 +4471 +4472 +4473 +4474 +4475 +4476 +4477 +4478 +4479 +4480 +4481 +4482 +4483 +4484 +4485 +4486 +4487 +4488 +4489 +4490 +4491 +4492 +4493 +4494 +4495 +4496 +4497 +4498 +4499 +4500 +4501 +4502 +4503 +4504 +4505 +4506 +4507 +4508 +4509 +4510 +4511 +4512 +4513 +4514 +4515 +4516 +4517 +4518 +4519 +4520 +4521 +4522 +4523 +4524 +4525 +4526 +4527 +4528 +4529 +4530 +4531 +4532 +4533 +4534 +4535 +4536 +4537 +4538 +4539 +4540 +4541 +4542 +4543 +4544 +4545 +4546 +4547 +4548 +4549 +4550 +4551 +4552 +4553 +4554 +4555 +4556 +4557 +4558 +4559 +4560 +4561 +4562 +4563 +4564 +4565 +4566 +4567 +4568 +4569 +4570 +4571 +4572 +4573 +4574 +4575 +4576 +4577 +4578 +4579 +4580 +4581 +4582 +4583 +4584 +4585 +4586 +4587 +4588 +4589 +4590 +4591 +4592 +4593 +4594 +4595 +4596 +4597 +4598 +4599 +4600 +4601 +4602 +4603 +4604 +4605 +4606 +4607 +4608 +4609 +4610 +4611 +4612 +4613 +4614 +4615 +4616 +4617 +4618 +4619 +4620 +4621 +4622 +4623 +4624 +4625 +4626 +4627 +4628 +4629 +4630 +4631 +4632 +4633 +4634 +4635 +4636 +4637 +4638 +4639 +4640 +4641 +4642 +4643 +4644 +4645 +4646 +4647 +4648 +4649 +4650 +4651 +4652 +4653 +4654 +4655 +4656 +4657 +4658 +4659 +4660 +4661 +4662 +4663 +4664 +4665 +4666 +4667 +4668 +4669 +4670 +4671 +4672 +4673 +4674 +4675 +4676 +4677 +4678 +4679 +4680 +4681 +4682 +4683 +4684 +4685 +4686 +4687 +4688 +4689 +4690 +4691 +4692 +4693 +4694 +4695 +4696 +4697 +4698 +4699 +4700 +4701 +4702 +4703 +4704 +4705 +4706 +4707 +4708 +4709 +4710 +4711 +4712 +4713 +4714 +4715 +4716 +4717 +4718 +4719 +4720 +4721 +4722 +4723 +4724 +4725 +4726 +4727 +4728 +4729 +4730 +4731 +4732 +4733 +4734 +4735 +4736 +4737 +4738 +4739 +4740 +4741 +4742 +4743 +4744 +4745 +4746 +4747 +4748 +4749 +4750 +4751 +4752 +4753 +4754 +4755 +4756 +4757 +4758 +4759 +4760 +4761 +4762 +4763 +4764 +4765 +4766 +4767 +4768 +4769 +4770 +4771 +4772 +4773 +4774 +4775 +4776 +4777 +4778 +4779 +4780 +4781 +4782 +4783 +4784 +4785 +4786 +4787 +4788 +4789 +4790 +4791 +4792 +4793 +4794 +4795 +4796 +4797 +4798 +4799 +4800 +4801 +4802 +4803 +4804 +4805 +4806 +4807 +4808 +4809 +4810 +4811 +4812 +4813 +4814 +4815 +4816 +4817 +4818 +4819 +4820 +4821 +4822 +4823 +4824 +4825 +4826 +4827 +4828 +4829 +4830 +4831 +4832 +4833 +4834 +4835 +4836 +4837 +4838 +4839 +4840 +4841 +4842 +4843 +4844 +4845 +4846 +4847 +4848 +4849 +4850 +4851 +4852 +4853 +4854 +4855 +4856 +4857 +4858 +4859 +4860 +4861 +4862 +4863 +4864 +4865 +4866 +4867 +4868 +4869 +4870 +4871 +4872 +4873 +4874 +4875 +4876 +4877 +4878 +4879 +4880 +4881 +4882 +4883 +4884 +4885 +4886 +4887 +4888 +4889 +4890 +4891 +4892 +4893 +4894 +4895 +4896 +4897 +4898 +4899 +4900 +4901 +4902 +4903 +4904 +4905 +4906 +4907 +4908 +4909 +4910 +4911 +4912 +4913 +4914 +4915 +4916 +4917 +4918 +4919 +4920 +4921 +4922 +4923 +4924 +4925 +4926 +4927 +4928 +4929 +4930 +4931 +4932 +4933 +4934 +4935 +4936 +4937 +4938 +4939 +4940 +4941 +4942 +4943 +4944 +4945 +4946 +4947 +4948 +4949 +4950 +4951 +4952 +4953 +4954 +4955 +4956 +4957 +4958 +4959 +4960 +4961 +4962 +4963 +4964 +4965 +4966 +4967 +4968 +4969 +4970 +4971 +4972 +4973 +4974 +4975 +4976 +4977 +4978 +4979 +4980 +4981 +4982 +4983 +4984 +4985 +4986 +4987 +4988 +4989 +4990 +4991 +4992 +4993 +4994 +4995 +4996 +4997 +4998 +4999 +5000 +5001 +5002 +5003 +5004 +5005 +5006 +5007 +5008 +5009 +5010 +5011 +5012 +5013 +5014 +5015 +5016 +5017 +5018 +5019 +5020 +5021 +5022 +5023 +5024 +5025 +5026 +5027 +5028 +5029 +5030 +5031 +5032 +5033 +5034 +5035 +5036 +5037 +5038 +5039 +5040 +5041 +5042 +5043 +5044 +5045 +5046 +5047 +5048 +5049 +5050 +5051 +5052 +5053 +5054 +5055 +5056 +5057 +5058 +5059 +5060 +5061 +5062 +5063 +5064 +5065 +5066 +5067 +5068 +5069 +5070 +5071 +5072 +5073 +5074 +5075 +5076 +5077 +5078 +5079 +5080 +5081 +5082 +5083 +5084 +5085 +5086 +5087 +5088 +5089 +5090 +5091 +5092 +5093 +5094 +5095 +5096 +5097 +5098 +5099 +5100 +5101 +5102 +5103 +5104 +5105 +5106 +5107 +5108 +5109 +5110 +5111 +5112 +5113 +5114 +5115 +5116 +5117 +5118 +5119 +5120 +5121 +5122 +5123 +5124 +5125 +5126 +5127 +5128 +5129 +5130 +5131 +5132 +5133 +5134 +5135 +5136 +5137 +5138 +5139 +5140 +5141 +5142 +5143 +5144 +5145 +5146 +5147 +5148 +5149 +5150 +5151 +5152 +5153 +5154 +5155 +5156 +5157 +5158 +5159 +5160 +5161 +5162 +5163 +5164 +5165 +5166 +5167 +5168 +5169 +5170 +5171 +5172 +5173 +5174 +5175 +5176 +5177 +5178 +5179 +5180 +5181 +5182 +5183 +5184 +5185 +5186 +5187 +5188 +5189 +5190 +5191 +5192 +5193 +5194 +5195 +5196 +5197 +5198 +5199 +5200 +5201 +5202 +5203 +5204 +5205 +5206 +5207 +5208 +5209 +5210 +5211 +5212 +5213 +5214 +5215 +5216 +5217 +5218 +5219 +5220 +5221 +5222 +5223 +5224 +5225 +5226 +5227 +5228 +5229 +5230 +5231 +5232 +5233 +5234 +5235 +5236 +5237 +5238 +5239 +5240 +5241 +5242 +5243 +5244 +5245 +5246 +5247 +5248 +5249 +5250 +5251 +5252 +5253 +5254 +5255 +5256 +5257 +5258 +5259 +5260 +5261 +5262 +5263 +5264 +5265 +5266 +5267 +5268 +5269 +5270 +5271 +5272 +5273 +5274 +5275 +5276 +5277 +5278 +5279 +5280 +5281 +5282 +5283 +5284 +5285 +5286 +5287 +5288 +5289 +5290 +5291 +5292 +5293 +5294 +5295 +5296 +5297 +5298 +5299 +5300 +5301 +5302 +5303 +5304 +5305 +5306 +5307 +5308 +5309 +5310 +5311 +5312 +5313 +5314 +5315 +5316 +5317 +5318 +5319 +5320 +5321 +5322 +5323 +5324 +5325 +5326 +5327 +5328 +5329 +5330 +5331 +5332 +5333 +5334 +5335 +5336 +5337 +5338 +5339 +5340 +5341 +5342 +5343 +5344 +5345 +5346 +5347 +5348 +5349 +5350 +5351 +5352 +5353 +5354 +5355 +5356 +5357 +5358 +5359 +5360 +5361 +5362 +5363 +5364 +5365 +5366 +5367 +5368 +5369 +5370 +5371 +5372 +5373 +5374 +5375 +5376 +5377 +5378 +5379 +5380 +5381 +5382 +5383 +5384 +5385 +5386 +5387 +5388 +5389 +5390 +5391 +5392 +5393 +5394 +5395 +5396 +5397 +5398 +5399 +5400 +5401 +5402 +5403 +5404 +5405 +5406 +5407 +5408 +5409 +5410 +5411 +5412 +5413 +5414 +5415 +5416 +5417 +5418 +5419 +5420 +5421 +5422 +5423 +5424 +5425 +5426 +5427 +5428 +5429 +5430 +5431 +5432 +5433 +5434 +5435 +5436 +5437 +5438 +5439 +5440 +5441 +5442 +5443 +5444 +5445 +5446 +5447 +5448 +5449 +5450 +5451 +5452 +5453 +5454 +5455 +5456 +5457 +5458 +5459 +5460 +5461 +5462 +5463 +5464 +5465 +5466 +5467 +5468 +5469 +5470 +5471 +5472 +5473 +5474 +5475 +5476 +5477 +5478 +5479 +5480 +5481 +5482 +5483 +5484 +5485 +5486 +5487 +5488 +5489 +5490 +5491 +5492 +5493 +5494 +5495 +5496 +5497 +5498 +5499 +5500 +5501 +5502 +5503 +5504 +5505 +5506 +5507 +5508 +5509 +5510 +5511 +5512 +5513 +5514 +5515 +5516 +5517 +5518 +5519 +5520 +5521 +5522 +5523 +5524 +5525 +5526 +5527 +5528 +5529 +5530 +5531 +5532 +5533 +5534 +5535 +5536 +5537 +5538 +5539 +5540 +5541 +5542 +5543 +5544 +5545 +5546 +5547 +5548 +5549 +5550 +5551 +5552 +5553 +5554 +5555 +5556 +5557 +5558 +5559 +5560 +5561 +5562 +5563 +5564 +5565 +5566 +5567 +5568 +5569 +5570 +5571 +5572 +5573 +5574 +5575 +5576 +5577 +5578 +5579 +5580 +5581 +5582 +5583 +5584 +5585 +5586 +5587 +5588 +5589 +5590 +5591 +5592 +5593 +5594 +5595 +5596 +5597 +5598 +5599 +5600 +5601 +5602 +5603 +5604 +5605 +5606 +5607 +5608 +5609 +5610 +5611 +5612 +5613 +5614 +5615 +5616 +5617 +5618 +5619 +5620 +5621 +5622 +5623 +5624 +5625 +5626 +5627 +5628 +5629 +5630 +5631 +5632 +5633 +5634 +5635 +5636 +5637 +5638 +5639 +5640 +5641 +5642 +5643 +5644 +5645 +5646 +5647 +5648 +5649 +5650 +5651 +5652 +5653 +5654 +5655 +5656 +5657 +5658 +5659 +5660 +5661 +5662 +5663 +5664 +5665 +5666 +5667 +5668 +5669 +5670 +5671 +5672 +5673 +5674 +5675 +5676 +5677 +5678 +5679 +5680 +5681 +5682 +5683 +5684 +5685 +5686 +5687 +5688 +5689 +5690 +5691 +5692 +5693 +5694 +5695 +5696 +5697 +5698 +5699 +5700 +5701 +5702 +5703 +5704 +5705 +5706 +5707 +5708 +5709 +5710 +5711 +5712 +5713 +5714 +5715 +5716 +5717 +5718 +5719 +5720 +5721 +5722 +5723 +5724 +5725 +5726 +5727 +5728 +5729 +5730 +5731 +5732 +5733 +5734 +5735 +5736 +5737 +5738 +5739 +5740 +5741 +5742 +5743 +5744 +5745 +5746 +5747 +5748 +5749 +5750 +5751 +5752 +5753 +5754 +5755 +5756 +5757 +5758 +5759 +5760 +5761 +5762 +5763 +5764 +5765 +5766 +5767 +5768 +5769 +5770 +5771 +5772 +5773 +5774 +5775 +5776 +5777 +5778 +5779 +5780 +5781 +5782 +5783 +5784 +5785 +5786 +5787 +5788 +5789 +5790 +5791 +5792 +5793 +5794 +5795 +5796 +5797 +5798 +5799 +5800 +5801 +5802 +5803 +5804 +5805 +5806 +5807 +5808 +5809 +5810 +5811 +5812 +5813 +5814 +5815 +5816 +5817 +5818 +5819 +5820 +5821 +5822 +5823 +5824 +5825 +5826 +5827 +5828 +5829 +5830 +5831 +5832 +5833 +5834 +5835 +5836 +5837 +5838 +5839 +5840 +5841 +5842 +5843 +5844 +5845 +5846 +5847 +5848 +5849 +5850 +5851 +5852 +5853 +5854 +5855 +5856 +5857 +5858 +5859 +5860 +5861 +5862 +5863 +5864 +5865 +5866 +5867 +5868 +5869 +5870 +5871 +5872 +5873 +5874 +5875 +5876 +5877 +5878 +5879 +5880 +5881 +5882 +5883 +5884 +5885 +5886 +5887 +5888 +5889 +5890 +5891 +5892 +5893 +5894 +5895 +5896 +5897 +5898 +5899 +5900 +5901 +5902 +5903 +5904 +5905 +5906 +5907 +5908 +5909 +5910 +5911 +5912 +5913 +5914 +5915 +5916 +5917 +5918 +5919 +5920 +5921 +5922 +5923 +5924 +5925 +5926 +5927 +5928 +5929 +5930 +5931 +5932 +5933 +5934 +5935 +5936 +5937 +5938 +5939 +5940 +5941 +5942 +5943 +5944 +5945 +5946 +5947 +5948 +5949 +5950 +5951 +5952 +5953 +5954 +5955 +5956 +5957 +5958 +5959 +5960 +5961 +5962 +5963 +5964 +5965 +5966 +5967 +5968 +5969 +5970 +5971 +5972 +5973 +5974 +5975 +5976 +5977 +5978 +5979 +5980 +5981 +5982 +5983 +5984 +5985 +5986 +5987 +5988 +5989 +5990 +5991 +5992 +5993 +5994 +5995 +5996 +5997 +5998 +5999 +6000 +6001 +6002 +6003 +6004 +6005 +6006 +6007 +6008 +6009 +6010 +6011 +6012 +6013 +6014 +6015 +6016 +6017 +6018 +6019 +6020 +6021 +6022 +6023 +6024 +6025 +6026 +6027 +6028 +6029 +6030 +6031 +6032 +6033 +6034 +6035 +6036 +6037 +6038 +6039 +6040 +6041 +6042 +6043 +6044 +6045 +6046 +6047 +6048 +6049 +6050 +6051 +6052 +6053 +6054 +6055 +6056 +6057 +6058 +6059 +6060 +6061 +6062 +6063 +6064 +6065 +6066 +6067 +6068 +6069 +6070 +6071 +6072 +6073 +6074 +6075 +6076 +6077 +6078 +6079 +6080 +6081 +6082 +6083 +6084 +6085 +6086 +6087 +6088 +6089 +6090 +6091 +6092 +6093 +6094 +6095 +6096 +6097 +6098 +6099 +6100 +6101 +6102 +6103 +6104 +6105 +6106 +6107 +6108 +6109 +6110 +6111 +6112 +6113 +6114 +6115 +6116 +6117 +6118 +6119 +6120 +6121 +6122 +6123 +6124 +6125 +6126 +6127 +6128 +6129 +6130 +6131 +6132 +6133 +6134 +6135 +6136 +6137 +6138 +6139 +6140 +6141 +6142 +6143 +6144 +6145 +6146 +6147 +6148 +6149 +6150 +6151 +6152 +6153 +6154 +6155 +6156 +6157 +6158 +6159 +6160 +6161 +6162 +6163 +6164 +6165 +6166 +6167 +6168 +6169 +6170 +6171 +6172 +6173 +6174 +6175 +6176 +6177 +6178 +6179 +6180 +6181 +6182 +6183 +6184 +6185 +6186 +6187 +6188 +6189 +6190 +6191 +6192 +6193 +6194 +6195 +6196 +6197 +6198 +6199 +6200 +6201 +6202 +6203 +6204 +6205 +6206 +6207 +6208 +6209 +6210 +6211 +6212 +6213 +6214 +6215 +6216 +6217 +6218 +6219 +6220 +6221 +6222 +6223 +6224 +6225 +6226 +6227 +6228 +6229 +6230 +6231 +6232 +6233 +6234 +6235 +6236 +6237 +6238 +6239 +6240 +6241 +6242 +6243 +6244 +6245 +6246 +6247 +6248 +6249 +6250 +6251 +6252 +6253 +6254 +6255 +6256 +6257 +6258 +6259 +6260 +6261 +6262 +6263 +6264 +6265 +6266 +6267 +6268 +6269 +6270 +6271 +6272 +6273 +6274 +6275 +6276 +6277 +6278 +6279 +6280 +6281 +6282 +6283 +6284 +6285 +6286 +6287 +6288 +6289 +6290 +6291 +6292 +6293 +6294 +6295 +6296 +6297 +6298 +6299 +6300 +6301 +6302 +6303 +6304 +6305 +6306 +6307 +6308 +6309 +6310 +6311 +6312 +6313 +6314 +6315 +6316 +6317 +6318 +6319 +6320 +6321 +6322 +6323 +6324 +6325 +6326 +6327 +6328 +6329 +6330 +6331 +6332 +6333 +6334 +6335 +6336 +6337 +6338 +6339 +6340 +6341 +6342 +6343 +6344 +6345 +6346 +6347 +6348 +6349 +6350 +6351 +6352 +6353 +6354 +6355 +6356 +6357 +6358 +6359 +6360 +6361 +6362 +6363 +6364 +6365 +6366 +6367 +6368 +6369 +6370 +6371 +6372 +6373 +6374 +6375 +6376 +6377 +6378 +6379 +6380 +6381 +6382 +6383 +6384 +6385 +6386 +6387 +6388 +6389 +6390 +6391 +6392 +6393 +6394 +6395 +6396 +6397 +6398 +6399 +6400 +6401 +6402 +6403 +6404 +6405 +6406 +6407 +6408 +6409 +6410 +6411 +6412 +6413 +6414 +6415 +6416 +6417 +6418 +6419 +6420 +6421 +6422 +6423 +6424 +6425 +6426 +6427 +6428 +6429 +6430 +6431 +6432 +6433 +6434 +6435 +6436 +6437 +6438 +6439 +6440 +6441 +6442 +6443 +6444 +6445 +6446 +6447 +6448 +6449 +6450 +6451 +6452 +6453 +6454 +6455 +6456 +6457 +6458 +6459 +6460 +6461 +6462 +6463 +6464 +6465 +6466 +6467 +6468 +6469 +6470 +6471 +6472 +6473 +6474 +6475 +6476 +6477 +6478 +6479 +6480 +6481 +6482 +6483 +6484 +6485 +6486 +6487 +6488 +6489 +6490 +6491 +6492 +6493 +6494 +6495 +6496 +6497 +6498 +6499 +6500 +6501 +6502 +6503 +6504 +6505 +6506 +6507 +6508 +6509 +6510 +6511 +6512 +6513 +6514 +6515 +6516 +6517 +6518 +6519 +6520 +6521 +6522 +6523 +6524 +6525 +6526 +6527 +6528 +6529 +6530 +6531 +6532 +6533 +6534 +6535 +6536 +6537 +6538 +6539 +6540 +6541 +6542 +6543 +6544 +6545 +6546 +6547 +6548 +6549 +6550 +6551 +6552 +6553 +6554 +6555 +6556 +6557 +6558 +6559 +6560 +6561 +6562 +6563 +6564 +6565 +6566 +6567 +6568 +6569 +6570 +6571 +6572 +6573 +6574 +6575 +6576 +6577 +6578 +6579 +6580 +6581 +6582 +6583 +6584 +6585 +6586 +6587 +6588 +6589 +6590 +6591 +6592 +6593 +6594 +6595 +6596 +6597 +6598 +6599 +6600 +6601 +6602 +6603 +6604 +6605 +6606 +6607 +6608 +6609 +6610 +6611 +6612 +6613 +6614 +6615 +6616 +6617 +6618 +6619 +6620 +6621 +6622 +6623 +6624 +6625 +6626 +6627 +6628 +6629 +6630 +6631 +6632 +6633 +6634 +6635 +6636 +6637 +6638 +6639 +6640 +6641 +6642 +6643 +6644 +6645 +6646 +6647 +6648 +6649 +6650 +6651 +6652 +6653 +6654 +6655 +6656 +6657 +6658 +6659 +6660 +6661 +6662 +6663 +6664 +6665 +6666 +6667 +6668 +6669 +6670 +6671 +6672 +6673 +6674 +6675 +6676 +6677 +6678 +6679 +6680 +6681 +6682 +6683 +6684 +6685 +6686 +6687 +6688 +6689 +6690 +6691 +6692 +6693 +6694 +6695 +6696 +6697 +6698 +6699 +6700 +6701 +6702 +6703 +6704 +6705 +6706 +6707 +6708 +6709 +6710 +6711 +6712 +6713 +6714 +6715 +6716 +6717 +6718 +6719 +6720 +6721 +6722 +6723 +6724 +6725 +6726 +6727 +6728 +6729 +6730 +6731 +6732 +6733 +6734 +6735 +6736 +6737 +6738 +6739 +6740 +6741 +6742 +6743 +6744 +6745 +6746 +6747 +6748 +6749 +6750 +6751 +6752 +6753 +6754 +6755 +6756 +6757 +6758 +6759 +6760 +6761 +6762 +6763 +6764 +6765 +6766 +6767 +6768 +6769 +6770 +6771 +6772 +6773 +6774 +6775 +6776 +6777 +6778 +6779 +6780 +6781 +6782 +6783 +6784 +6785 +6786 +6787 +6788 +6789 +6790 +6791 +6792 +6793 +6794 +6795 +6796 +6797 +6798 +6799 +6800 +6801 +6802 +6803 +6804 +6805 +6806 +6807 +6808 +6809 +6810 +6811 +6812 +6813 +6814 +6815 +6816 +6817 +6818 +6819 +6820 +6821 +6822 +6823 +6824 +6825 +6826 +6827 +6828 +6829 +6830 +6831 +6832 +6833 +6834 +6835 +6836 +6837 +6838 +6839 +6840 +6841 +6842 +6843 +6844 +6845 +6846 +6847 +6848 +6849 +6850 +6851 +6852 +6853 +6854 +6855 +6856 +6857 +6858 +6859 +6860 +6861 +6862 +6863 +6864 +6865 +6866 +6867 +6868 +6869 +6870 +6871 +6872 +6873 +6874 +6875 +6876 +6877 +6878 +6879 +6880 +6881 +6882 +6883 +6884 +6885 +6886 +6887 +6888 +6889 +6890 +6891 +6892 +6893 +6894 +6895 +6896 +6897 +6898 +6899 +6900 +6901 +6902 +6903 +6904 +6905 +6906 +6907 +6908 +6909 +6910 +6911 +6912 +6913 +6914 +6915 +6916 +6917 +6918 +6919 +6920 +6921 +6922 +6923 +6924 +6925 +6926 +6927 +6928 +6929 +6930 +6931 +6932 +6933 +6934 +6935 +6936 +6937 +6938 +6939 +6940 +6941 +6942 +6943 +6944 +6945 +6946 +6947 +6948 +6949 +6950 +6951 +6952 +6953 +6954 +6955 +6956 +6957 +6958 +6959 +6960 +6961 +6962 +6963 +6964 +6965 +6966 +6967 +6968 +6969 +6970 +6971 +6972 +6973 +6974 +6975 +6976 +6977 +6978 +6979 +6980 +6981 +6982 +6983 +6984 +6985 +6986 +6987 +6988 +6989 +6990 +6991 +6992 +6993 +6994 +6995 +6996 +6997 +6998 +6999 +7000 +7001 +7002 +7003 +7004 +7005 +7006 +7007 +7008 +7009 +7010 +7011 +7012 +7013 +7014 +7015 +7016 +7017 +7018 +7019 +7020 +7021 +7022 +7023 +7024 +7025 +7026 +7027 +7028 +7029 +7030 +7031 +7032 +7033 +7034 +7035 +7036 +7037 +7038 +7039 +7040 +7041 +7042 +7043 +7044 +7045 +7046 +7047 +7048 +7049 +7050 +7051 +7052 +7053 +7054 +7055 +7056 +7057 +7058 +7059 +7060 +7061 +7062 +7063 +7064 +7065 +7066 +7067 +7068 +7069 +7070 +7071 +7072 +7073 +7074 +7075 +7076 +7077 +7078 +7079 +7080 +7081 +7082 +7083 +7084 +7085 +7086 +7087 +7088 +7089 +7090 +7091 +7092 +7093 +7094 +7095 +7096 +7097 +7098 +7099 +7100 +7101 +7102 +7103 +7104 +7105 +7106 +7107 +7108 +7109 +7110 +7111 +7112 +7113 +7114 +7115 +7116 +7117 +7118 +7119 +7120 +7121 +7122 +7123 +7124 +7125 +7126 +7127 +7128 +7129 +7130 +7131 +7132 +7133 +7134 +7135 +7136 +7137 +7138 +7139 +7140 +7141 +7142 +7143 +7144 +7145 +7146 +7147 +7148 +7149 +7150 +7151 +7152 +7153 +7154 +7155 +7156 +7157 +7158 +7159 +7160 +7161 +7162 +7163 +7164 +7165 +7166 +7167 +7168 +7169 +7170 +7171 +7172 +7173 +7174 +7175 +7176 +7177 +7178 +7179 +7180 +7181 +7182 +7183 +7184 +7185 +7186 +7187 +7188 +7189 +7190 +7191 +7192 +7193 +7194 +7195 +7196 +7197 +7198 +7199 +7200 +7201 +7202 +7203 +7204 +7205 +7206 +7207 +7208 +7209 +7210 +7211 +7212 +7213 +7214 +7215 +7216 +7217 +7218 +7219 +7220 +7221 +7222 +7223 +7224 +7225 +7226 +7227 +7228 +7229 +7230 +7231 +7232 +7233 +7234 +7235 +7236 +7237 +7238 +7239 +7240 +7241 +7242 +7243 +7244 +7245 +7246 +7247 +7248 +7249 +7250 +7251 +7252 +7253 +7254 +7255 +7256 +7257 +7258 +7259 +7260 +7261 +7262 +7263 +7264 +7265 +7266 +7267 +7268 +7269 +7270 +7271 +7272 +7273 +7274 +7275 +7276 +7277 +7278 +7279 +7280 +7281 +7282 +7283 +7284 +7285 +7286 +7287 +7288 +7289 +7290 +7291 +7292 +7293 +7294 +7295 +7296 +7297 +7298 +7299 +7300 +7301 +7302 +7303 +7304 +7305 +7306 +7307 +7308 +7309 +7310 +7311 +7312 +7313 +7314 +7315 +7316 +7317 +7318 +7319 +7320 +7321 +7322 +7323 +7324 +7325 +7326 +7327 +7328 +7329 +7330 +7331 +7332 +7333 +7334 +7335 +7336 +7337 +7338 +7339 +7340 +7341 +7342 +7343 +7344 +7345 +7346 +7347 +7348 +7349 +7350 +7351 +7352 +7353 +7354 +7355 +7356 +7357 +7358 +7359 +7360 +7361 +7362 +7363 +7364 +7365 +7366 +7367 +7368 +7369 +7370 +7371 +7372 +7373 +7374 +7375 +7376 +7377 +7378 +7379 +7380 +7381 +7382 +7383 +7384 +7385 +7386 +7387 +7388 +7389 +7390 +7391 +7392 +7393 +7394 +7395 +7396 +7397 +7398 +7399 +7400 +7401 +7402 +7403 +7404 +7405 +7406 +7407 +7408 +7409 +7410 +7411 +7412 +7413 +7414 +7415 +7416 +7417 +7418 +7419 +7420 +7421 +7422 +7423 +7424 +7425 +7426 +7427 +7428 +7429 +7430 +7431 +7432 +7433 +7434 +7435 +7436 +7437 +7438 +7439 +7440 +7441 +7442 +7443 +7444 +7445 +7446 +7447 +7448 +7449 +7450 +7451 +7452 +7453 +7454 +7455 +7456 +7457 +7458 +7459 +7460 +7461 +7462 +7463 +7464 +7465 +7466 +7467 +7468 +7469 +7470 +7471 +7472 +7473 +7474 +7475 +7476 +7477 +7478 +7479 +7480 +7481 +7482 +7483 +7484 +7485 +7486 +7487 +7488 +7489 +7490 +7491 +7492 +7493 +7494 +7495 +7496 +7497 +7498 +7499 +7500 +7501 +7502 +7503 +7504 +7505 +7506 +7507 +7508 +7509 +7510 +7511 +7512 +7513 +7514 +7515 +7516 +7517 +7518 +7519 +7520 +7521 +7522 +7523 +7524 +7525 +7526 +7527 +7528 +7529 +7530 +7531 +7532 +7533 +7534 +7535 +7536 +7537 +7538 +7539 +7540 +7541 +7542 +7543 +7544 +7545 +7546 +7547 +7548 +7549 +7550 +7551 +7552 +7553 +7554 +7555 +7556 +7557 +7558 +7559 +7560 +7561 +7562 +7563 +7564 +7565 +7566 +7567 +7568 +7569 +7570 +7571 +7572 +7573 +7574 +7575 +7576 +7577 +7578 +7579 +7580 +7581 +7582 +7583 +7584 +7585 +7586 +7587 +7588 +7589 +7590 +7591 +7592 +7593 +7594 +7595 +7596 +7597 +7598 +7599 +7600 +7601 +7602 +7603 +7604 +7605 +7606 +7607 +7608 +7609 +7610 +7611 +7612 +7613 +7614 +7615 +7616 +7617 +7618 +7619 +7620 +7621 +7622 +7623 +7624 +7625 +7626 +7627 +7628 +7629 +7630 +7631 +7632 +7633 +7634 +7635 +7636 +7637 +7638 +7639 +7640 +7641 +7642 +7643 +7644 +7645 +7646 +7647 +7648 +7649 +7650 +7651 +7652 +7653 +7654 +7655 +7656 +7657 +7658 +7659 +7660 +7661 +7662 +7663 +7664 +7665 +7666 +7667 +7668 +7669 +7670 +7671 +7672 +7673 +7674 +7675 +7676 +7677 +7678 +7679 +7680 +7681 +7682 +7683 +7684 +7685 +7686 +7687 +7688 +7689 +7690 +7691 +7692 +7693 +7694 +7695 +7696 +7697 +7698 +7699 +7700 +7701 +7702 +7703 +7704 +7705 +7706 +7707 +7708 +7709 +7710 +7711 +7712 +7713 +7714 +7715 +7716 +7717 +7718 +7719 +7720 +7721 +7722 +7723 +7724 +7725 +7726 +7727 +7728 +7729 +7730 +7731 +7732 +7733 +7734 +7735 +7736 +7737 +7738 +7739 +7740 +7741 +7742 +7743 +7744 +7745 +7746 +7747 +7748 +7749 +7750 +7751 +7752 +7753 +7754 +7755 +7756 +7757 +7758 +7759 +7760 +7761 +7762 +7763 +7764 +7765 +7766 +7767 +7768 +7769 +7770 +7771 +7772 +7773 +7774 +7775 +7776 +7777 +7778 +7779 +7780 +7781 +7782 +7783 +7784 +7785 +7786 +7787 +7788 +7789 +7790 +7791 +7792 +7793 +7794 +7795 +7796 +7797 +7798 +7799 +7800 +7801 +7802 +7803 +7804 +7805 +7806 +7807 +7808 +7809 +7810 +7811 +7812 +7813 +7814 +7815 +7816 +7817 +7818 +7819 +7820 +7821 +7822 +7823 +7824 +7825 +7826 +7827 +7828 +7829 +7830 +7831 +7832 +7833 +7834 +7835 +7836 +7837 +7838 +7839 +7840 +7841 +7842 +7843 +7844 +7845 +7846 +7847 +7848 +7849 +7850 +7851 +7852 +7853 +7854 +7855 +7856 +7857 +7858 +7859 +7860 +7861 +7862 +7863 +7864 +7865 +7866 +7867 +7868 +7869 +7870 +7871 +7872 +7873 +7874 +7875 +7876 +7877 +7878 +7879 +7880 +7881 +7882 +7883 +7884 +7885 +7886 +7887 +7888 +7889 +7890 +7891 +7892 +7893 +7894 +7895 +7896 +7897 +7898 +7899 +7900 +7901 +7902 +7903 +7904 +7905 +7906 +7907 +7908 +7909 +7910 +7911 +7912 +7913 +7914 +7915 +7916 +7917 +7918 +7919 +7920 +7921 +7922 +7923 +7924 +7925 +7926 +7927 +7928 +7929 +7930 +7931 +7932 +7933 +7934 +7935 +7936 +7937 +7938 +7939 +7940 +7941 +7942 +7943 +7944 +7945 +7946 +7947 +7948 +7949 +7950 +7951 +7952 +7953 +7954 +7955 +7956 +7957 +7958 +7959 +7960 +7961 +7962 +7963 +7964 +7965 +7966 +7967 +7968 +7969 +7970 +7971 +7972 +7973 +7974 +7975 +7976 +7977 +7978 +7979 +7980 +7981 +7982 +7983 +7984 +7985 +7986 +7987 +7988 +7989 +7990 +7991 +7992 +7993 +7994 +7995 +7996 +7997 +7998 +7999 +8000 diff --git a/test/static/executor/test_data/employees_200.csv b/test/static/executor/test_data/employees_200.csv new file mode 100644 index 0000000..9bff65f --- /dev/null +++ b/test/static/executor/test_data/employees_200.csv @@ -0,0 +1,201 @@ +id:int,department_id:int +1 902 +2 258 +3 602 +4 479 +5 165 +6 564 +7 7 +8 699 +9 828 +10 197 +11 427 +12 923 +13 52 +14 748 +15 204 +16 169 +17 510 +18 897 +19 16 +20 478 +21 249 +22 816 +23 750 +24 580 +25 995 +26 623 +27 759 +28 566 +29 551 +30 715 +31 144 +32 343 +33 772 +34 193 +35 391 +36 172 +37 786 +38 532 +39 125 +40 135 +41 266 +42 983 +43 541 +44 590 +45 38 +46 968 +47 911 +48 927 +49 847 +50 735 +51 363 +52 256 +53 831 +54 488 +55 347 +56 932 +57 675 +58 640 +59 631 +60 478 +61 205 +62 224 +63 514 +64 616 +65 705 +66 177 +67 451 +68 230 +69 966 +70 134 +71 251 +72 182 +73 407 +74 747 +75 199 +76 551 +77 326 +78 425 +79 243 +80 629 +81 769 +82 425 +83 300 +84 721 +85 77 +86 283 +87 191 +88 974 +89 770 +90 539 +91 64 +92 88 +93 188 +94 406 +95 15 +96 864 +97 541 +98 325 +99 903 +100 534 +101 636 +102 761 +103 11 +104 266 +105 669 +106 457 +107 990 +108 561 +109 919 +110 623 +111 763 +112 591 +113 130 +114 186 +115 69 +116 323 +117 10 +118 4 +119 458 +120 235 +121 185 +122 368 +123 416 +124 879 +125 61 +126 570 +127 342 +128 340 +129 369 +130 99 +131 718 +132 728 +133 216 +134 682 +135 316 +136 706 +137 42 +138 3 +139 319 +140 142 +141 383 +142 596 +143 446 +144 660 +145 602 +146 305 +147 426 +148 11 +149 808 +150 722 +151 8 +152 200 +153 607 +154 556 +155 17 +156 378 +157 348 +158 40 +159 37 +160 644 +161 916 +162 880 +163 26 +164 922 +165 822 +166 486 +167 984 +168 787 +169 115 +170 679 +171 95 +172 838 +173 507 +174 749 +175 170 +176 460 +177 273 +178 575 +179 498 +180 814 +181 102 +182 434 +183 130 +184 255 +185 665 +186 202 +187 761 +188 852 +189 422 +190 435 +191 410 +192 347 +193 108 +194 783 +195 107 +196 206 +197 634 +198 131 +199 894 +200 9 From 5d51dc2cbba121fc2c909e0a348e7fbabf5119a7 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sat, 10 Jan 2026 22:53:30 +0300 Subject: [PATCH 38/43] Add benchmarks analysis --- .gitignore | 1 + README.md | 16 + benchmarks/analysis.ipynb | 920 ++++++++++++++++++ benchmarks/flamegraph-jit.svg | 1642 +++++++++++++++++++++++++++++++++ benchmarks/main.cpp | 9 +- benchmarks/results-01.json | 294 ++++++ flake.nix | 2 + flamegraph.svg | 0 8 files changed, 2881 insertions(+), 3 deletions(-) create mode 100644 benchmarks/analysis.ipynb create mode 100644 benchmarks/flamegraph-jit.svg create mode 100644 benchmarks/results-01.json create mode 100644 flamegraph.svg diff --git a/.gitignore b/.gitignore index 58ef3df..f3eafaf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ **/codegen/ /build-sanitizers/ /build-release/ +FlameGraph/ diff --git a/README.md b/README.md index e69de29..111b8f3 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,16 @@ +```sh +docker run --rm -it --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword postgres +docker exec -it some-postgres psql -U postgres +``` + + +```sh +git clone https://github.com/brendangregg/FlameGraph.git + +sed -i 's|#!/usr/bin/perl -w|#!/usr/bin/env perl|' FlameGraph/stackcollapse-perf.pl +sed -i 's|#!/usr/bin/perl -w|#!/usr/bin/env perl|' FlameGraph/flamegraph.pl + +perf record -F 99 -g ./build-release/bin/benchmarks --benchmark_filter="BM_SQL benchmarks/flamegraph-jit.svg +``` diff --git a/benchmarks/analysis.ipynb b/benchmarks/analysis.ipynb new file mode 100644 index 0000000..ae34f8f --- /dev/null +++ b/benchmarks/analysis.ipynb @@ -0,0 +1,920 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 51, + "id": "999ec94f", + "metadata": {}, + "outputs": [], + "source": [ + "data = \"\"\"\n", + "{\n", + " \"context\": {\n", + " \"date\": \"2026-01-10T22:17:41+03:00\",\n", + " \"host_name\": \"nixos\",\n", + " \"executable\": \"./build-release/bin/benchmarks\",\n", + " \"num_cpus\": 8,\n", + " \"mhz_per_cpu\": 4200,\n", + " \"cpu_scaling_enabled\": true,\n", + " \"caches\": [\n", + " {\n", + " \"type\": \"Data\",\n", + " \"level\": 1,\n", + " \"size\": 49152,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Instruction\",\n", + " \"level\": 1,\n", + " \"size\": 32768,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Unified\",\n", + " \"level\": 2,\n", + " \"size\": 1310720,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Unified\",\n", + " \"level\": 3,\n", + " \"size\": 8388608,\n", + " \"num_sharing\": 8\n", + " }\n", + " ],\n", + " \"load_avg\": [0.936523,0.910156,0.82959],\n", + " \"library_version\": \"v1.9.0\",\n", + " \"library_build_type\": \"release\",\n", + " \"json_schema_version\": 1\n", + " },\n", + " \"benchmarks\": [\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 0,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 37973,\n", + " \"real_time\": 1.8439718747488580e+04,\n", + " \"cpu_time\": 1.8393154688857870e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 1,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 37635,\n", + " \"real_time\": 1.8646254444011862e+04,\n", + " \"cpu_time\": 1.8605846924405472e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 2,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 11616,\n", + " \"real_time\": 6.1504978047441888e+04,\n", + " \"cpu_time\": 6.1064501205234126e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 3,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 11736,\n", + " \"real_time\": 5.8408048312897139e+04,\n", + " \"cpu_time\": 5.8085960889570553e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 4,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 9930,\n", + " \"real_time\": 7.0793681671783255e+04,\n", + " \"cpu_time\": 7.0392283987915405e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 5,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 10340,\n", + " \"real_time\": 6.7845838684643837e+04,\n", + " \"cpu_time\": 6.7519644680851081e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 6,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 959,\n", + " \"real_time\": 7.3587727215961309e+05,\n", + " \"cpu_time\": 7.3293833785192831e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 7,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 1324,\n", + " \"real_time\": 5.3172040483321249e+05,\n", + " \"cpu_time\": 5.2990015785498510e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 8,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 959,\n", + " \"real_time\": 7.3307096767407400e+05,\n", + " \"cpu_time\": 7.3027849426485947e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 9,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 1283,\n", + " \"real_time\": 5.3211894699760526e+05,\n", + " \"cpu_time\": 5.3048981060015631e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 10,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 615,\n", + " \"real_time\": 1.1423289658545756e+06,\n", + " \"cpu_time\": 1.1391460959349603e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 11,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 764,\n", + " \"real_time\": 9.1461425262047106e+05,\n", + " \"cpu_time\": 9.1169844371727761e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 12,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 359,\n", + " \"real_time\": 1.9482222869099684e+06,\n", + " \"cpu_time\": 1.9423905626740968e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 13,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 417,\n", + " \"real_time\": 1.6953180815330239e+06,\n", + " \"cpu_time\": 1.6724347146282962e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 14,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 198,\n", + " \"real_time\": 3.5503262929110364e+06,\n", + " \"cpu_time\": 3.5421274595959657e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 15,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 220,\n", + " \"real_time\": 3.1995414954508306e+06,\n", + " \"cpu_time\": 3.1910090772727318e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 16,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 104,\n", + " \"real_time\": 6.7452504230669336e+06,\n", + " \"cpu_time\": 6.7309672692307681e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL\",\n", + " \"family_index\": 17,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 113,\n", + " \"real_time\": 6.2372058495609770e+06,\n", + " \"cpu_time\": 6.2177674513274478e+06,\n", + " \"time_unit\": \"ns\"\n", + " }\n", + " ]\n", + "}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "39950eb9", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "data = json.loads(data) " + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "eb39d9d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
executorqueryreal_timecpu_time
0InterpretedExpressionExecutorkSimpleSelectSmall1.843972e+041.839315e+04
1CachedJitCompiledExpressionExecutorkSimpleSelectSmall1.864625e+041.860585e+04
2InterpretedExpressionExecutorkJoinSmall6.150498e+046.106450e+04
3CachedJitCompiledExpressionExecutorkJoinSmall5.840805e+045.808596e+04
4InterpretedExpressionExecutorkComplex57.079368e+047.039228e+04
5CachedJitCompiledExpressionExecutorkComplex56.784584e+046.751964e+04
6InterpretedExpressionExecutorkComplex5007.358773e+057.329383e+05
7CachedJitCompiledExpressionExecutorkComplex5005.317204e+055.299002e+05
8InterpretedExpressionExecutorkComplex10007.330710e+057.302785e+05
9CachedJitCompiledExpressionExecutorkComplex10005.321189e+055.304898e+05
10InterpretedExpressionExecutorkComplex20001.142329e+061.139146e+06
11CachedJitCompiledExpressionExecutorkComplex20009.146143e+059.116984e+05
12InterpretedExpressionExecutorkComplex40001.948222e+061.942391e+06
13CachedJitCompiledExpressionExecutorkComplex40001.695318e+061.672435e+06
14InterpretedExpressionExecutorkComplex80003.550326e+063.542127e+06
15CachedJitCompiledExpressionExecutorkComplex80003.199541e+063.191009e+06
16InterpretedExpressionExecutorkComplex160006.745250e+066.730967e+06
17CachedJitCompiledExpressionExecutorkComplex160006.237206e+066.217767e+06
\n", + "" + ], + "text/plain": [ + " executor query real_time \\\n", + "0 InterpretedExpressionExecutor kSimpleSelectSmall 1.843972e+04 \n", + "1 CachedJitCompiledExpressionExecutor kSimpleSelectSmall 1.864625e+04 \n", + "2 InterpretedExpressionExecutor kJoinSmall 6.150498e+04 \n", + "3 CachedJitCompiledExpressionExecutor kJoinSmall 5.840805e+04 \n", + "4 InterpretedExpressionExecutor kComplex5 7.079368e+04 \n", + "5 CachedJitCompiledExpressionExecutor kComplex5 6.784584e+04 \n", + "6 InterpretedExpressionExecutor kComplex500 7.358773e+05 \n", + "7 CachedJitCompiledExpressionExecutor kComplex500 5.317204e+05 \n", + "8 InterpretedExpressionExecutor kComplex1000 7.330710e+05 \n", + "9 CachedJitCompiledExpressionExecutor kComplex1000 5.321189e+05 \n", + "10 InterpretedExpressionExecutor kComplex2000 1.142329e+06 \n", + "11 CachedJitCompiledExpressionExecutor kComplex2000 9.146143e+05 \n", + "12 InterpretedExpressionExecutor kComplex4000 1.948222e+06 \n", + "13 CachedJitCompiledExpressionExecutor kComplex4000 1.695318e+06 \n", + "14 InterpretedExpressionExecutor kComplex8000 3.550326e+06 \n", + "15 CachedJitCompiledExpressionExecutor kComplex8000 3.199541e+06 \n", + "16 InterpretedExpressionExecutor kComplex16000 6.745250e+06 \n", + "17 CachedJitCompiledExpressionExecutor kComplex16000 6.237206e+06 \n", + "\n", + " cpu_time \n", + "0 1.839315e+04 \n", + "1 1.860585e+04 \n", + "2 6.106450e+04 \n", + "3 5.808596e+04 \n", + "4 7.039228e+04 \n", + "5 6.751964e+04 \n", + "6 7.329383e+05 \n", + "7 5.299002e+05 \n", + "8 7.302785e+05 \n", + "9 5.304898e+05 \n", + "10 1.139146e+06 \n", + "11 9.116984e+05 \n", + "12 1.942391e+06 \n", + "13 1.672435e+06 \n", + "14 3.542127e+06 \n", + "15 3.191009e+06 \n", + "16 6.730967e+06 \n", + "17 6.217767e+06 " + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import re\n", + "from typing import Any\n", + "\n", + "benchmarks: list[dict[str, Any]] = list()\n", + "\n", + "for benchmark in data['benchmarks']:\n", + " match = re.match('BM_SQL<(.*), (.*)>', benchmark['name'])\n", + " if match is None:\n", + " raise ValueError(\"invalid benchmark name\")\n", + " executor, query = match.group(1), match.group(2)\n", + " real_time, cpu_time = benchmark['real_time'], benchmark['cpu_time']\n", + " time_unit = benchmark['time_unit']\n", + " if time_unit != 'ns':\n", + " raise ValueError(\"time_unit is not ns\")\n", + " benchmarks.append({\n", + " 'executor': executor,\n", + " 'query': query,\n", + " 'real_time': real_time,\n", + " 'cpu_time': cpu_time,\n", + " })\n", + " \n", + "import pandas as pd\n", + "\n", + "benchmarks = pd.DataFrame(benchmarks)\n", + "\n", + "benchmarks\n" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "b31f03cf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
executorqueryreal_timecpu_time
0InterpretedExpressionExecutor57.079368e+047.039228e+04
1CachedJitCompiledExpressionExecutor56.784584e+046.751964e+04
2InterpretedExpressionExecutor5007.358773e+057.329383e+05
3CachedJitCompiledExpressionExecutor5005.317204e+055.299002e+05
4InterpretedExpressionExecutor10007.330710e+057.302785e+05
5CachedJitCompiledExpressionExecutor10005.321189e+055.304898e+05
6InterpretedExpressionExecutor20001.142329e+061.139146e+06
7CachedJitCompiledExpressionExecutor20009.146143e+059.116984e+05
8InterpretedExpressionExecutor40001.948222e+061.942391e+06
9CachedJitCompiledExpressionExecutor40001.695318e+061.672435e+06
10InterpretedExpressionExecutor80003.550326e+063.542127e+06
11CachedJitCompiledExpressionExecutor80003.199541e+063.191009e+06
12InterpretedExpressionExecutor160006.745250e+066.730967e+06
13CachedJitCompiledExpressionExecutor160006.237206e+066.217767e+06
\n", + "
" + ], + "text/plain": [ + " executor query real_time cpu_time\n", + "0 InterpretedExpressionExecutor 5 7.079368e+04 7.039228e+04\n", + "1 CachedJitCompiledExpressionExecutor 5 6.784584e+04 6.751964e+04\n", + "2 InterpretedExpressionExecutor 500 7.358773e+05 7.329383e+05\n", + "3 CachedJitCompiledExpressionExecutor 500 5.317204e+05 5.299002e+05\n", + "4 InterpretedExpressionExecutor 1000 7.330710e+05 7.302785e+05\n", + "5 CachedJitCompiledExpressionExecutor 1000 5.321189e+05 5.304898e+05\n", + "6 InterpretedExpressionExecutor 2000 1.142329e+06 1.139146e+06\n", + "7 CachedJitCompiledExpressionExecutor 2000 9.146143e+05 9.116984e+05\n", + "8 InterpretedExpressionExecutor 4000 1.948222e+06 1.942391e+06\n", + "9 CachedJitCompiledExpressionExecutor 4000 1.695318e+06 1.672435e+06\n", + "10 InterpretedExpressionExecutor 8000 3.550326e+06 3.542127e+06\n", + "11 CachedJitCompiledExpressionExecutor 8000 3.199541e+06 3.191009e+06\n", + "12 InterpretedExpressionExecutor 16000 6.745250e+06 6.730967e+06\n", + "13 CachedJitCompiledExpressionExecutor 16000 6.237206e+06 6.217767e+06" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "benchmarks = benchmarks[benchmarks['query'].str.contains('Complex')].copy()\n", + "benchmarks['query'] = benchmarks['query'].str.extract(r'(\\d+)').astype(int)\n", + "benchmarks.sort_values(by='query', inplace=True)\n", + "benchmarks.reset_index(drop=True, inplace=True)\n", + "benchmarks" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "deac9830", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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M1Olp65Qamoqb/dFREREpEElDnbvvfce1q9fLz1XKBRQKpVYuHAhOnTooNbiiIiIiKj4SjwUu3DhQnTq1AmnT59Gbm4upkyZggsXLiA1NRVRUVGlUSMRERERFUOJe+waNWqEK1euoF27dujZsycyMzPRu3dvnD17FrVr1y6NGomIiIioGN7olmKmpqaYMWOGumshIiIiorfwRsEuOzsbsbGxSE5OhlKpVJn30UcfqaUwIiIiIiqZEge7vXv3YvDgwfjnn3+KzFMoFCgoKFBLYURERERUMiU+xu6rr75Cv379kJSUBKVSqfJgqCMiIiLSnBIHuwcPHmDChAmoVq1aadRDRERERG+oxMGub9++iIyMLIVSiIiIiOhtlPgYuxUrVqBfv344evQoXFxcoKOjozJ/zJgxaiuOiIiIiIqvxMHuhx9+wP79+1G5cmVERkZCoVBI8xQKBYMdERHRuxJgWgrbTFP/NumdKXGwmzFjBgIDAzFt2jRoaZV4JJeIiIiISkmJk1lubi769+/PUEdERERUxpQ4nfn6+mLr1q2lUQsRERERvYUSD8UWFBRg4cKF2LdvH1xdXYucPLFkyRK1FUdERERExVfiYHf+/Hm4ubkBAOLi4lTmPXsiBRERERG9WyUOdocOHSqNOoiIiIjoLfEMCCIiIiKZKFaPXe/evREREQETExP07t37lctu27ZNLYURERERUckUK9iZmppKx8+ZmJjwWDoiIiKiMqhYwS48PFz6OSIiorRqISIiIqK3UOJj7Dp27IhHjx4VmZ6eno6OHTuqoyYiIiIiegMlDnaRkZHIzc0tMj07OxtHjx5VS1FEREREVHLFvtxJbGys9PPFixdx//596XlBQQH27t0LOzs79VZHRERERMVW7GDXpEkTKBQKKBSKFw656uvrY/ny5WotjoiIiIiKr9jB7saNGxBCoFatWjh58iQsLS2lebq6urCysoK2tnapFElEREREr1fsYFejRg0AgFKpLLViiIiIiOjN8c4TRERERDJR4nvFEhER0ZtxnLZLrdtLrKzWzZEMsMeOiIiISCYY7IiIiIhkothDsenp6S+cbmhoyLNhiYiIiMqAYvfYmZmZoUqVKkUe+vr6qFu3LtasWVOadRIRERHRaxS7x+7QoUMvnP7o0SNER0dj8uTJqFSpEoYOHVrsFw8JCUFISAgSExMBAA0bNsTs2bPRvXv3Ym+DiIiIiJ4qdrBr3779S+f17NkTjo6OWL58eYmCnb29PebPnw8nJycIIbBu3Tr07NkTZ8+eRcOGDYu9HSIiIiJS48kT7du3x9WrV0u0zocffggvLy84OTnB2dkZc+fOhZGREY4fP66usoiIiIgqDLVdxy4tLQ2mpqZvvH5BQQF++uknZGZmwt3dXV1lEREREVUYagl2eXl5+Pbbb9GqVasSr3v+/Hm4u7sjOzsbRkZG2L59Oxo0aPDCZXNycpCTkyM9f9mZukREREQVUbGDXe/evV84PS0tDRcuXIBCocDRo0dLXEDdunURExODtLQ0/Pzzz/D19cXhw4dfGO6CgoIQGBhY4tcgIiIiqgiKHexeNszq4OCAPn36YNCgQW80FKurq4s6deoAAJo1a4ZTp04hODgYoaGhRZadPn06JkyYID1PT0+Hg4NDiV+TiIiISI6KHezCw8NLsw6JUqlUGW59lp6eHvT09N5JHURERETlTYmOsTt+/Dh27NiB3NxcdOrUCd26dXurF58+fTq6d++O6tWr4/Hjx9i8eTMiIyOxb9++t9ouERERUUVU7GD3888/o3///tDX14eOjg6WLFmCBQsWYNKkSW/84snJyRg8eDCSkpJgamoKV1dX7Nu3D126dHnjbRIRERFVVMUOdkFBQRgxYgRWrlwJbW1tBAUFYd68eW8V7NauXfvG6xIRERGRqmJfoPjy5cuYNGkStLW1AQATJ07E48ePkZycXGrFEREREVHxFTvYZWVlwcTERHquq6uLypUrIyMjo1QKIyIiIqKSKdHJE2FhYTAyMpKe5+fnIyIiAhYWFtK0MWPGqK86IiIiIiq2Yge76tWrY82aNSrTrK2tsWHDBum5QqFgsCMiIiLSkGIHu8TExFIsg4iIiIjeVrGPsSMiIiKisq3Ywe7PP/9EgwYNkJ6eXmReWloaGjZsiCNHjqi1OCIiIiIqvmIHu6VLl2LEiBEqZ8YWMjU1xRdffIH//ve/ai2OiIiIiIqv2MHu3Llzr7yFWNeuXREdHa2WooiIiIio5Iod7B48eAAdHZ2Xzq9UqRIePnyolqKIiIiIqOSKHezs7OwQFxf30vmxsbGwsbFRS1FEREREVHLFDnZeXl6YNWsWsrOzi8x78uQJ/P390aNHD7UWR0RERETFV+zr2M2cORPbtm2Ds7Mz/Pz8ULduXQDApUuXsHLlShQUFGDGjBmlVigRERERvVqxg121atXw999/48svv8T06dMhhADw9G4Tnp6eWLlyJapVq1ZqhRIRERHRq5XoXrE1atTA7t278e+//+Lq1asQQsDJyQlVqlQprfqIiIiIqJhKFOwKValSBS1atFB3LURERET0FnhLMSIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZYLAjIiIikgkGOyIiIiKZ0GiwCwoKQosWLWBsbAwrKyt4e3vj8uXLmiyJiIiIqNzSaLA7fPgwRo8ejePHj+PAgQPIy8tD165dkZmZqcmyiIiIiMqlSpp88b1796o8j4iIgJWVFaKjo/H+++9rqCoiIiKi8qlMHWOXlpYGADA3N9dwJURERETlj0Z77J6lVCoxbtw4tG3bFo0aNXrhMjk5OcjJyZGep6env6vyiIiIiMq8MtNjN3r0aMTFxWHLli0vXSYoKAimpqbSw8HB4R1WSERERFS2lYlg5+fnh507d+LQoUOwt7d/6XLTp09HWlqa9Lh9+/Y7rJKIiIiobNPoUKwQAl999RW2b9+OyMhI1KxZ85XL6+npQU9P7x1VR0RERFS+aDTYjR49Gps3b8Zvv/0GY2Nj3L9/HwBgamoKfX19TZZGRESlLcBUzdtLU+/2iMohjQ7FhoSEIC0tDR4eHrCxsZEeW7du1WRZREREROWSxodiiYiIiEg9ysTJE0RERET09hjsiIiIiGSCwY6IiIhIJhjsiIiIiGSCwY6IiIhIJhjsiIiIiGSCwY6IiIhIJhjsiIiIiGSCwY6IiIhIJhjsiIiIiGRCo7cUIyKi8sFx2i61bzOxsto3SVThsceOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYqaboAIqJyJ8BUzdtLU+/2iKjCYo8dERERkUww2BERERHJhEaD3ZEjR/Dhhx/C1tYWCoUCv/76qybLISIiIirXNBrsMjMz0bhxY6xcuVKTZRARERHJgkZPnujevTu6d++uyRKIiIiIZKNcnRWbk5ODnJwc6Xl6eroGqyEiIiIqW8rVyRNBQUEwNTWVHg4ODpouiYiIiKjMKFfBbvr06UhLS5Met2/f1nRJRERERGVGuRqK1dPTg56enqbLICIiIiqTylWPHRERERG9nEZ77DIyMnD16lXp+Y0bNxATEwNzc3NUr15dg5URERERlT8aDXanT59Ghw4dpOcTJkwAAPj6+iIiIkJDVRGR3DhO26XW7SVWVuvmiIjURqPBzsPDA0IITZZAREREJBs8xo6IiIhIJhjsiIiIiGSCwY6IiIhIJhjsiIiIiGSCwY6IiIhIJhjsiIiIiGSCwY6IiIhIJsrVvWKpggswVfP20tS7vfJA3W0IVMx2JCIqo9hjR0RERCQTDHZEREREMsFgR0RERCQTDHZEREREMsFgR0RERCQTDHZEREREMsFgR0RERCQTDHZEREREMsFgR0RERCQTvPMElQrHabvUvs3EymrfZJmn7nasiG1IRFSRsMeOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkgsGOiIiISCYY7IiIiIhkokwEu5UrV8LR0RGVK1dGq1atcPLkSU2XRERERFTuVNJ0AVu3bsWECROwevVqtGrVCkuXLoWnpycuX74MKysrjdTkOG2XWreXWHmgWreHgDT1bo+IiIhkQeM9dkuWLMGIESMwdOhQNGjQAKtXr4aBgQG+//57TZdGREREVK5oNNjl5uYiOjoanTt3lqZpaWmhc+fOOHbsmAYrIyIiIip/NDoU+88//6CgoADVqlVTmV6tWjVcunSpyPI5OTnIycmRnqelPR2STE9PV2tdypwstW4vXSHUuj2oeX9Lg7rbEGA7qoPa2xBgO6plgxWvDQG2ozrwb1o9yno7FuYcIV5fp8aPsSuJoKAgBAYGFpnu4OCggWqKz1TdG5yv9i2WC2zHt1cqe8x2fHsVsA0BtqM68G9aPcpLOz5+/Bimpq/erkaDnYWFBbS1tfHgwQOV6Q8ePIC1tXWR5adPn44JEyZIz5VKJVJTU1G1alUoFIpSr/dNpKenw8HBAbdv34aJiYmmyym32I5vj22oHmxH9WA7vj22oXqUh3YUQuDx48ewtbV97bIaDXa6urpo1qwZDh48CG9vbwBPw9rBgwfh5+dXZHk9PT3o6empTDMzM3sHlb49ExOTMvuGKU/Yjm+PbagebEf1YDu+PbahepT1dnxdT10hjQ/FTpgwAb6+vmjevDlatmyJpUuXIjMzE0OHDtV0aURERETlisaDXf/+/fHw4UPMnj0b9+/fR5MmTbB3794iJ1QQERER0atpPNgBgJ+f3wuHXuVAT08P/v7+RYaQqWTYjm+PbagebEf1YDu+PbahesitHRWiOOfOEhEREVGZp/E7TxARERGRejDYEREREckEgx0RERGRTDDYEREREckEg90bKigoAFC8+7YRERGVFL9f6E0w2L2BmJgYeHt7Iysrq8zeyqy84AeXerE938zz7cZ2fDNsR/Uo7DgopFQqNVQJlUdl4jp25cm5c+fQpk0bjBkzBgYGBtJ0IQRDXgnk5+ejUqVKUCqV0NbWhlKphJYW/88oqfT0dDx58gS6urqoUqUKFAoF34sldPnyZWzatAm3bt1Cu3bt0K5dO9SrV4/vyRJiO6pHfHw8li9fjnv37qF+/fro27cvmjVrpumyypWkpCTEx8dDW1sb1atXR82aNQFUnO9pXseuBGJjY9GmTRuMGjUKCxculKbn5uZCV1dXg5WVL/Hx8Vi0aBEePXoECwsLTJgwAXXr1tV0WeXO+fPnMXLkSNy/fx9Vq1ZFo0aN8N1336FSJf6/VlwXL15EmzZt0LlzZyQlJaGgoAB3795FREQEOnXqVGG+CN4W21E9Ll26hFatWqFXr17IyMhAeno6jhw5gjVr1uDTTz/VdHnlwvnz59GtWzdYWlriypUraNiwIfr27YupU6cCqCDhTlCxJCUlCWtra+Hp6SmEECI/P1+MGzdOfPDBB6JevXriv//9r4iPj9dwlWXfpUuXhLGxsfD19RUDBgwQHTt2FJUrVxZr164VmZmZmi6v3EhMTBSWlpZi4sSJ4pdffhELFy4UTk5OwsXFRSQkJGi6vHIhPz9ffPLJJ2LQoEHStLNnz4phw4YJbW1tsXPnTiGEEAUFBZoqsVxgO6rPqFGjhLe3t/T8wYMHYubMmUJbW1usWrVKCCGEUqnUVHllXkpKiqhbt64YN26cSElJEcePHxf+/v5CX19fjB07VlpO7m3If+1LwN3dHbdv38Zvv/2G1atXIy8vD02aNIGjoyOWLVuGuLg4zJ49G9WrV9d0qWXW8uXL0aFDB0RERAAA8vLyEBgYiBEjRiAzMxMjR46Ejo6OZossB6Kjo1GjRg3Mnj0bJiYmAIA+ffpg4MCB+OijjxAZGQkrKysOg72CUqnE7du34e7uLk1r0qQJgoKCoKuri759++LQoUNo3bq1Bqss+9iO6lPY+17IysoK33zzDQwMDDB69GjUqFEDXl5eFaPX6Q2kpaWhUqVKGDp0KMzNzdGqVSs0aNAAtWrVwueff47KlStj/vz58m87TSfL8uTevXti8ODBQl9fX3Tp0kX8888/0rxNmzYJMzMzsXv3bg1WWPYNGjRIDBkyRAih+h/8nDlzhI6Ojti1a1eReVTUihUrhIWFhfS8sL3u3bsnGjduLNq2baup0sqV0aNHC3d3d5Gamqoy/datW6JPnz7Cy8tLpKWlaai68oPtqB4BAQHCwcFB3L17Vwjx/z1Lubm5YuTIkaJ+/foiKSlJkyWWaTdv3hT6+vpi06ZNKtOzs7NFaGiosLCwED/88IOGqnt3+K98CdjY2CAoKAjjxo3DtGnTULVqVemsr4EDB8LCwgKHDh3ScJVlW40aNbB3716kpaVBS0sLeXl5AIAZM2bgs88+w8iRI5GSksJeppcofL99+OGH0NPTw/z58wEAWlpaUCqVsLGxwapVq/DgwQNs3bpVk6WWC++//z6ePHmC8PBwPH78WJru4OCADz/8EDExMUhLS9NgheUD21E9PD094eDggKCgICQnJ0OhUECpVEJHRwd9+/ZFWloa7t+/r+kyyyxLS0v07dsXP/74I+Li4qTpenp68Pb2Rrt27XD8+HENVvhu8NuzhGxtbTFt2jS0a9cOAKSzEFNSUmBpaYkmTZpotsAybujQoahRowZGjRqF9PR06OjoSOFu+PDhEELgypUrGq6y7MnJyQHw9GxiADAzM0O/fv2we/du/PDDDwAgheFGjRpBS0sL165d00yxZVRiYiLWrFmDtWvXYt++fQAAHx8ftGvXDqGhodi4cSNSU1Ol5Vu0aAEDAwOVoEJsR3W5du0aFixYgDlz5mDDhg0AgNatW6NPnz7466+/sGjRIty9e1f6u65Xrx4MDQ2RmZmpybLLlNTUVNy4cQPXr18HAOjr66Nv3764dOkSwsLCcPXqVWlZKysr1KhRAydPnpQ+R+WKx9i9gcJjmgopFAosW7YM//zzD9q2bauhqsqeq1ev4ueff0ZaWhpcXV3h7e2NOnXqYPjw4QgNDcXEiRPx7bffwszMDABgbW0NPT092f/RldSFCxcwa9YsPH78GNra2pg+fTrat2+P8ePHY9SoUQgNDUV2djaGDh0K4On7s1atWtDT0wNQQc4Ce43z58+jQ4cOcHJywsOHD/HgwQP07dsXy5Ytw/LlyzF8+HCsWrUKV65cgZ+fH0xNTbFu3TpoaWmhWrVqmi6/zGA7qkdcXBzatWuHJk2aICsrC7Gxsdi8eTMWLlyICRMm4MmTJ/jtt99w6dIlfPPNNzA0NMTatWuRm5uL2rVra7r8MiE2NhaDBw/Go0ePUKlSJdSqVQvff/89PvroI6SkpMDf3x+5ubnw9fVFq1atAABZWVmoXbu2/K+vqMlxYDn44YcfxOeffy6qVKkizpw5o+lyyoy4uDhhZmYm2rdvL95//31RqVIl0atXL3HkyBEhhBBLly4VLVu2FO+//764cOGCOH/+vJg5c6aoXr26dHwJCXHlyhVhYmIiPv/8czF58mTRt29foVAoxMyZM0VmZqa4ceOG8PHxES4uLuKTTz4RGzZsECNHjhQmJibiypUrmi6/THj8+LFwd3cXX331lRDi6Rnue/bsEebm5qJTp07iwYMHQgghAgMDxXvvvScUCoVo1qyZsLa25t/0M9iO6pGVlSU8PT3FqFGjhBBCPHnyRFy8eFHUqVNHtGnTRhw/flwIIcT69etF9+7dhUKhEI0aNRI1atRgO/7P7du3ha2trZg2bZqIjIwUP/30k2jWrJmwt7cXBw8eFEIIsXHjRuHu7i6cnJxE9+7dhbe3tzAxMRHnzp3TcPWlj8HuLZ07d0588MEHIi4uTtOllBlZWVmiR48eYvTo0dK06Oho0bx5c9GhQwexb98+IYQQO3bsEJ07dxa6urqiXr16olatWiI6OlpTZZdJM2fOFF27dlWZtmzZMmFubi4mTZokcnNzxb1790RYWJho2rSpaNGihejQoYOIiYnRUMVlz5MnT0TTpk3Fli1bVKZfvnxZWFhYiB49ekjTHjx4IPbs2SP++usvcfv27XddapnGdlSftm3bioULFwohhMjLyxNCCHH37l3h6uoq2rZtK5KTk4UQTy8lc+LECXHhwgWeNPGMP//8UzRo0EDcu3dPmpafny+6d+8urKyspHB88uRJERERIQYOHChmzJghLly4oKmS3ykGOzXIycnRdAllTps2bYS/v78Q4v/P2IyPjxceHh6iS5cuKtf8O3HihIiPj+cH1wtMnDhRCnaFXwBCCLF69WphYGAgVq5cqbL8kydPxJMnT95pjWVdRkaGsLOzE4GBgdK03NxcIcTTf8wMDQ1FQECApsorN9iOb0+pVIonT56I5s2bi5EjR0rTC79DkpKShLm5ufjyyy81VWK58OOPPwozMzORnZ0thFD9Du7UqZOoW7eu7K9V9yoMdqQ2hQEuPT1ddOjQQfpwys/Pl0LJhQsXhL29vTScQ68WHBwsjI2NpeHpZz/AAgMDhaGhobh586amyis3Fi9eLOzt7cWOHTukaYWhZM6cOaJVq1YiJSWFl9l5Dbajevz0009CT09PrF+/XppW+A/Z+vXrhaOjo0hMTKzQ4eRFCtvj8ePHwt7eXmVUqPCz8e7du6JWrVpiwYIFKutUJDwrltQiJiYGPXv2RGZmJoyNjTFq1CisXr0a27Ztg7a2tnRpkwYNGmDhwoXYuHEjbt26Jf+DWN/SyJEj4ebmhj59+iAlJQW6urrIzs4GAHz++ecwNzdHdHS0hqssW5KSknDy5Ens27dPupl679694e7ujoULF2L//v0AIF0I28LCAunp6ahcuTIvs/MMtqN63L59G/v375fOFs7NzcVHH32E4cOHw9/fXzqrvXLlygAAIyMj6OrqwsjIqMKf9FToyZMnUCqVyM3NBQAYGhpiypQpOHr0KL799lsAgK6uLpRKJapWrQp7e3s8ePAAACpkG/Kvj97auXPn0KZNGzRs2BCGhoYAAG9vb4wePRoDBw7Ejh07oKWlJX0BmJmZwdraGoaGhhXyj+5lrly5gqlTp2Lo0KEIDg5GQkICdHV14e/vD6VSif79+yM1NVX6AtDT04OhoSHv1PGM2NhYuLu749NPP0X//v3RsGFDbNmyBXZ2dpgyZQpMTU0xc+ZMbNmyBcDTO59cv34dVlZWUnghtqO6xMbGomXLlpg0aRJGjx6NJk2aYNGiRXj8+DH+85//oGPHjhg/fjyWL1+O7OxsZGZm4vTp0zAyMmI4/p+4uDh8+OGHaNu2LZo1a4YNGzYgLS0NgwcPxvvvv48tW7bg66+/BvD0kk96enowNzeXPhcrZOeBprsMqXwrPLZm8uTJKtPz8/PFP//8I0aPHi10dHRESEiISEpKEk+ePBHTpk0TjRs3LnKV+orswoULwtTUVHTr1k306dNHmJqaio4dO0pDNTt27BAtW7YUNWvWFPv27RN//vmnmDlzprC2tuZQ7P8kJyeLevXqif/85z/i2rVr4u7du6J///7C2dlZBAYGiuzsbBETEyNGjhwpKlWqJBo3bixat24tqlSpIs6ePavp8ssMtqN6pKamiqZNm4opU6aIBw8eiIKCAjFx4kTRokUL4evrK5KTk8XDhw/F119/LXR1dUWdOnVE48aNhaWlJc9+/Z9r166JKlWqiNGjR4vly5eLr776SpiZmYnhw4eLq1evitTUVDF16lRRq1Yt0blzZzF//nzx2WefCSMjowp973YGO3pjSUlJwtraWnh6egohnoa5cePGie7du4sGDRqI5cuXi0OHDolly5YJXV1dUbNmTeHq6soPrufk5OSITz75RIwYMUKalpCQIPr37y9atGghQkNDhRBCXLx4UQwYMEBYWloKZ2dn0bBhQ55F/IwLFy4IR0dHcfr0aZXpU6dOFQ0bNhSLFi0SSqVSZGRkiGPHjolvvvlGrF69WiQkJGio4rKJ7ageN2/eFDVq1BB//PGHyvTly5eLVq1aiVGjRolHjx4JIZ6eWLZ27VqxZcsWcePGDQ1UWzYtWrRIvP/++yrTNm3aJFxcXMSgQYPEzZs3RWZmpvjjjz9E165dRceOHcVHH31UIS5p8iq8QDG9FXd3d9y+fRu//fYbVq9ejby8PDRp0gQ1a9bE0qVL0aFDByxduhTt27fHpUuXIIRA69atUaNGDU2XXmbo6uriwYMHqFmzJoCnQwd16tTBwoUL4e/vj/Xr18PBwQHdu3fH5s2bcenSJZiYmEBXVxcWFhYarr7syMvLQ35+PrKysgA8PS5HX18f8+fPx5MnT7B8+XJ06dIFrq6uaN26NW9K/xI5OTlsRzXQ0tKCgYEB7t27B+DpXWMqVaoEPz8/ZGdnIywsDJGRkejZsyfq1auHevXqabjisik9PR0ZGRkwMDCAlpYWBg4cCF1dXUyfPh2rV6/GvHnz0KlTJ3Tq1AnA08+BCn94iqaTJZVv9+7dE4MHDxb6+vqiS5cu4p9//pHmbdy4UZiamqqcQUeq8vPzRW5urhg6dKjo27evyM7OFkqlUjqr8Nq1a8Ld3V34+PhI61TEs7yKq/A6foUKL4cghBDNmzcXH3/8sSbKKvPu3bunco2vwmtOFmI7vpkePXoINzc3qWfu2UsWde/eXXh4eGiqtHJh69atQl9fXxrhefaqACEhIUJXV7fINWT5+cizYukt2djYICgoCOPGjcO0adNQtWpV6WDVQYMGwdLSEocPH9ZwlWVP4QHm2tra0NHRga+vL7Zv347Q0FAoFApoaWmhoKAAtWrVQlBQEH7++WdcuHABQMU8y+tFMjMz8fjxY6Snp0vTQkNDceHCBQwcOBAAVG5R9/777/M+my9w9+5duLi4YObMmdIN0tesWYPz58+zHUvgzp07+PHHH7Ft2zacPXsWABAeHo5Hjx6hX79+yM3NRaVK/z9I5unpiYKCAp5s8oyrV69i+/bt0r2xfXx80K1bN/Ts2RPJycnQ1dWV5o0cORLVq1fHn3/+qbINfj7yrFhSA1tbW0ybNg3t2rUD8PQPSwiBlJQUWFpaws3NTcMVli1XrlzB0qVLkZSUJE1r3749FixYgPHjxyMsLAzA09AHAMbGxqhbt650xjEBFy9eRO/evdG+fXvUr18fmzZtAgDUr18fwcHBOHDgAPr164e8vDzp7MLk5GQYGhoiPz+/Yp4p9xIJCQlIS0tDWloaQkJCcPbsWTRp0gQrVqzA3r170atXL7bja5w/fx7t2rXDt99+i1GjRsHf3x9XrlyBhYUFNm/ejPj4eHTt2hUJCQnS5YrOnz8PY2NjBrv/iY2NRZs2bbBnzx6kpKRI761vvvkG9vb2aN26Ne7cuSPdAzsrKwvGxsaoUqWKJssumzTaX0iyNnv2bOHk5CQSExM1XUqZkZCQIMzNzYVCoRDTp08XDx8+lOZlZmaKwMBA6V6wZ86cESkpKWLatGmiTp060m2GKroLFy6IqlWrivHjx4tNmzaJCRMmCB0dHWm4JjMzU/z+++/C3t5e1KtXT3h7ewsfHx9haGgozp8/r+Hqy56UlBTx0UcfidDQUNG0aVMxcOBA6T7Dv/76q2jQoIGoW7cu2/ElEhMThZ2dnZg2bZrIyMgQu3fvFtbW1uLEiRPSMnFxcaJBgwbCyclJtGzZUvTs2VMYGRlV+IP8C928eVNUr169yNUVCsXGxor33ntPmJmZidDQUPHDDz+IadOmiapVq4pr166942rLPoUQ/JeL1GvLli04dOgQfvrpJxw8eJA9dv+TmZmJMWPGQKlUokWLFvDz88OkSZMwefJkWFpaAgCUSiU2btyIqVOnQltbG8bGxkhPT8eOHTvQtGlTDe+B5qWmpmLAgAGoV68egoODpekdOnSAi4sLli1bJk17/Pgx5syZI13778svv0SDBg00UXaZVVBQgNTUVLRr1w5//vknTp48iaCgILi6uuLq1auoVq0awsLC8PXXX+PRo0dsxxf47rvv8MMPP+DPP/+UhgE/+OAD9OzZE3p6eqhRowY8PDwAAMuXL8e9e/egp6eHAQMGoG7duhqsvOzYuXMnQkJCsGvXLuTl5SEwMBAXLlyAubk5OnfujAEDBiAnJwdTpkzBgQMHoFQqYWZmhpCQEH6/vADPiiW1a9CgATZu3IijR4+iYcOGmi6nzNDS0kKzZs1QtWpV9O/fHxYWFvj4448BQAp3Wlpa0oU3b926haysLLi4uMDOzk7D1ZcNeXl5ePToEfr27QvgaRDW0tJCzZo1kZqaCuDpWcVCCBgbG2PBggUqy5EqLS0tWFpaokWLFoiLi0OvXr2gp6cHX19fZGdnY+nSpTA2Npau7s92LEoIgVu3biEmJgZubm6YO3cu9uzZg9zcXDx69Ai3bt3CnDlzMGLECHz11VeaLrdMOnPmjPT36+Xlhfz8fDRu3BgXL17EggULEBcXh7lz5yI4OBh3796VDksxMzPTYNVlF4MdqZ2rqyu2bdsGXV1dTZdSpujr68PX11f6UPLx8YEQAgMGDIAQAlOnToWFhQXy8/OhpaWF999/X8MVlz3VqlXDxo0b4eTkBOBpj5OWlhbs7Oxw8+ZNAE+P8VQoFEhPT4eJiYk0jYoqbBdtbW1ERkbC09MT27ZtQ0FBAapXr46///4bDRs2lC5pwnYsqmvXrli/fj18fHzQuHFjbNu2Ddu3b8dHH32Ehw8fYu7cudi8eTN69eoFc3NzaGlpQQjBtnxGmzZtcPjwYaxduxYKhQIbN26EnZ0d0tLSEBwcjD179uD8+fNwcXGBjY0N/7l4DQY7KhUMdS9WGOoKA0n//v0hhMDAgQOhUCgwbtw4LFq0CDdv3sT69ethYGDAL4DnFIY6pVKpctug5ORkaZmgoCDo6elhzJgxqFSpEtvwJQoDRseOHXHjxg2MGjUKu3fvRnR0NGJiYjB58mTo6urCzc0Nenp6bMcXqFmzJjZu3IhTp07h4sWLUCgU6NmzJwDAysoKtra2OHz4sMptwip6OxYUFEgnhwGAvb09Ll26hCVLlsDW1lYaoTA1NcXQoUOlXjsXFxeGumJgsCPSAG1tbQghoFQq8fHHH0OhUODTTz/F77//jmvXruHUqVM8C/Y1nu/5KPzAnz17NubMmYOzZ8+qXF6Ciipsu5o1a2Lo0KGoVq0adu7ciZo1a6JmzZpQKBRo3LixdCYivVhhe4WFheH06dPIzc2V/rl98OABHB0defbr/1y5cgU7duzAwIEDYWNjAwCoV68evvvuO3h7e+Off/7BsWPH4O7uDuBpL33r1q1hbm6uybLLFZ48QaRBhX9+CoUCnTp1QkxMDCIjI+Hi4qLhysqHwmO+AgICkJSUBCcnJ8ycORN///03TzYpgby8PGzYsAHNmzeHq6srhwrf0MWLF9GmTRvMmDED1tbWiIuLw3fffYcjR47wbxpPr1PXqlUr/Pvvv5g2bRomTJigcvecLVu2YNCgQejSpQuGDBmC5s2bY+3atVi/fj2OHz8OBwcHDVZffvDfWSINUigUKCgowOTJk3Ho0CHExMTwC6AECnvpdHR0sGbNGpiYmOCvv/5iqCshHR0dDBkyhEOFb6lBgwbYvn07RowYIR37efjwYf5N4+lVAYKCgvDRRx9JVwXIz8/HlClTpHD38ccfw9LSErNmzcK4ceNQpUoVKJVK7Ny5k6GuBNhjR6RhBQUFiIiIQLNmzdCkSRNNl1MunT59Gi1btkRcXBwvxUEal5qairy8POjp6fHMzf958uQJwsPDpasC/Pjjj/j4448xadIklXAHACkpKUhNTUVmZibs7e15T+wSYrAjKgM49PX2MjMzeVwiURn2/N/o1q1bMWDAAEycOFG6JWV+fj7u3LkDR0dHzRVaznEolqgMYKh7ewx1RGUbrwrwbrDHjoiIiN6pwguJa2lpYevWrfj0009Rq1Yt6aoAPCzlzTHYERER0TvHqwKUDg7FEhER0TvHqwKUDl7CmYiIiDSmYcOGOHPmDFxdXTVdiixwKJaIiIg0hlcFUC/22BEREZHGMNSpF4MdERERkUww2BERERHJBIMdERERkUww2BERERHJBIMdERERkUww2BERERHJBIMdERERkUww2BERERHJBIMdEVEpys3N1XQJRFSBMNgRUYWRmZmJwYMHw8jICDY2Nli8eDE8PDwwbtw4AE+vgP/rr7+qrGNmZoaIiAjp+e3bt+Hj4wMzMzOYm5ujZ8+eSExMlOYPGTIE3t7emDt3LmxtbVG3bl18/fXXaNSoUZF6mjRpglmzZpXCnhJRRcVgR0QVxuTJk3H48GH89ttv2L9/PyIjI3HmzJlir5+XlwdPT08YGxvj6NGjiIqKgpGREbp166bSM3fw4EFcvnwZBw4cwM6dO/HZZ58hPj4ep06dkpY5e/YsYmNjMXToULXuIxFVbJU0XQAR0buQkZGBtWvXYuPGjejUqRMAYN26dbC3ty/2NrZu3QqlUomwsDDp/pbh4eEwMzNDZGQkunbtCgAwNDREWFgYdHV1pXU9PT0RHh6OFi1aSOu1b98etWrVUtcuEhGxx46IKoZr164hNzcXrVq1kqaZm5ujbt26xd7GuXPncPXqVRgbG8PIyAhGRkYwNzdHdnY2rl27Ji3n4uKiEuoAYMSIEfjhhx+QnZ2N3NxcbN68GZ999tnb7xgR0TPYY0dE9D8KhQJCCJVpeXl50s8ZGRlo1qwZNm3aVGRdS0tL6WdDQ8Mi8z/88EPo6elh+/bt0NXVRV5eHvr27avG6omIGOyIqIKoXbs2dHR0cOLECVSvXh0A8O+//+LKlSto3749gKfhLCkpSVonISEBWVlZ0vOmTZti69atsLKygomJSYlev1KlSvD19UV4eDh0dXXx8ccfQ19fXw17RkT0/zgUS0QVgpGREYYNG4bJkyfjzz//RFxcHIYMGQItrf//GOzYsSNWrFiBs2fP4vTp0xg5ciR0dHSk+YMGDYKFhQV69uyJo0eP4saNG4iMjMSYMWNw586d19YwfPhw/Pnnn9i7dy+HYYmoVLDHjogqjG+//RYZGRn48MMPYWxsjIkTJyItLU2av3jxYgwdOhTvvfcebG1tERwcjOjoaGm+gYEBjhw5gqlTp6J37954/Pgx7Ozs0KlTp2L14Dk5OaFNmzZITU1VOdaPiEhdFOL5A0qIiCoQDw8PNGnSBEuXLi311xJCwMnJCaNGjcKECRNK/fWIqOJhjx0R0Tvw8OFDbNmyBffv3+e164io1DDYERG9A1ZWVrCwsMB3332HKlWqaLocIpIpDsUSERERyQTPiiUiIiKSCQY7IiIiIplgsCMiIiKSCQY7IiIiIplgsCMiIiKSCQY7IiIiIplgsCMiIiKSCQY7IiIiIplgsCMiIiKSif8DG6kQXkBjSlwAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "benchmarks.pivot(index='query', columns='executor', values='cpu_time').plot(kind='bar')\n", + "plt.ylabel('CPU time (ns)')\n", + "plt.xticks(rotation=45, ha='right')\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "1d432ac6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
executorCachedJitCompiledExpressionExecutorInterpretedExpressionExecutorspeedup
query
56.751964e+047.039228e+040.040809
5005.299002e+057.329383e+050.277019
10005.304898e+057.302785e+050.273579
20009.116984e+051.139146e+060.199665
40001.672435e+061.942391e+060.138981
80003.191009e+063.542127e+060.099126
160006.217767e+066.730967e+060.076245
\n", + "
" + ], + "text/plain": [ + "executor CachedJitCompiledExpressionExecutor InterpretedExpressionExecutor \\\n", + "query \n", + "5 6.751964e+04 7.039228e+04 \n", + "500 5.299002e+05 7.329383e+05 \n", + "1000 5.304898e+05 7.302785e+05 \n", + "2000 9.116984e+05 1.139146e+06 \n", + "4000 1.672435e+06 1.942391e+06 \n", + "8000 3.191009e+06 3.542127e+06 \n", + "16000 6.217767e+06 6.730967e+06 \n", + "\n", + "executor speedup \n", + "query \n", + "5 0.040809 \n", + "500 0.277019 \n", + "1000 0.273579 \n", + "2000 0.199665 \n", + "4000 0.138981 \n", + "8000 0.099126 \n", + "16000 0.076245 " + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pivoted = benchmarks.pivot(index='query', columns='executor', values='cpu_time')\n", + "pivoted['speedup'] = 1.0 - pivoted['CachedJitCompiledExpressionExecutor'] / pivoted['InterpretedExpressionExecutor']\n", + "pivoted" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "f5af9c1a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pivoted['speedup'].plot(kind='bar')\n", + "plt.ylabel('speedup (%)')\n", + "plt.xlabel('query')\n", + "plt.xticks(rotation=0, ha='center')\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/benchmarks/flamegraph-jit.svg b/benchmarks/flamegraph-jit.svg new file mode 100644 index 0000000..0d15927 --- /dev/null +++ b/benchmarks/flamegraph-jit.svg @@ -0,0 +1,1642 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete (3,439,095,085 samples, 90.52%) +boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete + + +antlr4::atn::LexerATNSimulator::computeTargetState (9,372,331 samples, 0.25%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +benchmark::RunSpecifiedBenchmarks (3,601,603,158 samples, 94.80%) +benchmark::RunSpecifiedBenchmarks + + +std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> > (40,849,199 samples, 1.08%) + + + +operator new (162,107,227 samples, 4.27%) +opera.. + + +_int_free (40,824,204 samples, 1.07%) + + + +std::pair<std::__detail::_Node_iterator<std::shared_ptr<antlr4::atn::PredictionContext const>, true, true>, bool> std::_Hashtable<std::shared_ptr<antlr4::atn::PredictionContext const>, std::shared_ptr<antlr4::atn::PredictionContext const>, std::allocator<std::shared_ptr<antlr4::atn::PredictionContext const> >, std::__detail::_Identity, antlr4::atn::PredictionContextCache::PredictionContextComparer, antlr4::atn::PredictionContextCache::PredictionContextHasher, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::_M_insert_unique<std::shared_ptr<antlr4::atn::PredictionContext const> const&, std::shared_ptr<antlr4::atn::PredictionContext const> const&, std::__detail::_AllocNode<std::allocator<std::__detail::_Hash_node<std::shared_ptr<antlr4::atn::PredictionContext const>, true> > > > (25,034,228 samples, 0.66%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (40,315,717 samples, 1.06%) + + + +antlr4::atn::ParserATNSimulator::closure (15,496,168 samples, 0.41%) + + + +boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (41,357,713 samples, 1.09%) + + + +stewkk::sql::CsvDirSequentialScanner::operator (163,475,163 samples, 4.30%) +stewk.. + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) + + + +pthread_mutex_lock@@GLIBC_2.2.5 (41,061,571 samples, 1.08%) + + + +_int_free (118,555,905 samples, 3.12%) +_in.. + + +boost::asio::detail::scheduler::work_finished (41,302,658 samples, 1.09%) + + + +antlr4::atn::ParserATNSimulator::computeStartState (34,013,412 samples, 0.90%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code> > (81,364,301 samples, 2.14%) +v.. + + +stewkk::sql::codegen::PostgreSQLParser::columnref (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > (161,995,300 samples, 4.26%) +void .. + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +[unknown] (106,618,516 samples, 2.81%) +[u.. + + +_int_malloc (39,701,173 samples, 1.04%) + + + +stewkk::sql::ReceiveTuples (40,555,284 samples, 1.07%) + + + +boost::asio::detail::scheduler::work_finished (41,261,829 samples, 1.09%) + + + +antlr4::atn::ParserATNSimulator::canDropLoopEntryEdgeInLeftRecursiveRule (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_qual (84,967,385 samples, 2.24%) +s.. + + +_ZZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateEENKUlvE_clEv.resume (109,835,884 samples, 2.89%) +_Z.. + + +boost::asio::detail::scheduler::work_finished (40,370,745 samples, 1.06%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > > (936,893,606 samples, 24.66%) +void boost::asio::execution::detail::an.. + + +boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (80,794,403 samples, 2.13%) +v.. + + +cfree@GLIBC_2.2.5 (201,211,283 samples, 5.30%) +cfree@.. + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (40,315,717 samples, 1.06%) + + + +stewkk::sql::codegen::PostgreSQLParser::target_el (84,967,385 samples, 2.24%) +s.. + + +antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) + + + +void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) + + + +stewkk::sql::codegen::PostgreSQLParser::stmtblock (100,463,553 samples, 2.64%) +st.. + + +boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (39,685,691 samples, 1.04%) + + + +all (3,799,319,765 samples, 100%) + + + +malloc (25,034,228 samples, 0.66%) + + + +stewkk::sql::ReceiveTuples (40,350,342 samples, 1.06%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (82,382,664 samples, 2.17%) +v.. + + +[unknown] (59,562,419 samples, 1.57%) + + + +malloc (40,555,284 samples, 1.07%) + + + +__libc_start_call_main (3,601,603,158 samples, 94.80%) +__libc_start_call_main + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (245,006,271 samples, 6.45%) +void boo.. + + +antlr4::atn::LexerATNSimulator::getReachableConfigSet (9,372,331 samples, 0.25%) + + + +std::enable_if<call_traits<boost_asio_require_fn::impl, boost::asio::any_io_executor const&, void (41,024,951 samples, 1.08%) + + + +stewkk::sql::codegen::PostgreSQLParser::stmtmulti (100,463,553 samples, 2.64%) +st.. + + +stewkk::sql::codegen::PostgreSQLParser::c_expr (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteJoin (522,843,079 samples, 13.76%) +stewkk::sql::Executo.. + + +stewkk::sql::ConcatTuples (121,354,626 samples, 3.19%) +ste.. + + +boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) + + + +antlr4::atn::ATNSimulator::getCachedContext (25,034,228 samples, 0.66%) + + + +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::asio::detail::executor_function> (2,228,166,127 samples, 58.65%) +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::as.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_at_time_zone (25,034,228 samples, 0.66%) + + + +boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (527,102,043 samples, 13.87%) +boost::asio::detail::.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_in (50,953,973 samples, 1.34%) + + + +malloc_consolidate (15,496,168 samples, 0.41%) + + + +__libc_start_main@@GLIBC_2.34 (3,601,603,158 samples, 94.80%) +__libc_start_main@@GLIBC_2.34 + + +_int_free (120,389,579 samples, 3.17%) +_in.. + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (39,685,691 samples, 1.04%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,187,300,681 samples, 57.57%) +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asi.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_isnull (25,034,228 samples, 0.66%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::Execute (123,171,394 samples, 3.24%) +ste.. + + +boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (81,364,301 samples, 2.14%) +b.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >* std::__do_uninit_copy<std::ranges::transform_view<std::ranges::ref_view<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteProjection (242,357,273 samples, 6.38%) +std::vec.. + + +SetImpliedBits (7,929,152 samples, 0.21%) + + + +cfree@GLIBC_2.2.5 (40,824,204 samples, 1.07%) + + + +stewkk::sql::codegen::PostgreSQLParser::select_clause (84,967,385 samples, 2.24%) +s.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::atn::LexerATNSimulator::matchATN (9,372,331 samples, 0.25%) + + + +_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) + + + +_int_free_maybe_consolidate.part.0 (40,780,288 samples, 1.07%) + + + +void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) + + + +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) + + + +void boost::asio::execution::detail::any_executor_base::copy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (40,041,233 samples, 1.05%) + + + +pthread_mutex_unlock@plt (40,513,751 samples, 1.07%) + + + +antlr4::BufferedTokenStream::setup (9,372,331 samples, 0.25%) + + + +std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> > (40,030,747 samples, 1.05%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (41,067,820 samples, 1.08%) + + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteProjection (484,047,544 samples, 12.74%) +stewkk::sql::Execut.. + + +antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (205,111,508 samples, 5.40%) +void b.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_compare (25,034,228 samples, 0.66%) + + + +boost::asio::detail::scheduler::work_cleanup::~work_cleanup (41,261,829 samples, 1.09%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) + + + +void boost::asio::execution::detail::any_executor_base::move_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (41,323,068 samples, 1.09%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (109,835,884 samples, 2.89%) +vo.. + + +llvm::po_iterator<llvm::Function*, llvm::SmallPtrSet<llvm::BasicBlock*, 8u>, false, llvm::GraphTraits<llvm::Function*> >::traverseChild (8,946,159 samples, 0.24%) + + + +benchmarks (3,799,319,765 samples, 100.00%) +benchmarks + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +malloc (80,416,204 samples, 2.12%) +m.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_or (84,967,385 samples, 2.24%) +s.. + + +benchmark::internal::BenchmarkInstance::Run (3,601,603,158 samples, 94.80%) +benchmark::internal::BenchmarkInstance::Run + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (39,894,763 samples, 1.05%) + + + +stewkk::sql::codegen::PostgreSQLParser::stmt (100,463,553 samples, 2.64%) +st.. + + +void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +uw_update_context_1 (33,889,616 samples, 0.89%) + + + +malloc (15,496,168 samples, 0.41%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::simple_select_pramary (84,967,385 samples, 2.24%) +s.. + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (774,233,850 samples, 20.38%) +boost::asio::detail::awaitable_.. + + +boost::asio::detail::scheduler::do_run_one (3,480,356,914 samples, 91.60%) +boost::asio::detail::scheduler::do_run_one + + +antlr4::atn::LexerATNSimulator::execATN (9,372,331 samples, 0.25%) + + + +malloc@plt (25,919,745 samples, 0.68%) + + + +boost::asio::awaitable<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > >, boost::asio::any_io_executor>::await_resume (40,478,988 samples, 1.07%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_add (25,034,228 samples, 0.66%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_not (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +cfree@GLIBC_2.2.5 (81,209,468 samples, 2.14%) +c.. + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,351,062,937 samples, 61.88%) +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asi.. + + +_ZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateE (3,601,603,158 samples, 94.80%) +_ZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateE + + +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (80,926,524 samples, 2.13%) +b.. + + +main (3,601,603,158 samples, 94.80%) +main + + +__GI_____strtoll_l_internal (81,010,836 samples, 2.13%) +_.. + + +boost::asio::detail::scheduler::run (3,601,603,158 samples, 94.80%) +boost::asio::detail::scheduler::run + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void> > (121,412,151 samples, 3.20%) +voi.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_like (25,034,228 samples, 0.66%) + + + +[unknown] (59,562,419 samples, 1.57%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +execute_cfa_program_specialized (40,605,147 samples, 1.07%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +benchmark::internal::(anonymous namespace)::RunInThread (3,601,603,158 samples, 94.80%) +benchmark::internal::(anonymous namespace)::RunInThread + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void>, std::allocator<void> > (978,196,264 samples, 25.75%) +void boost::asio::detail::executor_funct.. + + +benchmark::internal::BenchmarkRunner::DoOneRepetition (3,601,603,158 samples, 94.80%) +benchmark::internal::BenchmarkRunner::DoOneRepetition + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_between (50,953,973 samples, 1.34%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +operator new (80,416,204 samples, 2.12%) +o.. + + +std::_Function_handler<stewkk::sql::Value (40,383,450 samples, 1.06%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (327,144,129 samples, 8.61%) +boost::asio:.. + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (41,027,112 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closure (34,013,412 samples, 0.90%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_send_op<boost::asio::experimental::channel_traits<>, void (364,798,231 samples, 9.60%) +void boost::as.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +pthread_mutex_lock@@GLIBC_2.2.5 (41,364,543 samples, 1.09%) + + + +stewkk::sql::GetAST (109,835,884 samples, 2.89%) +st.. + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) + + + +_start (3,601,603,158 samples, 94.80%) +_start + + +operator new (121,717,805 samples, 3.20%) +ope.. + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +_int_malloc (39,726,451 samples, 1.05%) + + + +stewkk::sql::codegen::PostgreSQLParser::select_no_parens (84,967,385 samples, 2.24%) +s.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::LexerATNSimulator::match (9,372,331 samples, 0.25%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_caret (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) + + + +antlr4::atn::PredictionContextCache::put (25,034,228 samples, 0.66%) + + + +antlr4::atn::ATNConfigSet::optimizeConfigs (25,034,228 samples, 0.66%) + + + +void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) + + + +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >::operator (2,187,300,681 samples, 57.57%) +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::erro.. + + +boost::asio::detail::executor_function::impl<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,824,204 samples, 1.07%) + + + +cfree@GLIBC_2.2.5 (40,780,288 samples, 1.07%) + + + +antlr4::BufferedTokenStream::sync (9,372,331 samples, 0.25%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (41,070,038 samples, 1.08%) + + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteJoin (40,555,284 samples, 1.07%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_collate (25,034,228 samples, 0.66%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (486,786,326 samples, 12.81%) +void boost::asio::e.. + + +antlr4::atn::ParserATNSimulator::adaptivePredict (15,496,168 samples, 0.41%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,228,166,127 samples, 58.65%) +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<bo.. + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (161,995,300 samples, 4.26%) +void .. + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (41,054,766 samples, 1.08%) + + + +antlr4::BufferedTokenStream::fill (9,372,331 samples, 0.25%) + + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (40,583,149 samples, 1.07%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_and (50,953,973 samples, 1.34%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr (84,967,385 samples, 2.24%) +s.. + + +antlr4::atn::ATNConfigSet::add (15,496,168 samples, 0.41%) + + + +stewkk::sql::CsvDirSequentialScanner::operator (40,958,645 samples, 1.08%) + + + +_ZZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateEENKUlvE_clEv.resume (40,780,288 samples, 1.07%) + + + +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) + + + +boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +_ZNSt6ranges14__copy_or_moveILb0ETkSt14input_iteratorPKN6stewkk3sql5ValueETkSt12sentinel_forIT0_ES5_TkSt20weakly_incrementableSt20back_insert_iteratorISt6vectorIS3_SaIS3_EEEQquT_18indirectly_movableIS7_T2_E19indirectly_copyableIS7_SD_EEENSt13__conditionalIXT_EE4typeINS_13in_out_resultIS7_SD_EESI_EES7_T1_SD_ (81,633,781 samples, 2.15%) +_.. + + +operator new (80,410,706 samples, 2.12%) +o.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +boost::asio::awaitable<void, boost::asio::any_io_executor> boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (41,150,539 samples, 1.08%) + + + +benchmark::internal::BenchmarkRunner::DoNIterations (3,601,603,158 samples, 94.80%) +benchmark::internal::BenchmarkRunner::DoNIterations + + +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (41,067,820 samples, 1.08%) + + + +stewkk::sql::codegen::PostgreSQLParser::target_list_ (84,967,385 samples, 2.24%) +s.. + + +operator new (15,496,168 samples, 0.41%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +operator new (25,034,228 samples, 0.66%) + + + +malloc@plt (40,921,300 samples, 1.08%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_qual_op (25,034,228 samples, 0.66%) + + + +boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (161,995,300 samples, 4.26%) +boost.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +_int_malloc (41,112,758 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) + + + +_int_malloc (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) + + + +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (82,382,664 samples, 2.17%) +b.. + + +stewkk::sql::codegen::PostgreSQLParser::selectstmt (84,967,385 samples, 2.24%) +s.. + + +stewkk::sql::codegen::PostgreSQLParser::identifier (25,034,228 samples, 0.66%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_mul (25,034,228 samples, 0.66%) + + + +operator delete (40,898,461 samples, 1.08%) + + + +cfree@GLIBC_2.2.5 (40,478,988 samples, 1.07%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_is_not (25,034,228 samples, 0.66%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (109,835,884 samples, 2.89%) +bo.. + + +stewkk::sql::codegen::PostgreSQLParser::colid (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,228,166,127 samples, 58.65%) +boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::det.. + + +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (40,370,745 samples, 1.06%) + + + +boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> > > (40,583,149 samples, 1.07%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (41,417,591 samples, 1.09%) + + + +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) + + + +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::handler_work (39,894,763 samples, 1.05%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (40,555,284 samples, 1.07%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (2,146,150,142 samples, 56.49%) +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump + + +stewkk::sql::codegen::PostgreSQLParser::target_list (84,967,385 samples, 2.24%) +s.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_lessless (84,967,385 samples, 2.24%) +s.. + + +stewkk::sql::ApplyProjection (80,555,549 samples, 2.12%) +s.. + + +malloc (121,717,805 samples, 3.20%) +mal.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +[unknown] (5,237,329 samples, 0.14%) + + + +stewkk::sql::codegen::PostgreSQLParser::simple_select_intersect (84,967,385 samples, 2.24%) +s.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_typecast (25,034,228 samples, 0.66%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) + + + +cfree@GLIBC_2.2.5 (118,555,905 samples, 3.12%) +cfr.. + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_qualop (25,034,228 samples, 0.66%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +malloc_consolidate (40,780,288 samples, 1.07%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (82,382,664 samples, 2.17%) +v.. + + +void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) + + + +unlink_chunk.isra.0 (40,780,288 samples, 1.07%) + + + +void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::adaptivePredict (25,034,228 samples, 0.66%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (39,685,691 samples, 1.04%) + + + +malloc (121,118,658 samples, 3.19%) +mal.. + + +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) + + + +__memset_evex_unaligned_erms (5,237,329 samples, 0.14%) + + + +_int_malloc (15,496,168 samples, 0.41%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) + + + +antlr4::atn::PredictionContext::getCachedContext (25,034,228 samples, 0.66%) + + + +void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::asio::detail::executor_function> (936,893,606 samples, 24.66%) +void boost::asio::io_context::basic_exe.. + + +antlr4::atn::ParserATNSimulator::addDFAState (25,034,228 samples, 0.66%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> >, std::allocator<void> > (936,893,606 samples, 24.66%) +void boost::asio::detail::executor_func.. + + +_int_free (40,556,729 samples, 1.07%) + + + +non-virtual thunk to antlr4::Lexer::nextToken (9,372,331 samples, 0.25%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +memchr@plt (41,302,733 samples, 1.09%) + + + +stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +pthread_once@GLIBC_2.2.5 (18,957,272 samples, 0.50%) + + + +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>::operator (855,700,610 samples, 22.52%) +void boost::asio::detail::awaitable.. + + +antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) + + + +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (162,126,040 samples, 4.27%) +boost.. + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (41,323,068 samples, 1.09%) + + + +malloc (80,410,706 samples, 2.12%) +m.. + + +boost::asio::awaitable<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > >, boost::asio::any_io_executor> boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (81,850,497 samples, 2.15%) +b.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_sign (25,034,228 samples, 0.66%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (40,315,717 samples, 1.06%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +stewkk::sql::codegen::PostgreSQLParser::root (100,463,553 samples, 2.64%) +st.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) + + + +antlr4::BufferedTokenStream::fetch (9,372,331 samples, 0.25%) + + + +uw_frame_state_for (40,605,147 samples, 1.07%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) + + + +stewkk::sql::ReceiveTuples (40,675,052 samples, 1.07%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) + + + +benchmark::RunSpecifiedBenchmarks (3,601,603,158 samples, 94.80%) +benchmark::RunSpecifiedBenchmarks + + +antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (40,667,781 samples, 1.07%) + + + +std::_Function_handler<stewkk::sql::Value (40,183,704 samples, 1.06%) + + + +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (41,302,658 samples, 1.09%) + + + +antlr4::Lexer::nextToken (9,372,331 samples, 0.25%) + + + +antlr4::atn::ParserATNSimulator::computeStartState (15,496,168 samples, 0.41%) + + + +antlr4::atn::ParserATNSimulator::adaptivePredict (34,013,412 samples, 0.90%) + + + + diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index a231158..67f4264 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -18,7 +18,7 @@ const static std::string kProjectDir = std::getenv("PWD"); static constexpr char kSimpleSelectSmall[]{"SELECT users.id FROM users;"}; static constexpr char kJoinSmall[]{"SELECT * FROM employees RIGHT JOIN departments ON employees.department_id = departments.id;"}; -static constexpr char kComplexSmall[]{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2/2*2 < 30;"}; +static constexpr char kComplex5[]{"SELECT departments.id*2, employees.id+1 FROM employees RIGHT JOIN departments ON employees.department_id = departments.id AND departments.id > 3 AND departments.id*2*2/2/2*2 < 30;"}; static constexpr char kComplex500[]{"SELECT departments_500.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_500 ON employees_200.department_id = departments_500.id AND departments_500.id > 3 AND departments_500.id*2*2/2/2*2 < 30;"}; static constexpr char kComplex1000[]{"SELECT departments_1000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_1000 ON employees_200.department_id = departments_1000.id AND departments_1000.id > 3 AND departments_1000.id*2*2/2/2*2 < 30;"}; static constexpr char kComplex2000[]{"SELECT departments_2000.id*2, employees_200.id+1 FROM employees_200 RIGHT JOIN departments_2000 ON employees_200.department_id = departments_2000.id AND departments_2000.id > 3 AND departments_2000.id*2*2/2/2*2 < 30;"}; @@ -40,6 +40,9 @@ void BM_SQL(benchmark::State& state) { Executor executor(std::move(seq_scan), co_await boost::asio::this_coro::executor); + // NOTE: precompile query + benchmark::DoNotOptimize(co_await executor.Execute(op)); + for (auto _ : state) { benchmark::DoNotOptimize(co_await executor.Execute(op)); } @@ -55,8 +58,8 @@ BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); BENCHMARK(BM_SQL); diff --git a/benchmarks/results-01.json b/benchmarks/results-01.json new file mode 100644 index 0000000..70f9e5f --- /dev/null +++ b/benchmarks/results-01.json @@ -0,0 +1,294 @@ +{ + "context": { + "date": "2026-01-10T22:17:41+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [0.936523,0.910156,0.82959], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "BM_SQL", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 37973, + "real_time": 1.8439718747488580e+04, + "cpu_time": 1.8393154688857870e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 37635, + "real_time": 1.8646254444011862e+04, + "cpu_time": 1.8605846924405472e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 11616, + "real_time": 6.1504978047441888e+04, + "cpu_time": 6.1064501205234126e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 11736, + "real_time": 5.8408048312897139e+04, + "cpu_time": 5.8085960889570553e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 9930, + "real_time": 7.0793681671783255e+04, + "cpu_time": 7.0392283987915405e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 10340, + "real_time": 6.7845838684643837e+04, + "cpu_time": 6.7519644680851081e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 959, + "real_time": 7.3587727215961309e+05, + "cpu_time": 7.3293833785192831e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1324, + "real_time": 5.3172040483321249e+05, + "cpu_time": 5.2990015785498510e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 959, + "real_time": 7.3307096767407400e+05, + "cpu_time": 7.3027849426485947e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1283, + "real_time": 5.3211894699760526e+05, + "cpu_time": 5.3048981060015631e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 615, + "real_time": 1.1423289658545756e+06, + "cpu_time": 1.1391460959349603e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 764, + "real_time": 9.1461425262047106e+05, + "cpu_time": 9.1169844371727761e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 359, + "real_time": 1.9482222869099684e+06, + "cpu_time": 1.9423905626740968e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 417, + "real_time": 1.6953180815330239e+06, + "cpu_time": 1.6724347146282962e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 198, + "real_time": 3.5503262929110364e+06, + "cpu_time": 3.5421274595959657e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 220, + "real_time": 3.1995414954508306e+06, + "cpu_time": 3.1910090772727318e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 104, + "real_time": 6.7452504230669336e+06, + "cpu_time": 6.7309672692307681e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "BM_SQL", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 113, + "real_time": 6.2372058495609770e+06, + "cpu_time": 6.2177674513274478e+06, + "time_unit": "ns" + } + ] +} diff --git a/flake.nix b/flake.nix index 90cbcc5..ea1cd58 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,8 @@ zlib zlib.dev gdb + perf + perl ]; nativeBuildInputs = [ diff --git a/flamegraph.svg b/flamegraph.svg new file mode 100644 index 0000000..e69de29 From e348076c031ee4cf2a54e73c2b6973389d2e4aa2 Mon Sep 17 00:00:00 2001 From: Alexandr Starovoytov Date: Sun, 11 Jan 2026 02:06:08 +0300 Subject: [PATCH 39/43] Add report --- .gitignore | 5 + benchmarks/analysis.ipynb | 513 ++- benchmarks/flamegraph-jit.svg | 2780 +++++++++++++---- benchmarks/main.cpp | 86 +- benchmarks/results-02.json | 490 +++ flake.nix | 12 +- .../stewkk/sql/logic/executor/buffer_size.hpp | 2 +- report/Emblem.png | Bin 0 -> 176274 bytes report/Makefile | 20 + report/biblio.bib | 63 + report/dbms_scheme.png | Bin 0 -> 24412 bytes report/dbms_scheme.puml | 33 + report/expression.png | Bin 0 -> 14619 bytes report/query.png | Bin 0 -> 28923 bytes report/report.tex | 1101 +++++++ 15 files changed, 4464 insertions(+), 641 deletions(-) create mode 100644 benchmarks/results-02.json create mode 100644 report/Emblem.png create mode 100644 report/Makefile create mode 100644 report/biblio.bib create mode 100644 report/dbms_scheme.png create mode 100644 report/dbms_scheme.puml create mode 100644 report/expression.png create mode 100644 report/query.png create mode 100644 report/report.tex diff --git a/.gitignore b/.gitignore index f3eafaf..b5c418b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ /build-sanitizers/ /build-release/ FlameGraph/ +/.vscode/ +/perf.data +/perf.data.old +**/.auctex-auto/ +**/build/ diff --git a/benchmarks/analysis.ipynb b/benchmarks/analysis.ipynb index ae34f8f..3962465 100644 --- a/benchmarks/analysis.ipynb +++ b/benchmarks/analysis.ipynb @@ -894,6 +894,507 @@ "plt.tight_layout()\n", "plt.show()\n" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25a5fe61", + "metadata": {}, + "outputs": [], + "source": [ + "data_mt = \"\"\"\n", + "{\n", + " \"context\": {\n", + " \"date\": \"2026-01-10T23:38:31+03:00\",\n", + " \"host_name\": \"nixos\",\n", + " \"executable\": \"./build-release/bin/benchmarks\",\n", + " \"num_cpus\": 8,\n", + " \"mhz_per_cpu\": 4200,\n", + " \"cpu_scaling_enabled\": true,\n", + " \"caches\": [\n", + " {\n", + " \"type\": \"Data\",\n", + " \"level\": 1,\n", + " \"size\": 49152,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Instruction\",\n", + " \"level\": 1,\n", + " \"size\": 32768,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Unified\",\n", + " \"level\": 2,\n", + " \"size\": 1310720,\n", + " \"num_sharing\": 2\n", + " },\n", + " {\n", + " \"type\": \"Unified\",\n", + " \"level\": 3,\n", + " \"size\": 8388608,\n", + " \"num_sharing\": 8\n", + " }\n", + " ],\n", + " \"load_avg\": [0.419922,0.788574,0.725586],\n", + " \"library_version\": \"v1.9.0\",\n", + " \"library_build_type\": \"release\",\n", + " \"json_schema_version\": 1\n", + " },\n", + " \"benchmarks\": [\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 0,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 39131,\n", + " \"real_time\": 1.7763904934746992e+04,\n", + " \"cpu_time\": 1.7715753596892489e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 1,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 38563,\n", + " \"real_time\": 1.8130024764634931e+04,\n", + " \"cpu_time\": 1.8088057438477295e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 2,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 12373,\n", + " \"real_time\": 5.7384221530711082e+04,\n", + " \"cpu_time\": 5.7042944718338294e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 3,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 12308,\n", + " \"real_time\": 5.6059255443637892e+04,\n", + " \"cpu_time\": 5.5761686545336357e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 4,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 10318,\n", + " \"real_time\": 6.7906289300143544e+04,\n", + " \"cpu_time\": 6.7580570653227391e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 5,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 10710,\n", + " \"real_time\": 6.5059496918799705e+04,\n", + " \"cpu_time\": 6.4733736414565828e+04,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 6,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 916,\n", + " \"real_time\": 6.8747337554748205e+05,\n", + " \"cpu_time\": 6.8549197270742385e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 7,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 1368,\n", + " \"real_time\": 5.1371013011559722e+05,\n", + " \"cpu_time\": 5.0669114400584786e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 8,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 1016,\n", + " \"real_time\": 6.8965960629676247e+05,\n", + " \"cpu_time\": 6.8773966338582651e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 9,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 1376,\n", + " \"real_time\": 5.0798636846138834e+05,\n", + " \"cpu_time\": 5.0643320566860412e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 10,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 646,\n", + " \"real_time\": 1.0864569783272836e+06,\n", + " \"cpu_time\": 1.0833641873065040e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 11,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 796,\n", + " \"real_time\": 8.7479362060439051e+05,\n", + " \"cpu_time\": 8.7213186055276426e+05,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 12,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 378,\n", + " \"real_time\": 1.8729881534425891e+06,\n", + " \"cpu_time\": 1.8523415952380972e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 13,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 440,\n", + " \"real_time\": 1.6120009977302372e+06,\n", + " \"cpu_time\": 1.5915791840909088e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 14,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 206,\n", + " \"real_time\": 3.3982538106746450e+06,\n", + " \"cpu_time\": 3.3882851407766948e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 15,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 231,\n", + " \"real_time\": 3.0235657922004336e+06,\n", + " \"cpu_time\": 3.0163061558441538e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 16,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 108,\n", + " \"real_time\": 6.4594497962725991e+06,\n", + " \"cpu_time\": 6.4458291388888815e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL/real_time\",\n", + " \"family_index\": 17,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 117,\n", + " \"real_time\": 5.9735239316131240e+06,\n", + " \"cpu_time\": 5.8960350598290535e+06,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 18,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 5314,\n", + " \"real_time\": 1.3457497459533918e+05,\n", + " \"cpu_time\": 6.7566829130599754e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 19,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 5168,\n", + " \"real_time\": 1.3406582294902750e+05,\n", + " \"cpu_time\": 7.0243527476779418e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 20,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 607,\n", + " \"real_time\": 1.1404998929171630e+06,\n", + " \"cpu_time\": 5.1917100494251117e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 21,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 706,\n", + " \"real_time\": 9.3259300000174274e+05,\n", + " \"cpu_time\": 4.6985750708203832e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 22,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 630,\n", + " \"real_time\": 1.1420491063507230e+06,\n", + " \"cpu_time\": 6.7041952380945313e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 23,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 756,\n", + " \"real_time\": 1.0241538029106023e+06,\n", + " \"cpu_time\": 7.8318994708982927e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 24,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 381,\n", + " \"real_time\": 1.8506752703352764e+06,\n", + " \"cpu_time\": 4.9503858267712976e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 25,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 406,\n", + " \"real_time\": 1.6802704630580470e+06,\n", + " \"cpu_time\": 5.1671182266014257e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 26,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 214,\n", + " \"real_time\": 3.3278060280315354e+06,\n", + " \"cpu_time\": 6.8044112149555713e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 27,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 215,\n", + " \"real_time\": 3.2163211860596528e+06,\n", + " \"cpu_time\": 6.8673441860502680e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 28,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 114,\n", + " \"real_time\": 6.1605299473612504e+06,\n", + " \"cpu_time\": 5.7971666666632946e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 29,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 113,\n", + " \"real_time\": 5.8682074159150943e+06,\n", + " \"cpu_time\": 5.4813097345176830e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 30,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 61,\n", + " \"real_time\": 1.1749900573734328e+07,\n", + " \"cpu_time\": 6.1474918032748528e+03,\n", + " \"time_unit\": \"ns\"\n", + " },\n", + " {\n", + " \"name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"family_index\": 31,\n", + " \"per_family_instance_index\": 0,\n", + " \"run_name\": \"BM_SQL_Multithreaded/real_time\",\n", + " \"run_type\": \"iteration\",\n", + " \"repetitions\": 1,\n", + " \"repetition_index\": 0,\n", + " \"threads\": 1,\n", + " \"iterations\": 61,\n", + " \"real_time\": 1.1562019360650590e+07,\n", + " \"cpu_time\": 7.6402295082020755e+03,\n", + " \"time_unit\": \"ns\"\n", + " }\n", + " ]\n", + "}\n", + "\"\"\"" + ] } ], "metadata": { @@ -901,18 +1402,6 @@ "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.11" } }, "nbformat": 4, diff --git a/benchmarks/flamegraph-jit.svg b/benchmarks/flamegraph-jit.svg index 0d15927..c42206e 100644 --- a/benchmarks/flamegraph-jit.svg +++ b/benchmarks/flamegraph-jit.svg @@ -1,6 +1,6 @@ - + @@ -422,1221 +422,2785 @@ } ]]> - + Flame Graph - + Reset Zoom Search ic - + -boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete (3,439,095,085 samples, 90.52%) -boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete +pthread_cond_signal@@GLIBC_2.3.2 (12,413,973 samples, 0.23%) + -antlr4::atn::LexerATNSimulator::computeTargetState (9,372,331 samples, 0.25%) - +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (41,284,771 samples, 0.77%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +cfree@GLIBC_2.2.5 (20,221,764 samples, 0.38%) + -benchmark::RunSpecifiedBenchmarks (3,601,603,158 samples, 94.80%) -benchmark::RunSpecifiedBenchmarks +boost_asio_detail_posix_thread_function (5,097,584,863 samples, 94.65%) +boost_asio_detail_posix_thread_function -std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> > (40,849,199 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -operator new (162,107,227 samples, 4.27%) -opera.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -_int_free (40,824,204 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -std::pair<std::__detail::_Node_iterator<std::shared_ptr<antlr4::atn::PredictionContext const>, true, true>, bool> std::_Hashtable<std::shared_ptr<antlr4::atn::PredictionContext const>, std::shared_ptr<antlr4::atn::PredictionContext const>, std::allocator<std::shared_ptr<antlr4::atn::PredictionContext const> >, std::__detail::_Identity, antlr4::atn::PredictionContextCache::PredictionContextComparer, antlr4::atn::PredictionContextCache::PredictionContextHasher, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::_M_insert_unique<std::shared_ptr<antlr4::atn::PredictionContext const> const&, std::shared_ptr<antlr4::atn::PredictionContext const> const&, std::__detail::_AllocNode<std::allocator<std::__detail::_Hash_node<std::shared_ptr<antlr4::atn::PredictionContext const>, true> > > > (25,034,228 samples, 0.66%) - +boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete (4,253,950,191 samples, 78.98%) +boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_ope.. -void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (40,315,717 samples, 1.06%) - +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (1,330,290,316 samples, 24.70%) +boost::asio::detail::awaitable_thread<b.. -antlr4::atn::ParserATNSimulator::closure (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (41,357,713 samples, 1.09%) - +void boost::asio::execution::detail::any_executor_base::copy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (7,806,326 samples, 0.14%) + -stewkk::sql::CsvDirSequentialScanner::operator (163,475,163 samples, 4.30%) -stewk.. +_int_free (44,735,347 samples, 0.83%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -pthread_mutex_lock@@GLIBC_2.2.5 (41,061,571 samples, 1.08%) - +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (26,209,776 samples, 0.49%) + -_int_free (118,555,905 samples, 3.12%) -_in.. +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> >, std::allocator<void> > (1,378,912,689 samples, 25.60%) +void boost::asio::detail::executor_funct.. -boost::asio::detail::scheduler::work_finished (41,302,658 samples, 1.09%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::computeStartState (34,013,412 samples, 0.90%) - +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (26,937,911 samples, 0.50%) + -void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code> > (81,364,301 samples, 2.14%) -v.. +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (26,209,776 samples, 0.49%) + -stewkk::sql::codegen::PostgreSQLParser::columnref (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (69,759,531 samples, 1.30%) + -void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > (161,995,300 samples, 4.26%) -void .. +stewkk::sql::ReceiveTuples (42,963,583 samples, 0.80%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + -[unknown] (106,618,516 samples, 2.81%) -[u.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -_int_malloc (39,701,173 samples, 1.04%) - +_int_free (50,763,113 samples, 0.94%) + -stewkk::sql::ReceiveTuples (40,555,284 samples, 1.07%) - +boost::asio::detail::executor_function::impl<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (20,782,792 samples, 0.39%) + -boost::asio::detail::scheduler::work_finished (41,261,829 samples, 1.09%) - +std::unique_ptr<(anonymous namespace)::PostgreSQLLexerStaticData, std::default_delete<(anonymous namespace)::PostgreSQLLexerStaticData> >::~unique_ptr (29,421,778 samples, 0.55%) + -antlr4::atn::ParserATNSimulator::canDropLoopEntryEdgeInLeftRecursiveRule (34,013,412 samples, 0.90%) - +_int_malloc (17,249,947 samples, 0.32%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (29,672,186 samples, 0.55%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_qual (84,967,385 samples, 2.24%) -s.. +stewkk::sql::codegen::PostgreSQLParser::target_list_ (24,508,665 samples, 0.46%) + -_ZZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateEENKUlvE_clEv.resume (109,835,884 samples, 2.89%) -_Z.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -boost::asio::detail::scheduler::work_finished (40,370,745 samples, 1.06%) - +__pthread_mutex_cond_lock (25,449,055 samples, 0.47%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > > (936,893,606 samples, 24.66%) -void boost::asio::execution::detail::an.. +__GI___lll_lock_wait (41,186,819 samples, 0.76%) + -boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (80,794,403 samples, 2.13%) -v.. +antlr4::Lexer::nextToken (14,786,529 samples, 0.27%) + -cfree@GLIBC_2.2.5 (201,211,283 samples, 5.30%) -cfree@.. +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (73,960,902 samples, 1.37%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (40,315,717 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::target_el (84,967,385 samples, 2.24%) -s.. +_ZZZN6stewkk3sql20BM_SQL_MultithreadedINS0_29InterpretedExpressionExecutorEXadsoKcL_ZNS0_L12kComplex2000EEEEEEvRN9benchmark5StateEENKUlvE_clEvENKUlvE_clEv.resume (63,815,507 samples, 1.18%) + -antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::stmtblock (100,463,553 samples, 2.64%) -st.. +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (2,612,697,086 samples, 48.51%) +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::.. -boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (39,685,691 samples, 1.04%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -all (3,799,319,765 samples, 100%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_between (24,508,665 samples, 0.46%) + -malloc (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::ReceiveTuples (40,350,342 samples, 1.06%) - +__condvar_confirm_wakeup (21,216,559 samples, 0.39%) + -void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (82,382,664 samples, 2.17%) -v.. +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (25,825,382 samples, 0.48%) + -[unknown] (59,562,419 samples, 1.57%) - +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::ExecuteProjection (28,645,678 samples, 0.53%) + -malloc (40,555,284 samples, 1.07%) - +_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (43,166,371 samples, 0.80%) + -__libc_start_call_main (3,601,603,158 samples, 94.80%) -__libc_start_call_main +boost::asio::detail::scheduler::work_finished (21,270,699 samples, 0.39%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +std::enable_if<call_traits<boost_asio_require_fn::impl, boost::asio::any_io_executor const&, void (47,436,972 samples, 0.88%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (245,006,271 samples, 6.45%) -void boo.. +antlr4::atn::ParserATNSimulator::adaptivePredict (24,508,665 samples, 0.46%) + -antlr4::atn::LexerATNSimulator::getReachableConfigSet (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -std::enable_if<call_traits<boost_asio_require_fn::impl, boost::asio::any_io_executor const&, void (41,024,951 samples, 1.08%) - +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (19,405,153 samples, 0.36%) + -stewkk::sql::codegen::PostgreSQLParser::stmtmulti (100,463,553 samples, 2.64%) -st.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::c_expr (25,034,228 samples, 0.66%) - +std::_Hashtable<std::shared_ptr<antlr4::atn::ATNConfig>, std::shared_ptr<antlr4::atn::ATNConfig>, std::allocator<std::shared_ptr<antlr4::atn::ATNConfig> >, std::__detail::_Identity, antlr4::atn::ATNConfig::Comparer, antlr4::atn::ATNConfig::Hasher, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >::count (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +std::_Sp_counted_ptr_inplace<antlr4::atn::SingletonPredictionContext, std::allocator<void>, (29,421,778 samples, 0.55%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteJoin (522,843,079 samples, 13.76%) -stewkk::sql::Executo.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::ConcatTuples (121,354,626 samples, 3.19%) -ste.. +stewkk::sql::codegen::PostgreSQLParser::a_expr_or (37,424,238 samples, 0.69%) + -boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_typecast (24,508,665 samples, 0.46%) + -antlr4::atn::ATNSimulator::getCachedContext (25,034,228 samples, 0.66%) - +stewkk::sql::codegen::PostgreSQLParser::simple_select_intersect (61,932,903 samples, 1.15%) + -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::asio::detail::executor_function> (2,228,166,127 samples, 58.65%) -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::as.. +antlr4::atn::PredictionContextMergeCache::Entry::~Entry (37,424,238 samples, 0.69%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_at_time_zone (25,034,228 samples, 0.66%) - +boost::asio::detail::scheduler::work_finished (81,354,602 samples, 1.51%) + -boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (527,102,043 samples, 13.87%) -boost::asio::detail::.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_in (50,953,973 samples, 1.34%) - +__memmove_evex_unaligned_erms (9,485,663 samples, 0.18%) + -malloc_consolidate (15,496,168 samples, 0.41%) - +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,715,823,435 samples, 50.43%) +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_di.. -__libc_start_main@@GLIBC_2.34 (3,601,603,158 samples, 94.80%) -__libc_start_main@@GLIBC_2.34 +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (86,917,634 samples, 1.61%) + -_int_free (120,389,579 samples, 3.17%) -_in.. +boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (627,608,615 samples, 11.65%) +boost::asio::deta.. -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (39,685,691 samples, 1.04%) - +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u>::do_execute<boost::asio::detail::executor_function> (43,166,371 samples, 0.80%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,187,300,681 samples, 57.57%) -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asi.. +std::_Deque_base<boost::asio::detail::completion_payload<void (23,984,314 samples, 0.45%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_isnull (25,034,228 samples, 0.66%) - +benchmark::RunSpecifiedBenchmarks (100,364,807 samples, 1.86%) +b.. -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) - +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (24,917,780 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::Execute (123,171,394 samples, 3.24%) -ste.. +non-virtual thunk to antlr4::Lexer::nextToken (14,786,529 samples, 0.27%) + -boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (81,364,301 samples, 2.14%) -b.. +operator new (17,249,947 samples, 0.32%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (43,166,371 samples, 0.80%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > > (1,429,901,377 samples, 26.55%) +void boost::asio::execution::detail::any_e.. -std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >* std::__do_uninit_copy<std::ranges::transform_view<std::ranges::ref_view<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteProjection (242,357,273 samples, 6.38%) -std::vec.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -SetImpliedBits (7,929,152 samples, 0.21%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -cfree@GLIBC_2.2.5 (40,824,204 samples, 1.07%) - +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code> > (188,525,570 samples, 3.50%) +voi.. -stewkk::sql::codegen::PostgreSQLParser::select_clause (84,967,385 samples, 2.24%) -s.. +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (19,129,396 samples, 0.36%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::LexerATNSimulator::matchATN (9,372,331 samples, 0.25%) - +stewkk::sql::codegen::PostgreSQLParser::colid (24,508,665 samples, 0.46%) + -_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) - +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,664,225,604 samples, 49.47%) +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::det.. -_int_free_maybe_consolidate.part.0 (40,780,288 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (2,500,706,936 samples, 46.43%) +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump -antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) - +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (16,513,150 samples, 0.31%) + -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::execution::detail::any_executor_base::copy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (40,041,233 samples, 1.05%) - +stewkk::sql::MaterializeChannel (16,892,604 samples, 0.31%) + -pthread_mutex_unlock@plt (40,513,751 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::BufferedTokenStream::setup (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> > (40,030,747 samples, 1.05%) - +antlr4::atn::ParserATNSimulator::adaptivePredict (37,424,238 samples, 0.69%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (41,067,820 samples, 1.08%) - +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (26,209,776 samples, 0.49%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteProjection (484,047,544 samples, 12.74%) -stewkk::sql::Execut.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (205,111,508 samples, 5.40%) -void b.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_compare (25,034,228 samples, 0.66%) - +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::default_tag> (12,637,984 samples, 0.23%) + -boost::asio::detail::scheduler::work_cleanup::~work_cleanup (41,261,829 samples, 1.09%) - +benchmark::internal::BenchmarkInstance::Run (91,907,772 samples, 1.71%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) - +[unknown] (20,125,463 samples, 0.37%) + -void boost::asio::execution::detail::any_executor_base::move_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (41,323,068 samples, 1.09%) - +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (48,185,083 samples, 0.89%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (109,835,884 samples, 2.89%) -vo.. +stewkk::sql::codegen::PostgreSQLParser::target_el (24,508,665 samples, 0.46%) + -llvm::po_iterator<llvm::Function*, llvm::SmallPtrSet<llvm::BasicBlock*, 8u>, false, llvm::GraphTraits<llvm::Function*> >::traverseChild (8,946,159 samples, 0.24%) - +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (12,490,400 samples, 0.23%) + -benchmarks (3,799,319,765 samples, 100.00%) -benchmarks +__GI___lll_lock_wait (5,050,505 samples, 0.09%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +_ZN6stewkk3sql20BM_SQL_MultithreadedINS0_29InterpretedExpressionExecutorEXadsoKcL_ZNS0_L12kComplex2000EEEEEEvRN9benchmark5StateE (91,907,772 samples, 1.71%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -malloc (80,416,204 samples, 2.12%) -m.. +antlr4::atn::ATNConfig::~ATNConfig (29,421,778 samples, 0.55%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_or (84,967,385 samples, 2.24%) -s.. +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (26,937,911 samples, 0.50%) + -benchmark::internal::BenchmarkInstance::Run (3,601,603,158 samples, 94.80%) -benchmark::internal::BenchmarkInstance::Run +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (39,894,763 samples, 1.05%) - +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (230,596,188 samples, 4.28%) +void .. -stewkk::sql::codegen::PostgreSQLParser::stmt (100,463,553 samples, 2.64%) -st.. +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_qualop (24,508,665 samples, 0.46%) + -void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) - +_int_free (45,110,211 samples, 0.84%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +boost::asio::detail::executor_op<boost::asio::detail::executor_function, std::allocator<void>, boost::asio::detail::scheduler_operation>::do_complete (91,907,772 samples, 1.71%) + -uw_update_context_1 (33,889,616 samples, 0.89%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_lessless (24,508,665 samples, 0.46%) + -malloc (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +malloc (15,188,340 samples, 0.28%) + -stewkk::sql::codegen::PostgreSQLParser::simple_select_pramary (84,967,385 samples, 2.24%) -s.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (774,233,850 samples, 20.38%) -boost::asio::detail::awaitable_.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::scheduler::do_run_one (3,480,356,914 samples, 91.60%) -boost::asio::detail::scheduler::do_run_one +stewkk::sql::codegen::PostgreSQLParser::identifier (24,508,665 samples, 0.46%) + -antlr4::atn::LexerATNSimulator::execATN (9,372,331 samples, 0.25%) - +pthread_mutex_lock@@GLIBC_2.2.5 (15,169,748 samples, 0.28%) + -malloc@plt (25,919,745 samples, 0.68%) - +benchmark::internal::BenchmarkFamilies::FindBenchmarks (8,457,035 samples, 0.16%) + -boost::asio::awaitable<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > >, boost::asio::any_io_executor>::await_resume (40,478,988 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +boost::asio::detail::awaitable_async_op<void (43,166,371 samples, 0.80%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_add (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_not (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +stewkk::sql::codegen::PostgreSQLParser::stmtblock (61,932,903 samples, 1.15%) + -cfree@GLIBC_2.2.5 (81,209,468 samples, 2.14%) -c.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,351,062,937 samples, 61.88%) -void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asi.. +boost::asio::detail::scheduler::work_finished (29,261,323 samples, 0.54%) + -_ZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateE (3,601,603,158 samples, 94.80%) -_ZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateE +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (22,304,436 samples, 0.41%) + -boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (80,926,524 samples, 2.13%) -b.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -main (3,601,603,158 samples, 94.80%) -main +stewkk::sql::codegen::PostgreSQLParser::selectstmt (61,932,903 samples, 1.15%) + -__GI_____strtoll_l_internal (81,010,836 samples, 2.13%) -_.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -boost::asio::detail::scheduler::run (3,601,603,158 samples, 94.80%) -boost::asio::detail::scheduler::run +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void> > (121,412,151 samples, 3.20%) -voi.. +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u> > (20,879,359 samples, 0.39%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_like (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -[unknown] (59,562,419 samples, 1.57%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -execute_cfa_program_specialized (40,605,147 samples, 1.07%) - +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u>::do_execute<boost::asio::detail::executor_function> (43,166,371 samples, 0.80%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +stewkk::sql::codegen::PostgreSQLParser::stmt (61,932,903 samples, 1.15%) + -benchmark::internal::(anonymous namespace)::RunInThread (3,601,603,158 samples, 94.80%) -benchmark::internal::(anonymous namespace)::RunInThread +pthread_cond_signal@@GLIBC_2.3.2 (41,261,557 samples, 0.77%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void>, std::allocator<void> > (978,196,264 samples, 25.75%) -void boost::asio::detail::executor_funct.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -benchmark::internal::BenchmarkRunner::DoOneRepetition (3,601,603,158 samples, 94.80%) -benchmark::internal::BenchmarkRunner::DoOneRepetition +_int_free (37,424,238 samples, 0.69%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_between (50,953,973 samples, 1.34%) - +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (21,531,340 samples, 0.40%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -operator new (80,416,204 samples, 2.12%) -o.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -std::_Function_handler<stewkk::sql::Value (40,383,450 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +stewkk::sql::codegen::PostgreSQLParser::PostgreSQLParser (15,188,340 samples, 0.28%) + -boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (327,144,129 samples, 8.61%) -boost::asio:.. +malloc (25,168,871 samples, 0.47%) + -std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (41,027,112 samples, 1.08%) - +antlr4::atn::OrderedATNConfigSet::~OrderedATNConfigSet (29,421,778 samples, 0.55%) + -antlr4::atn::ParserATNSimulator::closure (34,013,412 samples, 0.90%) - +stewkk::sql::CalcExpression (47,573,615 samples, 0.88%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_send_op<boost::asio::experimental::channel_traits<>, void (364,798,231 samples, 9.60%) -void boost::as.. +cfree@GLIBC_2.2.5 (8,183,224 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -pthread_mutex_lock@@GLIBC_2.2.5 (41,364,543 samples, 1.09%) - +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::ExecuteJoin (863,671,399 samples, 16.04%) +stewkk::sql::Executor<st.. -stewkk::sql::GetAST (109,835,884 samples, 2.89%) -st.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (24,108,290 samples, 0.45%) + -_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) - +boost::asio::detail::scheduler::do_run_one (91,907,772 samples, 1.71%) + -_start (3,601,603,158 samples, 94.80%) -_start +pthread_mutex_lock@plt (8,018,740 samples, 0.15%) + -operator new (121,717,805 samples, 3.20%) -ope.. +stewkk::sql::ReceiveTuples (45,343,074 samples, 0.84%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +pthread_cond_signal@@GLIBC_2.3.2 (29,271,007 samples, 0.54%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_at_time_zone (24,508,665 samples, 0.46%) + -_int_malloc (39,726,451 samples, 1.05%) - +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (573,768,071 samples, 10.65%) +void boost::asi.. -stewkk::sql::codegen::PostgreSQLParser::select_no_parens (84,967,385 samples, 2.24%) -s.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::LexerATNSimulator::match (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_caret (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +boost::asio::detail::scheduler::post_immediate_completion (22,757,469 samples, 0.42%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +malloc (92,078,600 samples, 1.71%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) - +stewkk::sql::CalcExpression (282,042,305 samples, 5.24%) +stewkk.. -antlr4::atn::PredictionContextCache::put (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ATNConfigSet::optimizeConfigs (25,034,228 samples, 0.66%) - +boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> > > (9,314,054 samples, 0.17%) + -void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >::operator (2,187,300,681 samples, 57.57%) -void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::erro.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -boost::asio::detail::executor_function::impl<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,824,204 samples, 1.07%) - +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (19,129,396 samples, 0.36%) + -cfree@GLIBC_2.2.5 (40,780,288 samples, 1.07%) - +stewkk::sql::codegen::PostgreSQLParser::stmtmulti (61,932,903 samples, 1.15%) + -antlr4::BufferedTokenStream::sync (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (41,070,038 samples, 1.08%) - +_int_malloc (92,078,600 samples, 1.71%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::ExecuteJoin (40,555,284 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_collate (25,034,228 samples, 0.66%) - +antlr4::dfa::DFA::~DFA (29,421,778 samples, 0.55%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (486,786,326 samples, 12.81%) -void boost::asio::e.. +uw_update_context_1 (25,467,846 samples, 0.47%) + -antlr4::atn::ParserATNSimulator::adaptivePredict (15,496,168 samples, 0.41%) - +stewkk::sql::ApplyProjection (282,042,305 samples, 5.24%) +stewkk.. -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,228,166,127 samples, 58.65%) -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<bo.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (161,995,300 samples, 4.26%) -void .. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (41,054,766 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::BufferedTokenStream::fill (9,372,331 samples, 0.25%) - +malloc_consolidate (12,089,111 samples, 0.22%) + -std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (40,583,149 samples, 1.07%) - +antlr4::atn::LexerATNSimulator::match (14,786,529 samples, 0.27%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_and (50,953,973 samples, 1.34%) - +antlr4::atn::ATNState::addTransition (15,188,340 samples, 0.28%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr (84,967,385 samples, 2.24%) -s.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::atn::ATNConfigSet::add (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::CsvDirSequentialScanner::operator (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -_ZZN6stewkk3sql6BM_SQLINS0_35CachedJitCompiledExpressionExecutorEXadsoKcL_ZNS0_L12kComplex4000EEEEEEvRN9benchmark5StateEENKUlvE_clEv.resume (40,780,288 samples, 1.07%) - +antlr4::atn::LexerATNSimulator::computeTargetState (14,786,529 samples, 0.27%) + -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) - +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (16,186,366 samples, 0.30%) + -boost::asio::detail::awaitable_async_op<void (40,958,645 samples, 1.08%) - +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<stewkk::sql::AttributeInfo, std::allocator<stewkk::sql::AttributeInfo> > >::operator (26,937,911 samples, 0.50%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +stewkk::sql::codegen::PostgreSQLParser::c_expr (24,508,665 samples, 0.46%) + -_ZNSt6ranges14__copy_or_moveILb0ETkSt14input_iteratorPKN6stewkk3sql5ValueETkSt12sentinel_forIT0_ES5_TkSt20weakly_incrementableSt20back_insert_iteratorISt6vectorIS3_SaIS3_EEEQquT_18indirectly_movableIS7_T2_E19indirectly_copyableIS7_SD_EEENSt13__conditionalIXT_EE4typeINS_13in_out_resultIS7_SD_EESI_EES7_T1_SD_ (81,633,781 samples, 2.15%) -_.. +void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (43,166,371 samples, 0.80%) + -operator new (80,410,706 samples, 2.12%) -o.. +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (23,872,024 samples, 0.44%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (54,188,585 samples, 1.01%) + -boost::asio::awaitable<void, boost::asio::any_io_executor> boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (41,150,539 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -benchmark::internal::BenchmarkRunner::DoNIterations (3,601,603,158 samples, 94.80%) -benchmark::internal::BenchmarkRunner::DoNIterations +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (41,067,820 samples, 1.08%) - +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void> > (38,234,974 samples, 0.71%) + -stewkk::sql::codegen::PostgreSQLParser::target_list_ (84,967,385 samples, 2.24%) -s.. +stewkk::sql::GetChannels (23,984,314 samples, 0.45%) + -operator new (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (26,937,911 samples, 0.50%) + -operator new (25,034,228 samples, 0.66%) - +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::handler_work (24,917,780 samples, 0.46%) + -malloc@plt (40,921,300 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_qual_op (25,034,228 samples, 0.66%) - +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (26,937,911 samples, 0.50%) + -boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (161,995,300 samples, 4.26%) -boost.. +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + -_int_malloc (41,112,758 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +malloc_consolidate (16,892,604 samples, 0.31%) + -_ZN5boost4asio8co_spawnINS0_15any_io_executorES2_TkNS0_20completion_token_forIFvNSt15__exception_ptr13exception_ptrEEEERKNS0_10detached_tEEEDaRKT_NS0_9awaitableIvT0_EEOT1_NS0_10constraintIXaaoosr11is_executorISA_EE5valuesr9execution11is_executorISA_EE5valuesr14is_convertibleISA_SE_EE5valueEiE4typeE (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -_int_malloc (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) - +stewkk::sql::CalcExpression (201,925,159 samples, 3.75%) +stew.. -boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (82,382,664 samples, 2.17%) -b.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -stewkk::sql::codegen::PostgreSQLParser::selectstmt (84,967,385 samples, 2.24%) -s.. +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (43,166,371 samples, 0.80%) + -stewkk::sql::codegen::PostgreSQLParser::identifier (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_mul (25,034,228 samples, 0.66%) - +boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (12,841,321 samples, 0.24%) + -operator delete (40,898,461 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -cfree@GLIBC_2.2.5 (40,478,988 samples, 1.07%) - +pthread_cond_signal@@GLIBC_2.3.2 (17,896,964 samples, 0.33%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_is_not (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (109,835,884 samples, 2.89%) -bo.. +__GI___pthread_mutex_unlock_usercnt (111,861,054 samples, 2.08%) +_.. -stewkk::sql::codegen::PostgreSQLParser::colid (25,034,228 samples, 0.66%) - +malloc (255,483,021 samples, 4.74%) +malloc -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +_int_malloc (16,892,604 samples, 0.31%) + -boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,228,166,127 samples, 58.65%) -boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::det.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (40,370,745 samples, 1.06%) - +stewkk::sql::CsvDirSequentialScanner::operator (294,581,404 samples, 5.47%) +stewkk:.. -boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> > > (40,583,149 samples, 1.07%) - +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (238,402,514 samples, 4.43%) +boost.. -antlr4::atn::ParserATNSimulator::closureCheckingStopState (15,496,168 samples, 0.41%) - +[unknown] (25,467,846 samples, 0.47%) + -void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (41,417,591 samples, 1.09%) - +benchmarks (5,385,823,897 samples, 100.00%) +benchmarks -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul>::execute<boost::asio::detail::executor_function> (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::handler_work (39,894,763 samples, 1.05%) - +stewkk::sql::codegen::PostgreSQLParser::from_list (37,424,238 samples, 0.69%) + -void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (40,555,284 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (2,146,150,142 samples, 56.49%) -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump +_int_free (32,503,682 samples, 0.60%) + -stewkk::sql::codegen::PostgreSQLParser::target_list (84,967,385 samples, 2.24%) -s.. +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::Execute (19,182,057 samples, 0.36%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_lessless (84,967,385 samples, 2.24%) -s.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -stewkk::sql::ApplyProjection (80,555,549 samples, 2.12%) -s.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -malloc (121,717,805 samples, 3.20%) -mal.. +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> > > (25,825,382 samples, 0.48%) + -[unknown] (5,237,329 samples, 0.14%) - +antlr4::atn::PredictionContextMergeCache::get (24,508,665 samples, 0.46%) + -stewkk::sql::codegen::PostgreSQLParser::simple_select_intersect (84,967,385 samples, 2.24%) -s.. +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (43,166,371 samples, 0.80%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_typecast (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) - +boost::asio::detail::scheduler::work_finished (28,112,100 samples, 0.52%) + -cfree@GLIBC_2.2.5 (118,555,905 samples, 3.12%) -cfr.. +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +void boost::asio::execution::detail::any_executor_base::move_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (20,184,606 samples, 0.37%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_qualop (25,034,228 samples, 0.66%) - +_int_free (8,438,753 samples, 0.16%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +stewkk::sql::MaterializeChannel (45,068,730 samples, 0.84%) + -malloc_consolidate (40,780,288 samples, 1.07%) - +_ZNSt6ranges14__copy_or_moveILb0ETkSt14input_iteratorPKN6stewkk3sql5ValueETkSt12sentinel_forIT0_ES5_TkSt20weakly_incrementableSt20back_insert_iteratorISt6vectorIS3_SaIS3_EEEQquT_18indirectly_movableIS7_T2_E19indirectly_copyableIS7_SD_EEENSt13__conditionalIXT_EE4typeINS_13in_out_resultIS7_SD_EESI_EES7_T1_SD_ (82,005,608 samples, 1.52%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +operator new (23,984,314 samples, 0.45%) + -void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (82,382,664 samples, 2.17%) -v.. +stewkk::sql::codegen::PostgreSQLParser::a_expr_collate (24,508,665 samples, 0.46%) + -void boost::asio::detail::initiate_dispatch::operator (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -unlink_chunk.isra.0 (40,780,288 samples, 1.07%) - +stewkk::sql::codegen::PostgreSQLParser::root (61,932,903 samples, 1.15%) + -void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (40,958,645 samples, 1.08%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_is_not (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::adaptivePredict (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (39,685,691 samples, 1.04%) - +_int_free (18,818,159 samples, 0.35%) + -malloc (121,118,658 samples, 3.19%) -mal.. +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (40,315,717 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -__memset_evex_unaligned_erms (5,237,329 samples, 0.14%) - +operator new (118,326,047 samples, 2.20%) +o.. -_int_malloc (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -antlr4::atn::PredictionContext::getCachedContext (25,034,228 samples, 0.66%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + -void boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul>::execute<boost::asio::detail::executor_function> (936,893,606 samples, 24.66%) -void boost::asio::io_context::basic_exe.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::addDFAState (25,034,228 samples, 0.66%) - +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void>, std::allocator<void> > (26,133,025 samples, 0.49%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> >, std::allocator<void> > (936,893,606 samples, 24.66%) -void boost::asio::detail::executor_func.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -_int_free (40,556,729 samples, 1.07%) - +_ZZN6stewkk3sql20BM_SQL_MultithreadedINS0_29InterpretedExpressionExecutorEXadsoKcL_ZNS0_L12kComplex2000EEEEEEvRN9benchmark5StateEENKUlvE_clEv.resume (91,907,772 samples, 1.71%) + -non-virtual thunk to antlr4::Lexer::nextToken (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::computeStartState (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -memchr@plt (41,302,733 samples, 1.09%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::Executor<stewkk::sql::CachedJitCompiledExpressionExecutor>::SpawnExecutor (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +__pthread_once_slow.isra.0 (15,188,340 samples, 0.28%) + -pthread_once@GLIBC_2.2.5 (18,957,272 samples, 0.50%) - +benchmark::internal::(anonymous namespace)::RunInThread (91,907,772 samples, 1.71%) + -void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>::operator (855,700,610 samples, 22.52%) -void boost::asio::detail::awaitable.. +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (19,129,396 samples, 0.36%) + -antlr4::atn::ParserATNSimulator::closure_ (15,496,168 samples, 0.41%) - +boost::asio::detail::scheduler::work_finished (23,872,024 samples, 0.44%) + -boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (162,126,040 samples, 4.27%) -boost.. +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (31,970,717 samples, 0.59%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (19,129,396 samples, 0.36%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (41,323,068 samples, 1.09%) - +stewkk::sql::codegen::PostgreSQLParser::a_expr_qual (24,508,665 samples, 0.46%) + -malloc (80,410,706 samples, 2.12%) -m.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -boost::asio::awaitable<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > >, boost::asio::any_io_executor> boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (81,850,497 samples, 2.15%) -b.. +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + -stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_sign (25,034,228 samples, 0.66%) - +operator new (54,664,255 samples, 1.01%) + -void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (40,315,717 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (12,016,681 samples, 0.22%) + -stewkk::sql::codegen::PostgreSQLParser::root (100,463,553 samples, 2.64%) -st.. +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -antlr4::atn::ParserATNSimulator::closureCheckingStopState (34,013,412 samples, 0.90%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::BufferedTokenStream::fetch (9,372,331 samples, 0.25%) - +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -uw_frame_state_for (40,605,147 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (40,958,645 samples, 1.08%) - +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + -stewkk::sql::ReceiveTuples (40,675,052 samples, 1.07%) - +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + -void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (40,958,645 samples, 1.08%) - +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code> > (308,370,936 samples, 5.73%) +void bo.. -benchmark::RunSpecifiedBenchmarks (3,601,603,158 samples, 94.80%) -benchmark::RunSpecifiedBenchmarks +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor, void (39,830,937 samples, 0.74%) + -antlr4::atn::ParserATNSimulator::closure_ (34,013,412 samples, 0.90%) - +__GI___pthread_mutex_unlock_usercnt (23,992,394 samples, 0.45%) + -boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (40,667,781 samples, 1.07%) - +_int_free (8,320,762 samples, 0.15%) + -std::_Function_handler<stewkk::sql::Value (40,183,704 samples, 1.06%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::io_context::basic_executor_type<std::allocator<void>, 4ul> > (41,302,658 samples, 1.09%) - +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + -antlr4::Lexer::nextToken (9,372,331 samples, 0.25%) - +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (1,378,912,689 samples, 25.60%) +void boost::asio::thread_pool::basic_exe.. -antlr4::atn::ParserATNSimulator::computeStartState (15,496,168 samples, 0.41%) - +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + -antlr4::atn::ParserATNSimulator::adaptivePredict (34,013,412 samples, 0.90%) - +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (12,841,321 samples, 0.24%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +__run_exit_handlers (29,421,778 samples, 0.55%) + + + +boost::asio::detail::awaitable_async_op<void (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +__libc_start_main@@GLIBC_2.34 (129,786,585 samples, 2.41%) +__.. + + +std::istream::sentry::sentry (22,556,793 samples, 0.42%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::executor_function_tag> (12,442,960 samples, 0.23%) + + + +antlr4::atn::LexerATNSimulator::getReachableConfigSet (14,786,529 samples, 0.27%) + + + +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::Execute (107,095,505 samples, 1.99%) +s.. + + +__memcmp_evex_movbe (49,134,744 samples, 0.91%) + + + +std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> > (22,407,757 samples, 0.42%) + + + +cfree@GLIBC_2.2.5 (30,709,462 samples, 0.57%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> > > (27,364,608 samples, 0.51%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::executor_function_tag> (23,578,020 samples, 0.44%) + + + +boost::asio::detail::scheduler::work_finished (21,134,143 samples, 0.39%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::table_ref (37,424,238 samples, 0.69%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ArrayPredictionContext::hashCodeImpl (24,508,665 samples, 0.46%) + + + +stewkk::sql::codegen::PostgreSQLParser::select_clause (61,932,903 samples, 1.15%) + + + +void boost::asio::execution::detail::any_executor_base::move_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (19,495,844 samples, 0.36%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_send_op<boost::asio::experimental::channel_traits<>, void (26,209,776 samples, 0.49%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +operator new (15,188,340 samples, 0.28%) + + + +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (21,134,143 samples, 0.39%) + + + +_int_free_maybe_consolidate.part.0 (12,089,111 samples, 0.22%) + + + +auto boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<void> (38,357,846 samples, 0.71%) + + + +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (15,477,505 samples, 0.29%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_add (24,508,665 samples, 0.46%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,612,697,086 samples, 48.51%) +void boost::asio::detail::executor_function::complete<boost::asio::detail::bin.. + + +[unknown] (17,932,534 samples, 0.33%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_and (24,508,665 samples, 0.46%) + + + +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (61,465,075 samples, 1.14%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (26,209,776 samples, 0.49%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +stewkk::sql::CalcExpression (201,925,159 samples, 3.75%) +stew.. + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +_int_malloc (161,090,161 samples, 2.99%) +_i.. + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (57,656,311 samples, 1.07%) + + + +_int_free (44,410,066 samples, 0.82%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +pthread_mutex_lock@@GLIBC_2.2.5 (15,477,505 samples, 0.29%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::default_tag> (24,903,038 samples, 0.46%) + + + +_int_malloc (19,182,057 samples, 0.36%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (43,166,371 samples, 0.80%) + + + +antlr4::atn::PredictionContext::mergeArrays (24,508,665 samples, 0.46%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_qual (37,424,238 samples, 0.69%) + + + +std::basic_filebuf<char, std::char_traits<char> >::_M_destroy_internal_buffer (12,089,111 samples, 0.22%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +void boost::asio::execution::detail::any_executor_base::execute_ex<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (21,272,303 samples, 0.39%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +boost::asio::detail::scheduler::work_finished (12,490,400 samples, 0.23%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void>, std::allocator<void> > (1,451,172,076 samples, 26.94%) +void boost::asio::detail::executor_functio.. + + +__libc_start_call_main (129,786,585 samples, 2.41%) +__.. + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +all (5,385,823,897 samples, 100%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (11,823,152 samples, 0.22%) + + + +boost::asio::detail::scheduler::work_finished (69,759,531 samples, 1.30%) + + + +antlr4::atn::ATNDeserializer::deserialize (15,188,340 samples, 0.28%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::any_io_executor, void> > (174,285,447 samples, 3.24%) +voi.. + + +_int_malloc (7,068,924 samples, 0.13%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (26,937,911 samples, 0.50%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_compare (24,508,665 samples, 0.46%) + + + +boost::asio::detail::scheduler::work_finished (22,304,436 samples, 0.41%) + + + +cfree@GLIBC_2.2.5 (77,205,009 samples, 1.43%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_or (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +benchmark::internal::FindBenchmarksInternal (8,457,035 samples, 0.16%) + + + +benchmark::internal::BenchmarkRunner::DoOneRepetition (91,907,772 samples, 1.71%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +malloc_consolidate (19,182,057 samples, 0.36%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::CalcExpression (55,369,899 samples, 1.03%) + + + +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::ExecuteJoin (44,011,107 samples, 0.82%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +auto boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<void> (53,916,274 samples, 1.00%) + + + +antlr4::atn::ParserATNSimulator::closure (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +malloc (23,578,020 samples, 0.44%) + + + +boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::handler_work (9,314,054 samples, 0.17%) + + + +operator new (50,536,679 samples, 0.94%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +[unknown] (5,050,505 samples, 0.09%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::binder1<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>, boost::system::error_code>, boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code> > (12,841,321 samples, 0.24%) + + + +void boost::asio::detail::initiate_dispatch::operator (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (16,892,604 samples, 0.31%) + + + +stewkk::sql::DiskFileWriter::Write (20,168,759 samples, 0.37%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::executor_function_tag> (50,988,688 samples, 0.95%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_qual_op (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (26,209,776 samples, 0.49%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::BufferedTokenStream::fetch (14,786,529 samples, 0.27%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (29,261,323 samples, 0.54%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +stewkk::sql::ReceiveTuples (6,054,643 samples, 0.11%) + + + +cfree@GLIBC_2.2.5 (44,735,347 samples, 0.83%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_mul (24,508,665 samples, 0.46%) + + + +std::deque<boost::asio::detail::completion_payload<void (20,221,764 samples, 0.38%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_send_op<boost::asio::experimental::channel_traits<>, void (26,937,911 samples, 0.50%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +std::type_info const& boost::asio::execution::detail::any_executor_base::target_type_ex<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (26,937,911 samples, 0.50%) + + + +__GI_____strtoll_l_internal (74,846,533 samples, 1.39%) + + + +cfree@GLIBC_2.2.5 (12,089,111 samples, 0.22%) + + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (27,364,608 samples, 0.51%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (43,166,371 samples, 0.80%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (36,124,971 samples, 0.67%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::CalcExpression (129,995,096 samples, 2.41%) +st.. + + +__GI___lll_lock_wait (17,932,534 samples, 0.33%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +malloc (7,068,924 samples, 0.13%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +boost::asio::detail::posix_thread::func<boost::asio::thread_pool::thread_function, boost::asio::execution_context::allocator<void> >::run (5,097,584,863 samples, 94.65%) +boost::asio::detail::posix_thread::func<boost::asio::thread_pool::thread_function, boost::asio::execution_context::allocator<void> >::run + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >* std::__do_uninit_copy<std::ranges::transform_view<std::ranges::ref_view<std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::ExecuteProjection (360,235,704 samples, 6.69%) +std::vect.. + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::~awaitable_thread (26,477,355 samples, 0.49%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_isnull (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +void boost::asio::detail::initiate_co_spawn<boost::asio::any_io_executor>::operator (43,166,371 samples, 0.80%) + + + +void const* boost::asio::execution::detail::any_executor_base::target_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (12,128,983 samples, 0.23%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (176,035,170 samples, 3.27%) +voi.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +void boost::asio::execution::detail::any_executor_base::move_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (24,345,703 samples, 0.45%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (95,180,338 samples, 1.77%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +cfree@GLIBC_2.2.5 (147,744,567 samples, 2.74%) +cf.. + + +stewkk::sql::GetExpressionTypeUnchecked (17,360,474 samples, 0.32%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +boost::asio::detail::scheduler::post_immediate_completion (49,051,102 samples, 0.91%) + + + +antlr4::atn::ATNConfigSet::~ATNConfigSet (29,421,778 samples, 0.55%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +boost::asio::detail::scheduler::do_run_one (5,014,939,460 samples, 93.11%) +boost::asio::detail::scheduler::do_run_one + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (12,841,321 samples, 0.24%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +bcmp@plt (52,396,970 samples, 0.97%) + + + +boost::asio::detail::executor_function::executor_function<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (12,442,960 samples, 0.23%) + + + +benchmark::RunSpecifiedBenchmarks (100,364,807 samples, 1.86%) +b.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_not (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_caret (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +malloc@plt (31,818,749 samples, 0.59%) + + + +boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (372,428,037 samples, 6.91%) +boost::as.. + + +_int_free (24,899,971 samples, 0.46%) + + + +boost::asio::detail::scheduler::run (91,907,772 samples, 1.71%) + + + +std::enable_if<call_traits<boost_asio_prefer_fn::impl, boost::asio::any_io_executor const&, void (9,314,054 samples, 0.17%) + + + +boost::asio::detail::scheduler::post_immediate_completion (23,412,377 samples, 0.43%) + + + +stewkk::sql::codegen::PostgreSQLParser::simple_select_pramary (61,932,903 samples, 1.15%) + + + +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >::operator (2,560,910,854 samples, 47.55%) +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boo.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +void* boost::asio::detail::thread_info_base::allocate<boost::asio::detail::thread_info_base::awaitable_frame_tag> (19,182,057 samples, 0.36%) + + + +boost::asio::experimental::basic_concurrent_channel<boost::asio::any_io_executor, boost::asio::experimental::channel_traits<>, void (23,984,314 samples, 0.45%) + + + +stewkk::sql::codegen::PostgreSQLParser::PostgreSQLParser (15,188,340 samples, 0.28%) + + + +malloc (19,182,057 samples, 0.36%) + + + +boost::asio::detail::scheduler::work_cleanup::~work_cleanup (97,273,190 samples, 1.81%) +b.. + + +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::SpawnExecutor (43,166,371 samples, 0.80%) + + + +std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_append (22,419,907 samples, 0.42%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +boost::asio::awaitable<void, boost::asio::any_io_executor> boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (18,935,279 samples, 0.35%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (43,166,371 samples, 0.80%) + + + +cfree@GLIBC_2.2.5 (8,320,762 samples, 0.15%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (91,907,772 samples, 1.71%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_send_op<boost::asio::experimental::channel_traits<>, void (544,095,885 samples, 10.10%) +void boost::as.. + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (19,129,396 samples, 0.36%) + + + +antlr4::atn::PredictionContextMergeCache::clear (37,424,238 samples, 0.69%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +malloc (17,249,947 samples, 0.32%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::SemanticContext::Predicate::equals (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_in (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::ExecuteProjection (523,860,669 samples, 9.73%) +stewkk::sql::E.. + + +_int_free (12,089,111 samples, 0.22%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +pthread_cond_wait@@GLIBC_2.3.2 (86,512,120 samples, 1.61%) + + + +bool std::__detail::__regex_algo_impl<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, char, std::__cxx11::regex_traits<char> > (8,457,035 samples, 0.16%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_lessless (37,424,238 samples, 0.69%) + + + +start_thread (5,097,584,863 samples, 94.65%) +start_thread + + +boost::asio::detail::scheduler::post_immediate_completion (19,129,396 samples, 0.36%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +malloc (50,536,679 samples, 0.94%) + + + +[unknown] (29,271,007 samples, 0.54%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +void boost::asio::execution::detail::any_executor_base::destroy_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u> > (21,270,699 samples, 0.39%) + + + +boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (2,691,951,411 samples, 49.98%) +boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, bo.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_and (37,424,238 samples, 0.69%) + + + +_int_malloc (42,978,854 samples, 0.80%) + + + +__isoc23_strtol (21,467,623 samples, 0.40%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +boost::asio::detail::executor_function::impl<boost::asio::detail::binder0<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (24,848,321 samples, 0.46%) + + + +cfree@GLIBC_2.2.5 (42,409,786 samples, 0.79%) + + + +void boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 8u>::do_execute<boost::asio::detail::executor_function> (100,817,695 samples, 1.87%) +v.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +boost::asio::detail::scheduler::post_immediate_completion (15,477,505 samples, 0.29%) + + + +_start (129,786,585 samples, 2.41%) +_s.. + + +[libc.so.6] (29,421,778 samples, 0.55%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +boost::asio::detail::scheduler::post_immediate_completion (59,556,138 samples, 1.11%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_receive<boost::asio::experimental::channel_traits<>, void (514,987,329 samples, 9.56%) +void boost::a.. + + +stewkk::sql::CalcExpression (47,573,615 samples, 0.88%) + + + +stewkk::sql::codegen::PostgreSQLParser::columnref (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +boost::asio::experimental::detail::channel_receive_op<boost::asio::detail::completion_payload<void (26,937,911 samples, 0.50%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_like (24,508,665 samples, 0.46%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +void boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code>::operator (1,378,912,689 samples, 25.60%) +void boost::asio::detail::awaitable_hand.. + + +antlr4::dfa::DFAState::~DFAState (29,421,778 samples, 0.55%) + + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (91,907,772 samples, 1.71%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +boost::asio::detail::scheduler::run (5,089,566,123 samples, 94.50%) +boost::asio::detail::scheduler::run + + +stewkk::sql::ConcatTuples (82,005,608 samples, 1.52%) + + + +std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_M_dfs (8,457,035 samples, 0.16%) + + + +void boost::asio::execution::detail::any_executor_base::execute<boost::asio::detail::binder0<boost::asio::detail::awaitable_async_op_handler<void (43,166,371 samples, 0.80%) + + + +boost::asio::detail::awaitable_thread<boost::asio::any_io_executor>::pump (43,166,371 samples, 0.80%) + + + +benchmark::internal::BenchmarkRunner::DoNIterations (91,907,772 samples, 1.71%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +cfree@GLIBC_2.2.5 (45,617,961 samples, 0.85%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::async_send<boost::asio::experimental::channel_traits<>, void (93,040,213 samples, 1.73%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr (37,424,238 samples, 0.69%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +boost::asio::experimental::detail::channel_send_op<boost::asio::detail::completion_payload<void (210,580,654 samples, 3.91%) +boos.. + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +void std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > >::_M_realloc_append<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > (44,735,347 samples, 0.83%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +__memcmp_evex_movbe (39,055,049 samples, 0.73%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +operator new (7,068,924 samples, 0.13%) + + + +boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (652,420,885 samples, 12.11%) +boost::asio::detai.. + + +pthread_once@GLIBC_2.2.5 (15,188,340 samples, 0.28%) + + + +antlr4::atn::PredictionContext::merge (24,508,665 samples, 0.46%) + + + +stewkk::sql::CalcExpression (282,042,305 samples, 5.24%) +stewkk.. + + +void boost::asio::detail::executor_function::complete<boost::asio::detail::work_dispatcher<boost::asio::detail::empty_work_function, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (84,026,690 samples, 1.56%) + + + +cfree@GLIBC_2.2.5 (24,899,971 samples, 0.46%) + + + +void const* boost::asio::execution::detail::any_executor_base::target_object<boost::asio::thread_pool::basic_executor_type<std::allocator<void>, 0u> > (12,841,321 samples, 0.24%) + + + +boost::asio::detail::scheduler::work_finished (44,889,674 samples, 0.83%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +std::default_delete<(anonymous namespace)::PostgreSQLLexerStaticData>::operator (29,421,778 samples, 0.55%) + + + +__GI___pthread_mutex_unlock_usercnt (19,129,396 samples, 0.36%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work_base<boost::asio::any_io_executor, boost::asio::any_io_executor, void>::post<boost::asio::any_io_executor, boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (86,917,634 samples, 1.61%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::a_expr_unary_sign (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +main (100,364,807 samples, 1.86%) +m.. + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::CalcExpression (150,374,277 samples, 2.79%) +st.. + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (24,508,665 samples, 0.46%) + + + +malloc (54,664,255 samples, 1.01%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +cfree@GLIBC_2.2.5 (37,424,238 samples, 0.69%) + + + +antlr4::BufferedTokenStream::fill (14,786,529 samples, 0.27%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +_int_free (21,405,721 samples, 0.40%) + + + +malloc_consolidate (17,249,947 samples, 0.32%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +boost::asio::detail::awaitable_frame_base<boost::asio::any_io_executor>::await_transform<boost::asio::async_result<boost::asio::use_awaitable_t<boost::asio::any_io_executor>, void (26,937,911 samples, 0.50%) + + + +[unknown] (51,827,800 samples, 0.96%) + + + +stewkk::sql::codegen::PostgreSQLParser::select_no_parens (61,932,903 samples, 1.15%) + + + +antlr4::atn::ATNConfigSet::add (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::LexerATNSimulator::execATN (14,786,529 samples, 0.27%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +void boost::asio::detail::initiate_dispatch::operator (43,166,371 samples, 0.80%) + + + +std::_Hashtable<std::pair<antlr4::atn::PredictionContext const*, antlr4::atn::PredictionContext const*>, std::pair<std::pair<antlr4::atn::PredictionContext const*, antlr4::atn::PredictionContext const*> const, std::unique_ptr<antlr4::atn::PredictionContextMergeCache::Entry, std::default_delete<antlr4::atn::PredictionContextMergeCache::Entry> > >, std::allocator<std::pair<std::pair<antlr4::atn::PredictionContext const*, antlr4::atn::PredictionContext const*> const, std::unique_ptr<antlr4::atn::PredictionContextMergeCache::Entry, std::default_delete<antlr4::atn::PredictionContextMergeCache::Entry> > > >, std::__detail::_Select1st, antlr4::atn::PredictionContextMergeCache::PredictionContextComparer, antlr4::atn::PredictionContextMergeCache::PredictionContextHasher, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::find (24,508,665 samples, 0.46%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +stewkk::sql::codegen::PostgreSQLParser::from_clause (37,424,238 samples, 0.69%) + + + +malloc (16,892,604 samples, 0.31%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +stewkk::sql::codegen::PostgreSQLParser::target_list (24,508,665 samples, 0.46%) + + + +stewkk::sql::CalcExpression (150,374,277 samples, 2.79%) +st.. + + +stewkk::sql::codegen::PostgreSQLParser::a_expr (24,508,665 samples, 0.46%) + + + +malloc@plt (23,399,734 samples, 0.43%) + + + +__GI___clone3 (5,097,584,863 samples, 94.65%) +__GI___clone3 + + +stewkk::sql::codegen::PostgreSQLParser::join_qual (37,424,238 samples, 0.69%) + + + +__GI___lll_lock_wake (64,037,564 samples, 1.19%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closure_ (24,508,665 samples, 0.46%) + + + +void boost::asio::experimental::detail::channel_operation::handler_work<boost::asio::detail::awaitable_handler<boost::asio::any_io_executor, boost::system::error_code, std::vector<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> >, std::allocator<std::vector<stewkk::sql::Value, std::allocator<stewkk::sql::Value> > > > >, boost::asio::any_io_executor, void>::immediate<boost::asio::detail::completion_payload_handler<boost::asio::detail::completion_payload<void (238,402,514 samples, 4.43%) +void .. + + +operator new (282,249,956 samples, 5.24%) +operat.. + + +stewkk::sql::GetAST (91,907,772 samples, 1.71%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +std::_Sp_counted_ptr_inplace<antlr4::atn::ArrayPredictionContext, std::allocator<void>, (37,424,238 samples, 0.69%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +stewkk::sql::Executor<stewkk::sql::InterpretedExpressionExecutor>::SpawnExecutor (43,166,371 samples, 0.80%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +antlr4::atn::ParserATNSimulator::closure_ (8,320,762 samples, 0.15%) + + + +void boost::asio::detail::initiate_post_with_executor<boost::asio::any_io_executor>::operator (286,066,500 samples, 5.31%) +void b.. + + +stewkk::sql::DiskFileReader::Read (47,121,918 samples, 0.87%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (31,901,449 samples, 0.59%) + + + +malloc (12,442,960 samples, 0.23%) + + + +stewkk::sql::ReceiveTuples (24,434,585 samples, 0.45%) + + + +_int_malloc (11,953,581 samples, 0.22%) + + + +antlr4::atn::ParserATNSimulator::closure_ (31,901,449 samples, 0.59%) + + + +void boost::asio::experimental::detail::channel_service<boost::asio::detail::posix_mutex>::start_receive_op<boost::asio::experimental::channel_traits<>, void (469,204,932 samples, 8.71%) +void boost::.. + + +boost::asio::detail::scheduler::post_immediate_completion (12,016,681 samples, 0.22%) + + + +cfree@GLIBC_2.2.5 (45,110,211 samples, 0.84%) + + + +antlr4::atn::ParserATNSimulator::closureCheckingStopState (8,320,762 samples, 0.15%) + diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index 67f4264..f73eaf1 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -52,32 +52,82 @@ void BM_SQL(benchmark::State& state) { ctx.run(); } -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); -BENCHMARK(BM_SQL); -BENCHMARK(BM_SQL); +BENCHMARK(BM_SQL)->UseRealTime(); +BENCHMARK(BM_SQL)->UseRealTime(); + +template +void BM_SQL_Multithreaded(benchmark::State& state) { + std::ofstream nullstream("/dev/null"); + std::clog.rdbuf(nullstream.rdbuf()); + boost::asio::io_context ctx; + boost::asio::co_spawn( + ctx, + [&state]() -> boost::asio::awaitable { + boost::asio::thread_pool pool{4}; + std::stringstream s{Query}; + Operator op = GetAST(s).value(); + CsvDirSequentialScanner seq_scan{kProjectDir + "/test/static/executor/test_data"}; + Executor executor(std::move(seq_scan), + pool.executor()); + + // NOTE: precompile query + benchmark::DoNotOptimize(co_await executor.Execute(op)); + + for (auto _ : state) { + co_await boost::asio::co_spawn(pool, [&]() -> boost::asio::awaitable { + benchmark::DoNotOptimize(co_await executor.Execute(op)); + }, boost::asio::use_awaitable); + } + }(), + [](std::exception_ptr p) {}); + + ctx.run(); +} + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); + +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); +BENCHMARK(BM_SQL_Multithreaded)->UseRealTime(); } // namespace stewkk::sql diff --git a/benchmarks/results-02.json b/benchmarks/results-02.json new file mode 100644 index 0000000..d89cf9a --- /dev/null +++ b/benchmarks/results-02.json @@ -0,0 +1,490 @@ +{ + "context": { + "date": "2026-01-10T23:38:31+03:00", + "host_name": "nixos", + "executable": "./build-release/bin/benchmarks", + "num_cpus": 8, + "mhz_per_cpu": 4200, + "cpu_scaling_enabled": true, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 49152, + "num_sharing": 2 + }, + { + "type": "Instruction", + "level": 1, + "size": 32768, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 2, + "size": 1310720, + "num_sharing": 2 + }, + { + "type": "Unified", + "level": 3, + "size": 8388608, + "num_sharing": 8 + } + ], + "load_avg": [0.419922,0.788574,0.725586], + "library_version": "v1.9.0", + "library_build_type": "release", + "json_schema_version": 1 + }, + "benchmarks": [ + { + "name": "BM_SQL/real_time", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 39131, + "real_time": 1.7763904934746992e+04, + "cpu_time": 1.7715753596892489e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 1, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 38563, + "real_time": 1.8130024764634931e+04, + "cpu_time": 1.8088057438477295e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 2, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 12373, + "real_time": 5.7384221530711082e+04, + "cpu_time": 5.7042944718338294e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 3, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 12308, + "real_time": 5.6059255443637892e+04, + "cpu_time": 5.5761686545336357e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 4, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 10318, + "real_time": 6.7906289300143544e+04, + "cpu_time": 6.7580570653227391e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 5, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 10710, + "real_time": 6.5059496918799705e+04, + "cpu_time": 6.4733736414565828e+04, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 6, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 916, + "real_time": 6.8747337554748205e+05, + "cpu_time": 6.8549197270742385e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 7, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1368, + "real_time": 5.1371013011559722e+05, + "cpu_time": 5.0669114400584786e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 8, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1016, + "real_time": 6.8965960629676247e+05, + "cpu_time": 6.8773966338582651e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 9, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1376, + "real_time": 5.0798636846138834e+05, + "cpu_time": 5.0643320566860412e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 10, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 646, + "real_time": 1.0864569783272836e+06, + "cpu_time": 1.0833641873065040e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 11, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 796, + "real_time": 8.7479362060439051e+05, + "cpu_time": 8.7213186055276426e+05, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 12, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 378, + "real_time": 1.8729881534425891e+06, + "cpu_time": 1.8523415952380972e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 13, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 440, + "real_time": 1.6120009977302372e+06, + "cpu_time": 1.5915791840909088e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 14, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 206, + "real_time": 3.3982538106746450e+06, + "cpu_time": 3.3882851407766948e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 15, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 231, + "real_time": 3.0235657922004336e+06, + "cpu_time": 3.0163061558441538e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 16, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 108, + "real_time": 6.4594497962725991e+06, + "cpu_time": 6.4458291388888815e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL/real_time", + "family_index": 17, + "per_family_instance_index": 0, + "run_name": "BM_SQL/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 117, + "real_time": 5.9735239316131240e+06, + "cpu_time": 5.8960350598290535e+06, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 18, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 5314, + "real_time": 1.3457497459533918e+05, + "cpu_time": 6.7566829130599754e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 19, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 5168, + "real_time": 1.3406582294902750e+05, + "cpu_time": 7.0243527476779418e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 20, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 607, + "real_time": 1.1404998929171630e+06, + "cpu_time": 5.1917100494251117e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 21, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 706, + "real_time": 9.3259300000174274e+05, + "cpu_time": 4.6985750708203832e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 22, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 630, + "real_time": 1.1420491063507230e+06, + "cpu_time": 6.7041952380945313e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 23, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 756, + "real_time": 1.0241538029106023e+06, + "cpu_time": 7.8318994708982927e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 24, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 381, + "real_time": 1.8506752703352764e+06, + "cpu_time": 4.9503858267712976e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 25, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 406, + "real_time": 1.6802704630580470e+06, + "cpu_time": 5.1671182266014257e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 26, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 214, + "real_time": 3.3278060280315354e+06, + "cpu_time": 6.8044112149555713e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 27, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 215, + "real_time": 3.2163211860596528e+06, + "cpu_time": 6.8673441860502680e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 28, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 114, + "real_time": 6.1605299473612504e+06, + "cpu_time": 5.7971666666632946e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 29, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 113, + "real_time": 5.8682074159150943e+06, + "cpu_time": 5.4813097345176830e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 30, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 61, + "real_time": 1.1749900573734328e+07, + "cpu_time": 6.1474918032748528e+03, + "time_unit": "ns" + }, + { + "name": "BM_SQL_Multithreaded/real_time", + "family_index": 31, + "per_family_instance_index": 0, + "run_name": "BM_SQL_Multithreaded/real_time", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 61, + "real_time": 1.1562019360650590e+07, + "cpu_time": 7.6402295082020755e+03, + "time_unit": "ns" + } + ] +} diff --git a/flake.nix b/flake.nix index ea1cd58..d3566d8 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,12 @@ pythonEnv = pkgs.python313.withPackages (ps: [ ps.pip ps.virtualenv + ps.pygments ]); + tex = (pkgs.texlive.combine { + inherit (pkgs.texlive) scheme-full + latexmk; + }); in { devShells.default = pkgs.mkShell.override {stdenv = pkgs.llvmPackages_21.stdenv;} { buildInputs = with pkgs; [ @@ -32,8 +37,11 @@ zlib zlib.dev gdb - perf - perl + perf + perl + tex + plantuml + inkscape ]; nativeBuildInputs = [ diff --git a/include/stewkk/sql/logic/executor/buffer_size.hpp b/include/stewkk/sql/logic/executor/buffer_size.hpp index 438228b..cf4bb29 100644 --- a/include/stewkk/sql/logic/executor/buffer_size.hpp +++ b/include/stewkk/sql/logic/executor/buffer_size.hpp @@ -4,7 +4,7 @@ namespace stewkk::sql { -constexpr static std::size_t kBufSize = 10; +constexpr static std::size_t kBufSize = 2048; } // stewkk::sql diff --git a/report/Emblem.png b/report/Emblem.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d065d5fb7eaa63b78f66fba9eec0911a13cb74 GIT binary patch literal 176274 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
yWh)6^v|l~Ph~+znY)V*$G`8uuwPKe5?QRj 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

c7psr9hpFdjPy=lxKT=?DD=0Ng;_1Uwt@g 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