From 4cc33334447d5de89e3fcf9fe3afc976a35e86e2 Mon Sep 17 00:00:00 2001 From: Roland Kuehn Date: Sun, 14 Apr 2024 16:35:04 +0200 Subject: [PATCH] Setting up BeeDB for summer semester 2024 --- .clang-format | 127 + .gitignore | 12 + .pre-commit-hook | 29 + CMakeLists.txt | 128 + LICENSE | 21 + README.md | 114 + beedb.ini | 15 + include/boot/execution_callback.h | 131 + include/buffer/clock_strategy.h | 44 + include/buffer/frame.h | 184 + include/buffer/lfu_strategy.h | 42 + include/buffer/lru_k_strategy.h | 54 + include/buffer/lru_strategy.h | 42 + include/buffer/manager.h | 140 + include/buffer/random_strategy.h | 44 + include/buffer/replacement_strategy.h | 58 + include/compression/wah_bit_vector.h | 98 + include/concurrency/metadata.h | 172 + include/concurrency/timestamp.h | 116 + include/concurrency/transaction.h | 367 + include/concurrency/transaction_callback.h | 98 + include/concurrency/transaction_manager.h | 170 + include/config.h | 196 + include/database.h | 203 + include/exception/command_exception.h | 88 + include/exception/concurrency_exception.h | 47 + include/exception/config_exception.h | 59 + include/exception/disk_exception.h | 80 + include/exception/exception.h | 64 + include/exception/execution_exception.h | 58 + include/exception/logical_exception.h | 215 + include/exception/parser_exception.h | 90 + include/execution/add_to_index_operator.h | 60 + include/execution/aggregate_operator.h | 145 + include/execution/aggregator.h | 222 + include/execution/arithmetic_calculator.h | 211 + include/execution/arithmetic_operator.h | 54 + include/execution/binary_operator.h | 69 + include/execution/build_index_operator.h | 77 + include/execution/create_index_operator.h | 78 + include/execution/create_table_operator.h | 65 + include/execution/cross_product_operator.h | 58 + include/execution/delete_operator.h | 64 + include/execution/hash_join_operator.h | 109 + include/execution/index_scan_operator.h | 141 + include/execution/insert_operator.h | 69 + include/execution/limit_operator.h | 60 + .../execution/nested_loops_join_operator.h | 67 + include/execution/operator_interface.h | 63 + include/execution/order_operator.h | 111 + include/execution/predicate_matcher.h | 299 + include/execution/projection_operator.h | 56 + include/execution/selection_operator.h | 68 + include/execution/sequential_scan_operator.h | 68 + include/execution/transaction_operator.h | 125 + include/execution/tuple_buffer.h | 69 + include/execution/tuple_buffer_operator.h | 70 + include/execution/unary_operator.h | 54 + include/execution/update_operator.h | 66 + include/expression/attribute.h | 130 + include/expression/operation.h | 384 + include/expression/term.h | 241 + include/index/b_plus_tree/b_plus_tree.h | 287 + include/index/b_plus_tree/b_plus_tree.hpp | 206 + include/index/b_plus_tree/b_plus_tree_node.h | 294 + .../non_unique_b_plus_tree_index.h | 74 + .../b_plus_tree/unique_b_plus_tree_index.h | 73 + include/index/index_factory.h | 60 + include/index/index_interface.h | 70 + include/index/non_unique_index_interface.h | 49 + include/index/range_index_interface.h | 52 + include/index/return_value.h | 38 + include/index/type.h | 39 + include/index/unique_index_interface.h | 48 + include/io/client_console.h | 48 + include/io/client_handler.h | 77 + include/io/client_message_serializer.h | 72 + include/io/command/commander.h | 56 + include/io/command/custom_command_interface.h | 40 + include/io/command/custom_commands.h | 142 + include/io/execution_callback.h | 104 + include/io/executor.h | 157 + include/io/file_executor.h | 50 + include/io/query_result_serializer.h | 77 + include/io/result_output_formatter.h | 109 + include/io/server_response.h | 198 + include/network/client.h | 56 + include/network/server.h | 90 + include/parser/.gitignore | 2 + include/parser/driver.h | 63 + include/parser/node.h | 291 + include/parser/scanner.hpp | 54 + include/parser/sql_parser.h | 42 + include/plan/graph/basic_graph.h | 297 + include/plan/logical/builder.h | 58 + include/plan/logical/node/aggregation_node.h | 82 + include/plan/logical/node/arithmetic_node.h | 69 + include/plan/logical/node/create_index_node.h | 79 + include/plan/logical/node/create_table_node.h | 73 + .../plan/logical/node/cross_product_node.h | 59 + include/plan/logical/node/delete_node.h | 63 + include/plan/logical/node/insert_node.h | 92 + include/plan/logical/node/join_node.h | 120 + include/plan/logical/node/limit_node.h | 72 + include/plan/logical/node/node_interface.h | 196 + include/plan/logical/node/order_by_node.h | 68 + include/plan/logical/node/projection_node.h | 99 + include/plan/logical/node/scan_node.h | 151 + include/plan/logical/node/schema.h | 30 + include/plan/logical/node/selection_node.h | 75 + include/plan/logical/node/table.h | 137 + include/plan/logical/node/transaction_node.h | 55 + include/plan/logical/node/update_node.h | 77 + include/plan/logical/plan_view.h | 89 + include/plan/optimizer/optimizer.h | 76 + .../rule/cross_product_optimization_rule.h | 40 + .../rule/hash_join_optimization_rule.h | 42 + .../rule/index_scan_optimization_rule.h | 49 + .../rule/merge_selection_optimization_rule.h | 37 + .../optimizer/rule/optimizer_rule_interface.h | 37 + .../predicate_push_down_optimization_rule.h | 68 + .../remove_projection_optimization_rule.h | 37 + .../rule/swap_operands_optimization_rule.h | 46 + include/plan/physical/builder.h | 161 + include/plan/physical/plan.h | 60 + include/statistic/system_statistics.h | 99 + include/storage/manager.h | 85 + include/storage/metadata_page.h | 47 + include/storage/page.h | 167 + include/storage/record_identifier.h | 91 + include/storage/record_page.h | 149 + include/table/column.h | 214 + include/table/date.h | 138 + include/table/memory_table.h | 189 + include/table/schema.h | 339 + include/table/table.h | 182 + include/table/table_disk_manager.h | 127 + include/table/tuple.h | 420 + include/table/type.h | 159 + include/table/value.h | 595 + include/util/clock.h | 56 + include/util/command_line_interface.h | 61 + include/util/ini_parser.h | 171 + include/util/mpmc_queue.h | 148 + include/util/optional.h | 228 + include/util/perf.h | 192 + include/util/quicksort.h | 91 + include/util/random_generator.h | 59 + include/util/text_table.h | 115 + lib/argparse/argparse.hpp | 539 + lib/linenoise/LICENSE | 25 + lib/linenoise/README.markdown | 247 + lib/linenoise/linenoise.c | 1225 + lib/linenoise/linenoise.h | 77 + lib/nlohmann/json.hpp | 24867 ++++++++++++++++ logo.png | Bin 0 -> 189113 bytes src/beedb_client.cpp | 47 + src/beedb_server.cpp | 238 + src/boot/execution_callback.cpp | 89 + src/buffer/clock_strategy.cpp | 58 + src/buffer/lfu_strategy.cpp | 71 + src/buffer/lru_k_strategy.cpp | 78 + src/buffer/lru_strategy.cpp | 74 + src/buffer/manager.cpp | 136 + src/buffer/random_strategy.cpp | 49 + src/concurrency/transaction_manager.cpp | 286 + src/concurrency/transaction_visibility.cpp | 79 + src/database.cpp | 325 + src/execution/add_to_index_operator.cpp | 64 + src/execution/aggregate_operator.cpp | 191 + src/execution/arithmetic_operator.cpp | 72 + src/execution/binary_operator.cpp | 36 + src/execution/build_index_operator.cpp | 86 + src/execution/create_index_operator.cpp | 50 + src/execution/create_table_operator.cpp | 37 + src/execution/cross_product_operator.cpp | 66 + src/execution/delete_operator.cpp | 69 + src/execution/hash_join_operator.cpp | 149 + src/execution/index_scan_operator.cpp | 161 + src/execution/insert_operator.cpp | 67 + src/execution/limit_operator.cpp | 82 + src/execution/nested_loops_join_operator.cpp | 120 + src/execution/order_operator.cpp | 72 + src/execution/projection_operator.cpp | 51 + src/execution/selection_operator.cpp | 56 + src/execution/sequential_scan_operator.cpp | 120 + src/execution/transaction_operator.cpp | 75 + src/execution/tuple_buffer_operator.cpp | 41 + src/execution/update_operator.cpp | 79 + src/expression/operation.cpp | 127 + src/io/client_console.cpp | 145 + src/io/client_handler.cpp | 124 + src/io/command/commander.cpp | 62 + src/io/command/custom_commands.cpp | 207 + src/io/executor.cpp | 152 + src/io/file_executor.cpp | 203 + src/io/query_result_serializer.cpp | 154 + src/io/result_output_formatter.cpp | 144 + src/network/client.cpp | 111 + src/network/server.cpp | 170 + src/parser/.gitignore | 5 + src/parser/driver.cpp | 35 + src/parser/parser.yy | 375 + src/parser/scanner.ll | 93 + src/parser/sql_parser.cpp | 34 + src/plan/logical/builder.cpp | 445 + src/plan/logical/plan_view.cpp | 176 + src/plan/optimizer/optimizer.cpp | 154 + .../rule/cross_product_optimization_rule.cpp | 102 + .../rule/hash_join_optimization_rule.cpp | 50 + .../rule/index_scan_optimization_rule.cpp | 109 + .../merge_selection_optimization_rule.cpp | 53 + .../predicate_push_down_optimization_rule.cpp | 152 + .../remove_projection_optimization_rule.cpp | 49 + .../rule/swap_operands_optimization_rule.cpp | 148 + src/plan/physical/builder.cpp | 1219 + src/plan/physical/plan.cpp | 66 + src/storage/manager.cpp | 121 + src/table/column.cpp | 31 + src/table/table.cpp | 40 + src/table/table_disk_manager.cpp | 211 + src/table/value.cpp | 31 + src/util/ini_parser.cpp | 68 + src/util/perf.cpp | 93 + src/util/random_generator.cpp | 63 + src/util/text_table.cpp | 130 + 226 files changed, 52399 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100755 .pre-commit-hook create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 beedb.ini create mode 100644 include/boot/execution_callback.h create mode 100644 include/buffer/clock_strategy.h create mode 100644 include/buffer/frame.h create mode 100644 include/buffer/lfu_strategy.h create mode 100644 include/buffer/lru_k_strategy.h create mode 100644 include/buffer/lru_strategy.h create mode 100644 include/buffer/manager.h create mode 100644 include/buffer/random_strategy.h create mode 100644 include/buffer/replacement_strategy.h create mode 100644 include/compression/wah_bit_vector.h create mode 100644 include/concurrency/metadata.h create mode 100644 include/concurrency/timestamp.h create mode 100644 include/concurrency/transaction.h create mode 100644 include/concurrency/transaction_callback.h create mode 100644 include/concurrency/transaction_manager.h create mode 100644 include/config.h create mode 100644 include/database.h create mode 100644 include/exception/command_exception.h create mode 100644 include/exception/concurrency_exception.h create mode 100644 include/exception/config_exception.h create mode 100644 include/exception/disk_exception.h create mode 100644 include/exception/exception.h create mode 100644 include/exception/execution_exception.h create mode 100644 include/exception/logical_exception.h create mode 100644 include/exception/parser_exception.h create mode 100644 include/execution/add_to_index_operator.h create mode 100644 include/execution/aggregate_operator.h create mode 100644 include/execution/aggregator.h create mode 100644 include/execution/arithmetic_calculator.h create mode 100644 include/execution/arithmetic_operator.h create mode 100644 include/execution/binary_operator.h create mode 100644 include/execution/build_index_operator.h create mode 100644 include/execution/create_index_operator.h create mode 100644 include/execution/create_table_operator.h create mode 100644 include/execution/cross_product_operator.h create mode 100644 include/execution/delete_operator.h create mode 100644 include/execution/hash_join_operator.h create mode 100644 include/execution/index_scan_operator.h create mode 100644 include/execution/insert_operator.h create mode 100644 include/execution/limit_operator.h create mode 100644 include/execution/nested_loops_join_operator.h create mode 100644 include/execution/operator_interface.h create mode 100644 include/execution/order_operator.h create mode 100644 include/execution/predicate_matcher.h create mode 100644 include/execution/projection_operator.h create mode 100644 include/execution/selection_operator.h create mode 100644 include/execution/sequential_scan_operator.h create mode 100644 include/execution/transaction_operator.h create mode 100644 include/execution/tuple_buffer.h create mode 100644 include/execution/tuple_buffer_operator.h create mode 100644 include/execution/unary_operator.h create mode 100644 include/execution/update_operator.h create mode 100644 include/expression/attribute.h create mode 100644 include/expression/operation.h create mode 100644 include/expression/term.h create mode 100644 include/index/b_plus_tree/b_plus_tree.h create mode 100644 include/index/b_plus_tree/b_plus_tree.hpp create mode 100644 include/index/b_plus_tree/b_plus_tree_node.h create mode 100644 include/index/b_plus_tree/non_unique_b_plus_tree_index.h create mode 100644 include/index/b_plus_tree/unique_b_plus_tree_index.h create mode 100644 include/index/index_factory.h create mode 100644 include/index/index_interface.h create mode 100644 include/index/non_unique_index_interface.h create mode 100644 include/index/range_index_interface.h create mode 100644 include/index/return_value.h create mode 100644 include/index/type.h create mode 100644 include/index/unique_index_interface.h create mode 100644 include/io/client_console.h create mode 100644 include/io/client_handler.h create mode 100644 include/io/client_message_serializer.h create mode 100644 include/io/command/commander.h create mode 100644 include/io/command/custom_command_interface.h create mode 100644 include/io/command/custom_commands.h create mode 100644 include/io/execution_callback.h create mode 100644 include/io/executor.h create mode 100644 include/io/file_executor.h create mode 100644 include/io/query_result_serializer.h create mode 100644 include/io/result_output_formatter.h create mode 100644 include/io/server_response.h create mode 100644 include/network/client.h create mode 100644 include/network/server.h create mode 100644 include/parser/.gitignore create mode 100644 include/parser/driver.h create mode 100644 include/parser/node.h create mode 100644 include/parser/scanner.hpp create mode 100644 include/parser/sql_parser.h create mode 100644 include/plan/graph/basic_graph.h create mode 100644 include/plan/logical/builder.h create mode 100644 include/plan/logical/node/aggregation_node.h create mode 100644 include/plan/logical/node/arithmetic_node.h create mode 100644 include/plan/logical/node/create_index_node.h create mode 100644 include/plan/logical/node/create_table_node.h create mode 100644 include/plan/logical/node/cross_product_node.h create mode 100644 include/plan/logical/node/delete_node.h create mode 100644 include/plan/logical/node/insert_node.h create mode 100644 include/plan/logical/node/join_node.h create mode 100644 include/plan/logical/node/limit_node.h create mode 100644 include/plan/logical/node/node_interface.h create mode 100644 include/plan/logical/node/order_by_node.h create mode 100644 include/plan/logical/node/projection_node.h create mode 100644 include/plan/logical/node/scan_node.h create mode 100644 include/plan/logical/node/schema.h create mode 100644 include/plan/logical/node/selection_node.h create mode 100644 include/plan/logical/node/table.h create mode 100644 include/plan/logical/node/transaction_node.h create mode 100644 include/plan/logical/node/update_node.h create mode 100644 include/plan/logical/plan_view.h create mode 100644 include/plan/optimizer/optimizer.h create mode 100644 include/plan/optimizer/rule/cross_product_optimization_rule.h create mode 100644 include/plan/optimizer/rule/hash_join_optimization_rule.h create mode 100644 include/plan/optimizer/rule/index_scan_optimization_rule.h create mode 100644 include/plan/optimizer/rule/merge_selection_optimization_rule.h create mode 100644 include/plan/optimizer/rule/optimizer_rule_interface.h create mode 100644 include/plan/optimizer/rule/predicate_push_down_optimization_rule.h create mode 100644 include/plan/optimizer/rule/remove_projection_optimization_rule.h create mode 100644 include/plan/optimizer/rule/swap_operands_optimization_rule.h create mode 100644 include/plan/physical/builder.h create mode 100644 include/plan/physical/plan.h create mode 100644 include/statistic/system_statistics.h create mode 100644 include/storage/manager.h create mode 100644 include/storage/metadata_page.h create mode 100644 include/storage/page.h create mode 100644 include/storage/record_identifier.h create mode 100644 include/storage/record_page.h create mode 100644 include/table/column.h create mode 100644 include/table/date.h create mode 100644 include/table/memory_table.h create mode 100644 include/table/schema.h create mode 100644 include/table/table.h create mode 100644 include/table/table_disk_manager.h create mode 100644 include/table/tuple.h create mode 100644 include/table/type.h create mode 100644 include/table/value.h create mode 100644 include/util/clock.h create mode 100644 include/util/command_line_interface.h create mode 100644 include/util/ini_parser.h create mode 100644 include/util/mpmc_queue.h create mode 100644 include/util/optional.h create mode 100644 include/util/perf.h create mode 100644 include/util/quicksort.h create mode 100644 include/util/random_generator.h create mode 100644 include/util/text_table.h create mode 100644 lib/argparse/argparse.hpp create mode 100644 lib/linenoise/LICENSE create mode 100644 lib/linenoise/README.markdown create mode 100644 lib/linenoise/linenoise.c create mode 100644 lib/linenoise/linenoise.h create mode 100644 lib/nlohmann/json.hpp create mode 100644 logo.png create mode 100644 src/beedb_client.cpp create mode 100644 src/beedb_server.cpp create mode 100644 src/boot/execution_callback.cpp create mode 100644 src/buffer/clock_strategy.cpp create mode 100644 src/buffer/lfu_strategy.cpp create mode 100644 src/buffer/lru_k_strategy.cpp create mode 100644 src/buffer/lru_strategy.cpp create mode 100644 src/buffer/manager.cpp create mode 100644 src/buffer/random_strategy.cpp create mode 100644 src/concurrency/transaction_manager.cpp create mode 100644 src/concurrency/transaction_visibility.cpp create mode 100644 src/database.cpp create mode 100644 src/execution/add_to_index_operator.cpp create mode 100644 src/execution/aggregate_operator.cpp create mode 100644 src/execution/arithmetic_operator.cpp create mode 100644 src/execution/binary_operator.cpp create mode 100644 src/execution/build_index_operator.cpp create mode 100644 src/execution/create_index_operator.cpp create mode 100644 src/execution/create_table_operator.cpp create mode 100644 src/execution/cross_product_operator.cpp create mode 100644 src/execution/delete_operator.cpp create mode 100644 src/execution/hash_join_operator.cpp create mode 100644 src/execution/index_scan_operator.cpp create mode 100644 src/execution/insert_operator.cpp create mode 100644 src/execution/limit_operator.cpp create mode 100644 src/execution/nested_loops_join_operator.cpp create mode 100644 src/execution/order_operator.cpp create mode 100644 src/execution/projection_operator.cpp create mode 100644 src/execution/selection_operator.cpp create mode 100644 src/execution/sequential_scan_operator.cpp create mode 100644 src/execution/transaction_operator.cpp create mode 100644 src/execution/tuple_buffer_operator.cpp create mode 100644 src/execution/update_operator.cpp create mode 100644 src/expression/operation.cpp create mode 100644 src/io/client_console.cpp create mode 100644 src/io/client_handler.cpp create mode 100644 src/io/command/commander.cpp create mode 100644 src/io/command/custom_commands.cpp create mode 100644 src/io/executor.cpp create mode 100644 src/io/file_executor.cpp create mode 100644 src/io/query_result_serializer.cpp create mode 100644 src/io/result_output_formatter.cpp create mode 100644 src/network/client.cpp create mode 100644 src/network/server.cpp create mode 100644 src/parser/.gitignore create mode 100644 src/parser/driver.cpp create mode 100644 src/parser/parser.yy create mode 100644 src/parser/scanner.ll create mode 100644 src/parser/sql_parser.cpp create mode 100644 src/plan/logical/builder.cpp create mode 100644 src/plan/logical/plan_view.cpp create mode 100644 src/plan/optimizer/optimizer.cpp create mode 100644 src/plan/optimizer/rule/cross_product_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/hash_join_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/index_scan_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/merge_selection_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/predicate_push_down_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/remove_projection_optimization_rule.cpp create mode 100644 src/plan/optimizer/rule/swap_operands_optimization_rule.cpp create mode 100644 src/plan/physical/builder.cpp create mode 100644 src/plan/physical/plan.cpp create mode 100644 src/storage/manager.cpp create mode 100644 src/table/column.cpp create mode 100644 src/table/table.cpp create mode 100644 src/table/table_disk_manager.cpp create mode 100644 src/table/value.cpp create mode 100644 src/util/ini_parser.cpp create mode 100644 src/util/perf.cpp create mode 100644 src/util/random_generator.cpp create mode 100644 src/util/text_table.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..428d393 --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: Never +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4e2cad --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.o +build +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles/ +Makefile +bee.db +cmake_install.cmake +lib/include/ +lib/libsqlparser.so +patch/* +**/.DS_Store diff --git a/.pre-commit-hook b/.pre-commit-hook new file mode 100755 index 0000000..ab40424 --- /dev/null +++ b/.pre-commit-hook @@ -0,0 +1,29 @@ +#!/bin/bash + +STYLE=$(git config --get hooks.clangformat.style) +if [ -n "${STYLE}" ] ; then + STYLEARG="-style=${STYLE}" +else + STYLEARG="" +fi + +format_file() { + file="${1}" + if [ -f $file ]; then + if [ "${file##*.}" = "cpp" ] || [ "${file##*.}" = "h" ] || [ "${file##*.}" = "hpp" ]; then + clang-format -i ${STYLEARG} ${1} + git add ${1} + fi + fi +} + +case "${1}" in + --about ) + echo "Runs clang-format on source files" + ;; + * ) + for file in `git diff-index --cached --name-only HEAD` ; do + format_file "${file}" + done + ;; +esac diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4f67586 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,128 @@ +include(ExternalProject) +cmake_minimum_required(VERSION 3.9) + +project(BeeDB) + +## Set default settings +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_FLAGS "-pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Wno-unused") +set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") +set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -DNDEBUG") +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE DEBUG) # "Debug" is default build type, if nothing is defined +endif() + +message( "Setting up '" ${CMAKE_BUILD_TYPE} "' build.") + +## External dependencies +find_package(BISON REQUIRED) +find_package(FLEX REQUIRED) + +## Include and link directories +include_directories(${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/lib/ ${PROJECT_SOURCE_DIR}/lib/linenoise ${PROJECT_SOURCE_DIR}/include/parser) +link_directories(${PROJECT_SOURCE_DIR}/lib) + +## Add Parser targets +BISON_TARGET(parser + src/parser/parser.yy + src/parser/parser.cpp + DEFINES_FILE include/parser/parser.hpp + VERBOSE + COMPILE_FLAGS "--defines --language=C++" +) + +FLEX_TARGET(lexer + src/parser/scanner.ll + src/parser/scanner.cpp + COMPILE_FLAGS "--c+ -i" +) +ADD_FLEX_BISON_DEPENDENCY(lexer parser) + +## BeeDB Server Sources +add_executable(beedb + lib/linenoise/linenoise.c + ${FLEX_lexer_OUTPUTS} + ${BISON_parser_OUTPUTS} + + src/beedb_server.cpp + src/database.cpp + src/concurrency/transaction_manager.cpp + src/concurrency/transaction_visibility.cpp + src/storage/manager.cpp + src/buffer/manager.cpp + src/buffer/random_strategy.cpp + src/buffer/lru_strategy.cpp + src/buffer/lru_k_strategy.cpp + src/buffer/lfu_strategy.cpp + src/buffer/clock_strategy.cpp + src/table/table.cpp + src/table/column.cpp + src/table/value.cpp + src/table/table_disk_manager.cpp + src/parser/driver.cpp + src/parser/sql_parser.cpp + src/execution/binary_operator.cpp + src/execution/sequential_scan_operator.cpp + src/execution/index_scan_operator.cpp + src/execution/create_table_operator.cpp + src/execution/create_index_operator.cpp + src/execution/insert_operator.cpp + src/execution/selection_operator.cpp + src/execution/projection_operator.cpp + src/execution/nested_loops_join_operator.cpp + src/execution/hash_join_operator.cpp + src/execution/limit_operator.cpp + src/execution/build_index_operator.cpp + src/execution/order_operator.cpp + src/execution/aggregate_operator.cpp + src/execution/cross_product_operator.cpp + src/execution/tuple_buffer_operator.cpp + src/execution/add_to_index_operator.cpp + src/execution/update_operator.cpp + src/execution/delete_operator.cpp + src/execution/transaction_operator.cpp + src/execution/arithmetic_operator.cpp + src/expression/operation.cpp + src/plan/physical/plan.cpp + src/plan/logical/builder.cpp + src/plan/logical/plan_view.cpp + src/plan/physical/builder.cpp + src/io/executor.cpp + src/io/file_executor.cpp + src/io/result_output_formatter.cpp + src/io/command/commander.cpp + src/io/command/custom_commands.cpp + src/io/client_handler.cpp + src/io/client_console.cpp + src/io/query_result_serializer.cpp + src/util/text_table.cpp + src/util/ini_parser.cpp + src/util/random_generator.cpp + src/plan/optimizer/optimizer.cpp + src/plan/optimizer/rule/index_scan_optimization_rule.cpp + src/plan/optimizer/rule/hash_join_optimization_rule.cpp + src/plan/optimizer/rule/swap_operands_optimization_rule.cpp + src/plan/optimizer/rule/predicate_push_down_optimization_rule.cpp + src/plan/optimizer/rule/remove_projection_optimization_rule.cpp + src/plan/optimizer/rule/cross_product_optimization_rule.cpp + src/plan/optimizer/rule/merge_selection_optimization_rule.cpp + src/network/server.cpp + src/network/client.cpp + src/boot/execution_callback.cpp +) + +add_executable(beedb_client + lib/linenoise/linenoise.c + + src/beedb_client.cpp + src/io/client_console.cpp + src/io/result_output_formatter.cpp + src/util/text_table.cpp + src/network/client.cpp +) + +## Build target +target_link_libraries(beedb pthread) + +## Git install hook target +add_custom_target(git-hook cp ${PROJECT_SOURCE_DIR}/.pre-commit-hook ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit && chmod +x ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2101e25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 TheScriptbot + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5089d9 --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +![BeeDB](logo.png) + +Welcome to **BeeDB**, the educational DBMS tailored for the course **Architecture and Implementation of DBMS**. +BeeDB bridges theory and practice, fostering a deep understanding of DBMS structures and operations. +Dive into the world of databases with BeeDB, your gateway to mastering database management. + +**Architecture and Implementation of DBMS** is usually taught every summer term, see [dbis page of TU Dortmund](http://dbis.cs.tu-dortmund.de/cms/en/teaching/index.html) for more information. + +## Dependencies +* `git` +* `cmake` (at least version `3.9`) +* `build-essential` +* `bison` and `flex` (and in Ubuntu the package `libfl-dev`) + +## How to build +### Option (a): Build into current folder: + * `cmake .` + * `make` (or `make -j` for parallel compilation) + +### Option (b): Build into separate `build` folder: + * `mkdir build && cd build` + * `cmake ..` + * `make` (or `make -j` for parallel compilation) + +### Switch between `Release` and `Debug` modes +* Default build is in `Debug` mode. +* If you want to build in `Release` mode use: + * `cmake . -DCMAKE_BUILD_TYPE=Release` + * **or** set `CMAKE_BUILD_TYPE` in `CMakeLists.txt`. + +## How to use +BeeDB uses a client/server model where the executable `beedb` starts the server and `beedb_client` runs a client. + +### Server + Usage: beedb [options] db-file + + Positional arguments: + db-file File the database is stored in. Default: bee.db + + Optional arguments: + -h --help show this help message and exit + -p --port Port of the server + -l --load Load SQL file into database. + -q --query Execute Query. + -cmd --custom_command Execute custom command and exit right after. + -k --keep Keep server running after executing query, command or loading a file. + -c --client Start an additional client next to the server + --buffer-manager-frames Number of frames within the frame buffer. + --scan-page-limit Number of pages the SCAN operator can pin at a time. + --enable-index-scan Enable index scan and use whenever possible. + --enable-hash-join Enable hash join and use whenever possible. + --enable-predicate-push-down Enable predicate push down and use whenever possible. + --stats Print all execution statistics + + +### Client + Usage: beedb_client [options] host + + Positional arguments: + host Name or IP of the beedb server + + Optional arguments: + -h --help show this help message and exit + -p --port Port of the server + +### Please note! +Just stopping the server by killing (or `Ctrl-C`) crashes the server; you may lose (unflushed) data. +To stop the server clean, use `:stop` command by a client. + +## Configuration +Some configuration outside the console arguments is stored in the file `beedb.ini`. +* The number of pages stored as frames in the buffer manager (`buffer manager.frames`) +* The replacement strategy of frames in the buffer manager (`buffer manager.strategy`) +* The `k` parameter for `LRU-K` replacement strategy (`buffer manager.k`) +* The number of how many pages can be pinned by a scan at a time (`scan.page-limit`) +* Enable or disable usage of index scan (`optimizer.enable-index-scan`) +* Enable or disable usage of hash join (`optimizer.enable-hash-join`) +* Enable or disable predicate push down (`optimizer.enable-predicate-push-down`) + +## Non-SQL Commands +Despite SQL commands, you can use the following special commands from the client. +* `:explain `: prints the query plan, either as a table or a graph (a list of nodes and edges) +* `:get `: prints either all or the secified option of the database configuration +* `:set `: changes the specified option. Only numerical values are valid +* `:show [tables,indices,columns]`: A quick way to show available tables, their columns or indices +* `:stop`: Stops the server (and flushes all data to the disk). + +## Examples + +##### Import and SQL file (containing `CREATE` and `INSERT`) +`./beedb -l movies.sql` + +##### Run a single query and terminate +`./beedb -q "SELECT * FROM movie;"` + +##### Run a query and open console afterwards +`./beedb -q "SELECT * FROM movie;" -c` + +##### Start the BeeDB server only (connect clients later) +`./beedb` + +##### Start the BeeDB server and open a client console +`./beedb -c` + +#### Start a BeeDB client (you can start multiple ones) +`./beedb_client` + +# For developers +* If you want to commit to the repository please `make git-hook` before commit. + +# Credits +* Thanks to **p-ranav** for `argparse` (MIT license, [See on GitHub](https://github.com/p-ranav/argparse)). +* Thanks to **antirez** for `linenoise` (BSD license, [See on Github](https://github.com/antirez/linenoise)). +* Thanks to **nlohmann** and further contributors for `nlohmann_json` (MIT license, [See on GitHub](https://github.com/nlohmann/json)). \ No newline at end of file diff --git a/beedb.ini b/beedb.ini new file mode 100644 index 0000000..f69f080 --- /dev/null +++ b/beedb.ini @@ -0,0 +1,15 @@ +[buffer manager] +frames = 256 +strategy = Random ; Random | LRU-K | LFU | LRU | CLOCK +k = 2 ; LRU-K parameter + +[scan] +page-limit = 64 + +[optimizer] +enable-index-scan = 0 ; 1 for enable index scan +enable-hash-join = 0 ; 1 for enable hash join +enable-predicate-push-down = 0 ; 1 for enabling predicate push down + +[executor] +print-statistics = 0 ; 1 for printing all execution statistics diff --git a/include/boot/execution_callback.h b/include/boot/execution_callback.h new file mode 100644 index 0000000..599d476 --- /dev/null +++ b/include/boot/execution_callback.h @@ -0,0 +1,131 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include + +namespace beedb::boot +{ +class TableExecutionCallback final : public io::ExecutionCallback +{ + public: + TableExecutionCallback(Database &database, concurrency::Transaction *transaction, + std::atomic_uint32_t &next_table_id, std::atomic_uint32_t &next_column_id, + std::atomic_uint32_t &next_index_id, std::unordered_map &tables) + : _database(database), _transaction(transaction), _next_table_id(next_table_id), + _next_column_id(next_column_id), _next_index_id(next_index_id), _tables(tables) + { + } + + void on_schema(const table::Schema &) override + { + } + void on_tuple(const table::Tuple &tuple) override; + + void on_plan(const std::unique_ptr &) override + { + } + + private: + Database &_database; + concurrency::Transaction *_transaction; + std::atomic_uint32_t &_next_table_id; + std::atomic_uint32_t &_next_column_id; + std::atomic_uint32_t &_next_index_id; + std::unordered_map &_tables; +}; + +class ColumnExecutionCallback final : public io::ExecutionCallback +{ + public: + ColumnExecutionCallback(Database &database, concurrency::Transaction *transaction, + std::atomic_uint32_t &next_column_id, std::atomic_uint32_t &next_index_id, + table::Schema &schema) + : _database(database), _transaction(transaction), _next_column_id(next_column_id), + _next_index_id(next_index_id), _schema(schema) + { + } + ~ColumnExecutionCallback() override = default; + + void on_schema(const table::Schema &) override + { + } + void on_tuple(const table::Tuple &tuple) override; + + void on_plan(const std::unique_ptr &) override + { + } + + private: + Database &_database; + concurrency::Transaction *_transaction; + std::atomic_uint32_t &_next_column_id; + std::atomic_uint32_t &_next_index_id; + table::Schema &_schema; +}; + +class IndexExecutionCallback final : public io::ExecutionCallback +{ + public: + IndexExecutionCallback(std::atomic_uint32_t &next_index_id, + std::vector> &indices) + : _next_index_id(next_index_id), _indices(indices) + { + } + ~IndexExecutionCallback() override = default; + + void on_schema(const table::Schema &) override + { + } + void on_tuple(const table::Tuple &tuple) override; + + void on_plan(const std::unique_ptr &) override + { + } + + private: + std::atomic_uint32_t &_next_index_id; + std::vector> &_indices; +}; + +class StatisticExecutionCallback final : public io::ExecutionCallback +{ + public: + explicit StatisticExecutionCallback(statistic::SystemStatistics &system_statistics) + : _system_statistics(system_statistics) + { + } + ~StatisticExecutionCallback() override = default; + + void on_schema(const table::Schema &) override + { + } + void on_tuple(const table::Tuple &tuple) override; + + void on_plan(const std::unique_ptr &) override + { + } + + private: + statistic::SystemStatistics &_system_statistics; +}; +} // namespace beedb::boot \ No newline at end of file diff --git a/include/buffer/clock_strategy.h b/include/buffer/clock_strategy.h new file mode 100644 index 0000000..5598955 --- /dev/null +++ b/include/buffer/clock_strategy.h @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "replacement_strategy.h" + +namespace beedb::buffer +{ +class ClockStrategy final : public ReplacementStrategy +{ + public: + ClockStrategy(std::size_t count_frames); + ~ClockStrategy() override = default; + + std::size_t find_victim(std::vector &pages) override; + + void on_pin(std::size_t frame_index, std::size_t timestamp) override; + + private: + const std::size_t _pool_size; + std::size_t _current_frame = 0u; + std::vector _last_chance_bits; +}; +} // namespace beedb::buffer \ No newline at end of file diff --git a/include/buffer/frame.h b/include/buffer/frame.h new file mode 100644 index 0000000..2f2fd9a --- /dev/null +++ b/include/buffer/frame.h @@ -0,0 +1,184 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include + +namespace beedb::buffer +{ +/** + * Storage for a page that is loaded from disk to memory. + */ +using Frame = std::array; + +/** + * Stores information about a frame that holds a page + * in memory. + * Information are: + * - The id of the current page hold by the frame + * - Number of pins + * - Dirty bit; if set, the page has to be written back + * to disk when the page is replaced. + * - History of pin timestamps + */ +class FrameInformation +{ + public: + FrameInformation() = default; + ~FrameInformation() = default; + + void occupy(const storage::Page::id_t page_id, const std::size_t timestamp) + { + this->_page_id = page_id; + this->_pin_count = 1u; + this->_is_dirty = false; + this->_is_last_chance = false; + this->_pin_timestamps.clear(); + this->_pin_timestamps.push_back(timestamp); + } + + /** + * @return Id of the occupied page. + */ + [[nodiscard]] storage::Page::id_t page_id() const + { + return _page_id; + } + + /** + * @return True, if the frame is occupied by a page. + */ + [[nodiscard]] bool is_occupied() const + { + return _page_id != storage::Page::INVALID_PAGE_ID; + } + + /** + * @return Number of active pins. + */ + [[nodiscard]] std::size_t pin_count() const + { + return _pin_count; + } + + /** + * @return True, if the frame is pinned at the moment. + */ + [[nodiscard]] bool is_pinned() const + { + return _pin_count > 0u; + } + + /** + * Increases the pin count and adds the timestamp to history. + * @param timestamp Timestamp of the pin. + */ + void increase_pin_count(const std::size_t timestamp) + { + _pin_count++; + _pin_timestamps.push_back(timestamp); + } + + /** + * Decreases the pin count. + */ + void decrease_pin_count() + { + _pin_count--; + } + + /** + * @return True, if the frame is dirty ergo the content + * of the page was modified. + */ + [[nodiscard]] bool is_dirty() const + { + return _is_dirty; + } + + /** + * Update the dirty flag. + * @param is_dirty + */ + void is_dirty(const bool is_dirty) + { + _is_dirty = is_dirty; + } + + /** + * @return Timestamp of the last pin. + */ + [[nodiscard]] std::size_t last_pin_timestamp() const + { + if (_pin_timestamps.empty()) + { + return std::numeric_limits::max(); + } + + return _pin_timestamps.back(); + } + + /** + * @return Timestamp of the i-th pin. + */ + [[nodiscard]] std::size_t pin_timestamp(const std::size_t i) const + { + return _pin_timestamps[i]; + } + + /** + * @return Number of how many times the frame was pinned. + */ + [[nodiscard]] std::size_t count_all_pins() const + { + return _pin_timestamps.size(); + } + + [[nodiscard]] bool is_last_chance() const + { + return _is_last_chance; + } + void is_last_chance(bool is_last_chance) + { + _is_last_chance = is_last_chance; + } + + private: + // Id of the page that is loaded into this frame. + storage::Page::id_t _page_id = storage::Page::INVALID_PAGE_ID; + + // Number of current pins. + std::size_t _pin_count = 0u; + + // Is the frame modified and should be written back to disk? + bool _is_dirty = false; + + // Last chance bit for clock strategy. + bool _is_last_chance = false; + + // List of timestamps. + std::vector _pin_timestamps; +}; +} // namespace beedb::buffer diff --git a/include/buffer/lfu_strategy.h b/include/buffer/lfu_strategy.h new file mode 100644 index 0000000..2c3d08a --- /dev/null +++ b/include/buffer/lfu_strategy.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "replacement_strategy.h" + +namespace beedb::buffer +{ +/** + * Replaces the last frequently used frame. + */ +class LFUStrategy final : public ReplacementStrategy +{ + public: + LFUStrategy(std::size_t count_frames); + ~LFUStrategy() override = default; + std::size_t find_victim(std::vector &pages) override; + void on_pin(std::size_t frame_index, std::size_t timestamp) override; + + private: + std::vector _pin_count; +}; +} // namespace beedb::buffer diff --git a/include/buffer/lru_k_strategy.h b/include/buffer/lru_k_strategy.h new file mode 100644 index 0000000..e8918e5 --- /dev/null +++ b/include/buffer/lru_k_strategy.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "replacement_strategy.h" +#include + +namespace beedb::buffer +{ +class LRUKStrategy final : public ReplacementStrategy +{ + public: + LRUKStrategy(std::size_t count_frames, std::size_t k); + ~LRUKStrategy() override = default; + std::size_t find_victim(std::vector &pages) override; + void on_pin(std::size_t frame_index, std::size_t timestamp) override; + + private: + // Configured K-parameter. + const std::size_t _k = 0; + + // List of timestamps for every page. + std::vector> _history; + + [[nodiscard]] std::size_t last_timestamp(std::size_t frame_index) const + { + if (_history[frame_index].empty()) + { + return std::numeric_limits::max(); + } + + return _history[frame_index].back(); + } +}; +} // namespace beedb::buffer diff --git a/include/buffer/lru_strategy.h b/include/buffer/lru_strategy.h new file mode 100644 index 0000000..8d0ebe0 --- /dev/null +++ b/include/buffer/lru_strategy.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "replacement_strategy.h" + +namespace beedb::buffer +{ +/** + * Replaces the last recently used frame. + */ +class LRUStrategy final : public ReplacementStrategy +{ + public: + LRUStrategy(std::size_t count_frames); + ~LRUStrategy() override = default; + std::size_t find_victim(std::vector &pages) override; + void on_pin(std::size_t frame_index, std::size_t timestamp) override; + + private: + std::vector _last_pin_timestamps; +}; +} // namespace beedb::buffer diff --git a/include/buffer/manager.h b/include/buffer/manager.h new file mode 100644 index 0000000..df4587d --- /dev/null +++ b/include/buffer/manager.h @@ -0,0 +1,140 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "frame.h" +#include "replacement_strategy.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::buffer +{ +/** + * The BufferManager buffers pages stored on the disk in memory. + * Since the system has not infinite memory, the number of buffered + * pages (named "frames") is limited. + * + * Every access to a page on the disk is done by pinning the page + * through the BufferManager. When the page is not needed any more + * (e.g. all tuples are scanned), the page can be unpinned by the + * BufferManager. + */ +class Manager +{ + public: + Manager(std::size_t count_frames, storage::Manager &space_manager, + std::unique_ptr &&replacement_strategy = nullptr); + ~Manager(); + + /** + * Loads the page from disk into memory and returns a pointer + * to the page. When the page is still buffered, the page will + * not be loaded twice, but guaranteed to stay in memory until + * it is unpinned. + * + * @param page_id Id of the page. + * @return Pointer to the page, that allows accessing the data. + */ + storage::Page *pin(storage::Page::id_t page_id); + + /** + * Notifies the BufferManager that the page is not needed anymore. + * In case no one needs the page, the frame can be used for other + * pages buffered from disk in memory. + * + * @param page_id Id of the page. + * @param is_dirty True, when the content of the page was modified. + */ + void unpin(storage::Page::id_t page_id, bool is_dirty); + + /** + * Notifies the BufferManager that the page is not needed anymore. + * In case no one needs the page, the frame can be used for other + * pages buffered from disk in memory. + * + * @param page_id Id of the page. + * @param is_dirty True, when the content of the page was modified. + */ + void unpin(storage::Page *page, bool is_dirty) + { + unpin(page->id(), is_dirty); + } + + /** + * Allocates a new page on the disk and loads the page to memory. + * + * @return Pointer to the pinned(!) page. + */ + template storage::Page *allocate() + { + return this->pin(this->_space_manager.allocate

()); + } + + /** + * Set the replacement strategy which picks frames to be replaced, + * when all frames are occupied, but a new page is requested to + * be loaded from disk to memory. + * @param replacement_strategy + */ + void replacement_strategy(std::unique_ptr &&replacement_strategy) + { + _replacement_strategy = std::move(replacement_strategy); + } + + /** + * @return Number of evicted frames. + */ + [[nodiscard]] std::size_t evicted_frames() const + { + return _evicted_frames; + } + + private: + storage::Manager &_space_manager; + std::unique_ptr _replacement_strategy; + + std::vector _frames; + std::size_t _pin_sequence = 0u; + std::size_t _evicted_frames = 0u; + + std::mutex _latch; + + /** + * Writes all dirty pages from memory to disk. + */ + void flush(); + + /** + * Lookup for frame information for a specific page. + * The frame information stores information like pin + * history, pinned page for a frame. + * + * @param page_id Id of the page. + * @return An iterator to the frame information or end() if the frame was not found. + */ + std::vector::iterator frame_information(storage::Page::id_t page_id); +}; +} // namespace beedb::buffer \ No newline at end of file diff --git a/include/buffer/random_strategy.h b/include/buffer/random_strategy.h new file mode 100644 index 0000000..4635404 --- /dev/null +++ b/include/buffer/random_strategy.h @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "replacement_strategy.h" +#include + +namespace beedb::buffer +{ +class RandomStrategy final : public ReplacementStrategy +{ + public: + RandomStrategy(std::size_t count_frames); + ~RandomStrategy() override = default; + + std::size_t find_victim(std::vector &pages) override; + + void on_pin(std::size_t frame_index, std::size_t timestamp) override; + + private: + const std::size_t _pool_size; + util::RandomGenerator _random_generator{}; +}; +} // namespace beedb::buffer \ No newline at end of file diff --git a/include/buffer/replacement_strategy.h b/include/buffer/replacement_strategy.h new file mode 100644 index 0000000..fc82b8e --- /dev/null +++ b/include/buffer/replacement_strategy.h @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include + +namespace beedb::buffer +{ +/** + * The BufferReplacementStrategy decides which frame should + * be re-used for a new page, when no free frame is available. + */ +class ReplacementStrategy +{ + public: + ReplacementStrategy() = default; + virtual ~ReplacementStrategy() = default; + + /** + * Picks a frame that holds a unused page and should be + * replaced by a new page, requested by a query. + * + * @param frame_information Information of all frames. + * @return Index of the frame, that should be used for a new page. + */ + virtual std::size_t find_victim(std::vector &pages) = 0; + + /** + * This callback is called every time the buffer manager pins a page + * to given index in the frame buffer. This gives the strategy the + * possibility to notice some additional information like the pin + * history that is used for finding a victim. + * @param frame_index Index in the frame buffer that is used for pinning. + * @param timestamp Timestamp the page was pinned in the frame buffer. + */ + virtual void on_pin(std::size_t frame_index, std::size_t timestamp) = 0; +}; +} // namespace beedb::buffer diff --git a/include/compression/wah_bit_vector.h b/include/compression/wah_bit_vector.h new file mode 100644 index 0000000..08c89c2 --- /dev/null +++ b/include/compression/wah_bit_vector.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::compression +{ +class WAHBitVector +{ + public: + WAHBitVector() : _is_literal_word(0), _content(0) + { + } + ~WAHBitVector() = default; + + inline bool is_fill() const + { + return _is_literal_word == 0; + } + inline bool is_literal() const + { + return _is_literal_word; + } + inline void is_fill(bool is_fill) + { + _is_literal_word = !is_fill; + } + + inline bool fill_bit() const + { + return _content >> 30; + } + inline void fill_bit(const bool fill_bit) + { + _content = (fill_bit << 30) | count(); + } + + inline std::uint32_t count() const + { + return _content & 0x3FFFFFFF; + } + + WAHBitVector &operator++() + { + _content = (count() + 1) | (fill_bit() << 30); + return *this; + } + + WAHBitVector operator++(int) + { + WAHBitVector copy(*this); + ++(*this); + return copy; + } + + inline void set(const std::size_t index, const bool bit) + { + assert(index >= 0 && index <= 31); + _content ^= (-bit ^ _content) & (1UL << index); + } + + inline bool get(const std::size_t index) const + { + assert(index >= 0 && index <= 31); + return (_content >> index) & 1U; + } + + inline void clear() + { + _content = 0; + } + + private: + std::uint32_t _is_literal_word : 1, _content : 31; +} __attribute__((packed)); +} // namespace beedb::compression diff --git a/include/concurrency/metadata.h b/include/concurrency/metadata.h new file mode 100644 index 0000000..b40d40a --- /dev/null +++ b/include/concurrency/metadata.h @@ -0,0 +1,172 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "timestamp.h" +#include +#include +#include + +namespace beedb::concurrency +{ +/** + * Information about a tuple, stored in front of the data on a page. + * The information contains begin and end timestamp of the data and + * also a pointer (in form of a record identifier) to the "original" + * record. + * The original record is the record placed in the table space while + * versioned records are stored in the time travel space. + */ +class Metadata +{ + public: + explicit Metadata(const timestamp begin_timestamp) + : _begin_timestamp(begin_timestamp), _end_timestamp(timestamp::make_infinity()) + { + } + + Metadata(const storage::RecordIdentifier original_record_identifier, const timestamp begin_timestamp) + : _original_record_identifier(original_record_identifier), _begin_timestamp(begin_timestamp), + _end_timestamp(timestamp::make_infinity()) + { + } + + Metadata(const Metadata &other) + : _original_record_identifier(other._original_record_identifier), _begin_timestamp(timestamp::make_infinity()), + _end_timestamp(timestamp::make_infinity()) + { + _begin_timestamp = other._begin_timestamp; + _end_timestamp = other._end_timestamp; + _next_in_version_chain = other._next_in_version_chain; + } + + ~Metadata() = default; + + /** + * @return Timestamp the related record was created. + */ + [[nodiscard]] timestamp begin_timestamp() const + { + return _begin_timestamp; + } + + /** + * @return Timestamp the related record was removed + * or overridden by an update. + */ + [[nodiscard]] timestamp end_timestamp() const + { + return _end_timestamp; + } + + /** + * @return Pointer to the next record in the version chain. + */ + [[nodiscard]] storage::RecordIdentifier next_in_version_chain() const + { + return _next_in_version_chain; + } + + /** + * Set the creation timestamp. + * @param timestamp Timestamp the related records gets alive. + */ + void begin_timestamp(const timestamp timestamp) + { + _begin_timestamp = timestamp; + } + + /** + * Tries to set the timestamp but if and only if + * the timestamp holds the value given in the old_timestamp + * argument. + * @param old_timestamp Expected timestamp. + * @param timestamp New timestamp. + * @return True, if the timestamp could be set. + */ + bool try_begin_timestamp(timestamp old_timestamp, const timestamp timestamp) + { + if (_begin_timestamp == old_timestamp) + { + _begin_timestamp = timestamp; + return true; + } + return false; + } + + /** + * Updates the end timestamp. + * @param timestamp Timestamp the record ends. + */ + void end_timestamp(const timestamp timestamp) + { + _end_timestamp.store(timestamp); + } + + /** + * Tries to set the timestamp but if and only if + * the timestamp holds the value given in the old_timestamp + * argument. + * @param old_timestamp Expected timestamp. + * @param timestamp New timestamp. + * @return True, if the timestamp could be set. + */ + bool try_end_timestamp(timestamp old_timestamp, const timestamp timestamp) + { + if (_end_timestamp == old_timestamp) + { + _end_timestamp = timestamp; + return true; + } + return false; + } + + /** + * Update the pointer to the next version in history. + * @param next Pointer to the next version. + */ + void next_in_version_chain(const storage::RecordIdentifier next) + { + _next_in_version_chain = next; + } + + /** + * @return Pointer to the current version in the table space. + */ + [[nodiscard]] storage::RecordIdentifier original_record_identifier() const + { + return _original_record_identifier; + } + + // Pointer to the record in the table space. + storage::RecordIdentifier _original_record_identifier; + + // Timestamp the record begins living. + timestamp _begin_timestamp; + + // Timestamp the record dies. + timestamp _end_timestamp; + + // Pointer to the next record in version chain. + storage::RecordIdentifier _next_in_version_chain; +}; +} // namespace beedb::concurrency \ No newline at end of file diff --git a/include/concurrency/timestamp.h b/include/concurrency/timestamp.h new file mode 100644 index 0000000..ad45b62 --- /dev/null +++ b/include/concurrency/timestamp.h @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include + +namespace beedb::concurrency +{ +/** + * Represents a transaction timestamp, e.g., a begin or end timestamp + * of a record or begin or commit time of a transaction. + * The timestamp contains the "real" time (that is a global counter incremented + * for each new transaction) and a flag that indices the commit-state of the timestamp. + * The flag may be 1 for committed and else 0. + * + * The time "0" in combination with the set committed flag indicates "infinity". + */ +class timestamp +{ + public: + using timestamp_t = std::uint64_t; + + static auto make_infinity() + { + return timestamp{}; + } + + timestamp(const timestamp_t t, bool is_committed) : _timestamp_and_committed_flag((t << 1u) | is_committed) + { + } + + timestamp &operator=(const timestamp &) = default; + + ~timestamp() = default; + + /** + * @return True, if the transaction of the timestamp was committed. + */ + [[nodiscard]] bool is_committed() const + { + return _timestamp_and_committed_flag & 1u; + } + + /** + * @return The "real" time without "committed" flag. + */ + [[nodiscard]] timestamp_t time() const + { + return _timestamp_and_committed_flag >> 1u; + } + + /** + * @return True, when this timestamp is "never ending". + */ + [[nodiscard]] bool is_infinity() const + { + return _timestamp_and_committed_flag == 1u; + } + + bool operator==(const timestamp other) const + { + return other._timestamp_and_committed_flag == _timestamp_and_committed_flag; + } + + bool operator!=(const timestamp other) const + { + return other._timestamp_and_committed_flag != _timestamp_and_committed_flag; + } + + bool operator<(const timestamp other) const + { + return time() < other.time(); + } + + bool operator<=(const timestamp other) const + { + return time() <= other.time(); + } + + bool operator>(const timestamp other) const + { + return time() > other.time(); + } + + bool operator>=(const timestamp other) const + { + return time() >= other.time(); + } + + private: + timestamp() = default; + + // Time and committed flag (last bit). + timestamp_t _timestamp_and_committed_flag = 1u; +}; +} // namespace beedb::concurrency \ No newline at end of file diff --git a/include/concurrency/transaction.h b/include/concurrency/transaction.h new file mode 100644 index 0000000..b29e0e3 --- /dev/null +++ b/include/concurrency/transaction.h @@ -0,0 +1,367 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "metadata.h" +#include "timestamp.h" +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::concurrency +{ +/** + * Isolation levels with more (serializable) or + * less (read uncommitted) isolation. + */ +enum IsolationLevel : std::uint8_t +{ + Serializable = 0, + // RepeatableRead = 1, + // ReadCommitted = 2, + // ReadUncommitted = 3 +}; + +/** + * A read set item contains two pointers (in form of record identifiers): + * - A pointer to the "original" record placed in the table space + * - A pointer to the read version (which may be the same as the "original"). + * The first one is needed to compare read set items with the write set items + * of other transactions. + */ +class ReadSetItem +{ + public: + ReadSetItem(const storage::RecordIdentifier in_place_record_identifier, + const storage::RecordIdentifier read_record_identifier) + : _in_place_record_identifier(in_place_record_identifier), _read_record_identifier(read_record_identifier) + { + } + ReadSetItem(ReadSetItem &&) = default; + ~ReadSetItem() = default; + + /** + * @return Pointer to the record in the table space. + */ + [[nodiscard]] storage::RecordIdentifier in_place_record_identifier() const + { + return _read_record_identifier; + } + + /** + * @return Pointer to the really read record (which may + * be in table or time travel space). + */ + [[nodiscard]] storage::RecordIdentifier read_record_identifier() const + { + return _read_record_identifier; + } + + private: + // Pointer to the record in the table space. + storage::RecordIdentifier _in_place_record_identifier; + + // Pointer to the really read record. + storage::RecordIdentifier _read_record_identifier; +}; + +/** + * The scan set indicates that a transaction read a set + * of tuples (select * from ... or aggregation for example). + * A scan can miss a record that is inserted/updated/deleted + * by a concurrent transaction. Therefore the scan has to be + * validated at commit time. + */ +class ScanSetItem +{ + public: + ScanSetItem() = default; + + explicit ScanSetItem(std::unique_ptr &&predicate) + : _predicate(std::move(predicate)) + { + } + + explicit ScanSetItem(const table::Table &table) : _table(std::make_optional(std::ref(table))) + { + } + + ScanSetItem(ScanSetItem &&) = default; + + ~ScanSetItem() = default; + + /** + * @return Table that was scanned. + */ + [[nodiscard]] std::optional> table() const + { + return _table; + } + + /** + * Update table that was scanned. + * @param table Table + */ + void table(const table::Table &table) + { + _table = std::make_optional(std::ref(table)); + } + + /** + * @return Predicate that was used for scanning. + */ + [[nodiscard]] const std::unique_ptr &predicate() const + { + return _predicate; + } + + /** + * Update predicate that was used for scanning. + * @param predicate Predicate that was used for scanning. + */ + void predicate(std::unique_ptr &&predicate) + { + _predicate = std::move(predicate); + } + + private: + // Table, needed for re-execution. + std::optional> _table = std::nullopt; + + // Predicates, needed for re-execution. + std::unique_ptr _predicate{nullptr}; +}; + +/** + * A write set item contains two pointers (in form of record identifiers): + * - A pointer to the "original" record placed in the table space + * - A pointer to the updated/deleted version. + * Also the type of modification (inserted/updated/removed) is stored in + * the write set. + */ +class WriteSetItem +{ + public: + enum ModificationType + { + Inserted, + Updated, + Deleted + }; + + WriteSetItem(const table::Table::id_t table_id, const storage::RecordIdentifier in_place_record_identifier, + const storage::RecordIdentifier old_version_record_identifier, + const ModificationType modification_type, const storage::Page::offset_t size_written) + : _table_id(table_id), _in_place_record_identifier(in_place_record_identifier), + _old_version_record_identifier(old_version_record_identifier), _type(modification_type), + _written_size(size_written) + { + } + + WriteSetItem(const table::Table::id_t table_id, const storage::RecordIdentifier record_identifier, + const storage::Page::offset_t size_written) + : WriteSetItem(table_id, record_identifier, record_identifier, ModificationType::Inserted, size_written) + { + } + + WriteSetItem(WriteSetItem &&) = default; + WriteSetItem(const WriteSetItem &) = default; + + ~WriteSetItem() = default; + + /** + * @return The id of the table this write belongs to. + */ + [[nodiscard]] table::Table::id_t table_id() const + { + return _table_id; + } + + /** + * @return Pointer to the record that was really written. + */ + [[nodiscard]] storage::RecordIdentifier in_place_record_identifier() const + { + return _in_place_record_identifier; + } + + /** + * @return Pointer to the record that was overwritten (for updates + * and deletes). + */ + [[nodiscard]] storage::RecordIdentifier old_version_record_identifier() const + { + return _old_version_record_identifier; + } + + bool operator==(const ModificationType type) const + { + return _type == type; + } + + /** + * @return Number of bytes written. + */ + [[nodiscard]] storage::Page::offset_t written_size() const + { + return _written_size; + } + + private: + // Id of the table, this write belongs to. + table::Table::id_t _table_id; + + // The record identifier of the record that was updated in place. + storage::RecordIdentifier _in_place_record_identifier; + + // The record identifier of the record that was versioned. + storage::RecordIdentifier _old_version_record_identifier; + + // Type of the update. + ModificationType _type; + + // Size of the written data (inclusively metadata). + storage::Page::offset_t _written_size; +}; + +/** + * The transaction stores the begin and commit timestamp of the transaction, + * as well as the read and write set. + */ +class Transaction +{ + public: + explicit Transaction(const IsolationLevel isolation_level, const timestamp begin) + : _isolation_level(isolation_level), _begin_timestamp(begin), _commit_timestamp(timestamp::make_infinity()) + { + } + + ~Transaction() = default; + + /** + * @return Isolation level of this transaction. + */ + [[maybe_unused]] [[nodiscard]] IsolationLevel isolation_level() const + { + return _isolation_level; + } + + /** + * Update the commit timestamp of this transaction. + * @param commit_timestamp Timestamp this transaction commits. + */ + void commit_timestamp(const timestamp commit_timestamp) + { + _commit_timestamp = commit_timestamp; + } + + /** + * @return Timestamp this transaction was started. + */ + [[nodiscard]] timestamp begin_timestamp() const + { + return _begin_timestamp; + } + + /** + * @return Timestamp this transaction was committed. + */ + [[nodiscard]] timestamp commit_timestamp() const + { + return _commit_timestamp; + } + + /** + * Adds an item to the read set. + * @param read_set_item Item that was read by this transaction. + */ + void add_to_read_set(ReadSetItem &&read_set_item) + { + _read_set.emplace_back(std::move(read_set_item)); + } + + /** + * Adds an item to the write set. + * @param write_set_item Item that was written by this transaction. + */ + void add_to_write_set(WriteSetItem &&write_set_item) + { + _write_set.emplace_back(std::move(write_set_item)); + } + + /** + * Adds an item to the scan set. + * @param scan_set_item Item that was scanned by this transaction. + */ + void add_to_scan_set(ScanSetItem *scan_set_item) + { + _scan_set.emplace_back(scan_set_item); + } + + /** + * @return Read set of this transaction. + */ + [[nodiscard]] const std::vector &read_set() const + { + return _read_set; + } + + /** + * @return Write set of this transaction. + */ + [[nodiscard]] const std::vector &write_set() const + { + return _write_set; + } + + /** + * @return Scan set of this transaction. + */ + [[nodiscard]] const std::vector &scan_set() const + { + return _scan_set; + } + + private: + // Isolation level of the transaction. + const IsolationLevel _isolation_level; + + // Timestamp this transaction was created. + const timestamp _begin_timestamp; + + // Timestamp this transaction committed. + timestamp _commit_timestamp; + + // Items that were read by this transaction. + std::vector _read_set; + + // Items that were written by this transaction. + std::vector _write_set; + + // Items that were scanned by this transaction. + std::vector _scan_set; +}; +} // namespace beedb::concurrency \ No newline at end of file diff --git a/include/concurrency/transaction_callback.h b/include/concurrency/transaction_callback.h new file mode 100644 index 0000000..bd5f481 --- /dev/null +++ b/include/concurrency/transaction_callback.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "transaction.h" +#include + +namespace beedb::concurrency +{ +/** + * The transaction callback will be called when a transaction begins + * and when the transaction was aborted or committed. + */ +class TransactionCallback +{ + public: + TransactionCallback() = default; + virtual ~TransactionCallback() = default; + + /** + * Will be called when a transaction starts. + * @param transaction Transaction that started. + */ + virtual void on_begin(Transaction *transaction) = 0; + + /** + * Will be called when a transaction ends. This may be + * a successful or not successful commit or abort. + * @param transaction Transaction that ends. + * @param successful True, when successful committed. + */ + virtual void on_end(Transaction *transaction, const bool successful) = 0; +}; + +class SilentTransactionCallback : public TransactionCallback +{ + public: + SilentTransactionCallback() = default; + ~SilentTransactionCallback() override = default; + + void on_begin(Transaction *) override + { + } + + void on_end(Transaction *, const bool) override + { + } +}; + +/** + * This implementation of a transaction callback takes two lambdas + * that will be invoked on start and end of a transaction. + */ +class FunctionTransactionCallback : public TransactionCallback +{ + public: + FunctionTransactionCallback(std::function &&begin_callback, + std::function &&end_callback) + : _begin_transaction_callback(std::move(begin_callback)), _end_transaction_callback(std::move(end_callback)) + { + } + + ~FunctionTransactionCallback() override = default; + + void on_begin(Transaction *transaction) override + { + _begin_transaction_callback(transaction); + } + + void on_end(Transaction *transaction, const bool successful) override + { + _end_transaction_callback(transaction, successful); + } + + private: + std::function _begin_transaction_callback; + std::function _end_transaction_callback; +}; +} // namespace beedb::concurrency \ No newline at end of file diff --git a/include/concurrency/transaction_manager.h b/include/concurrency/transaction_manager.h new file mode 100644 index 0000000..22a7830 --- /dev/null +++ b/include/concurrency/transaction_manager.h @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "metadata.h" +#include "transaction.h" +#include +#include +#include +#include +#include + +namespace beedb::concurrency +{ +/** + * The TransactionManager creates new transactions and performs + * commit and abort of those transactions. + */ +class TransactionManager +{ + public: + explicit TransactionManager(buffer::Manager &buffer_manager); + ~TransactionManager(); + + /** + * Creates a new transaction with the given isolation level. + * + * @param isolation_level Isolation level. + * @return New transaction. + */ + Transaction *new_transaction(const IsolationLevel isolation_level = IsolationLevel::Serializable); + + /** + * Aborts the given transaction and reverts all + * writes done by this transaction. + * @param transaction Transaction to abort. + */ + void abort(Transaction &transaction); + + /** + * Commits a transaction. Before writing the data written by the transaction + * to the global state, the transaction is validated. + * @param transaction Transaction to commit. + * @return True, when the commit was successful. Otherwise, the transaction + * will be aborted automatically. + */ + bool commit(Transaction &transaction); + + /** + * Tests if a given timestamp range is visible for the given transaction. + * @param transaction Transaction to test. + * @param begin_timestamp Start timestamp. + * @param end_timestamp End timestamp. + * @return True, when the transaction is allowed to see the data living in the given range. + */ + static bool is_visible(const Transaction &transaction, const timestamp begin_timestamp, + const timestamp end_timestamp); + + /** + * Tests if a given metadata is visible for the given transaction. + * @param transaction Transaction to test. + * @param metadata Metadata of a record. + * @return True, when the transaction is allowed to see the data living in the given range. + */ + static bool is_visible(const Transaction &transaction, Metadata *const metadata) + { + return is_visible(transaction, metadata->begin_timestamp(), metadata->end_timestamp()); + } + + /** + * @return The next timestamp for transactions. + */ + timestamp::timestamp_t next_timestamp() const + { + return _next_timestamp.load(); + } + + /** + * Updates the next timestamp for transactions. + * @param timestamp Next timestamp for the next transaction. + */ + void next_timestamp(const timestamp::timestamp_t timestamp) + { + _next_timestamp.store(timestamp); + } + + private: + // Buffer manager to read/write to pages. + buffer::Manager &_buffer_manager; + + // Timestamp for the next transaction. + std::atomic _next_timestamp{2u}; + + // Map of Timestamp => Transaction of only committed transactions. + std::unordered_map _commit_history; + + // Latch for the history map. + std::shared_mutex _commit_history_latch; + + /** + * Validates a transaction to commit. + * @param transaction Transaction to commit. + * @return True, when the transaction is valid. + */ + bool validate(Transaction &transaction); + + /** + * Validates write skew anomalies. A write skew anomaly can occur, + * when a transaction T reads a record A and writes a record B, + * but a concurrent transaction T' writes to A between T reading A and + * writing B. + * What the DBMS can not see is how A affects B in T, therefore we have + * to abort the transaction. + * @param transaction Transaction to validate. + * @param concurrent_transactions List of transactions that committed between + * the transactions started and committed. + * @return True, when the transaction is valid. + */ + static bool validate_write_skew_anomalies(Transaction &transaction, + const std::vector &concurrent_transactions); + + /** + * Validates the scan set of a transaction. A transaction T + * may scan a table while a concurrent transaction inserts/deletes/updates + * a record that would be part of the scan. Therefore, the scan + * has to be (re)checked to validate that the scan would be the same + * at commit time. + * @param transaction Transaction to validate. + * @param concurrent_transactions List of concurrent transactions. + * @return True, when the transaction is valid. + */ + bool validate_scan_set(Transaction &transaction, const std::vector &concurrent_transactions); + + /** + * Validates a single scan of a transaction against the write sets of concurrent transactions. + * @param scan_set_item Single scan set. + * @param write_set List of write sets of concurrent transactions. + * @return True, when the scan set is valid. + */ + bool validate_scan_set_item(ScanSetItem *scan_set_item, const std::vector &write_set); + + /** + * Calculates the transactions that have committed between begin and end. + * @param begin Begin. + * @param end End. + * @return List of transactions that committed in the given time range. + */ + std::vector committed_transactions(const timestamp::timestamp_t begin, + const timestamp::timestamp_t end); +}; +} // namespace beedb::concurrency \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..eaa1fd1 --- /dev/null +++ b/include/config.h @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace beedb +{ +/** + * Holds all configuration for the DBMS. + */ +class Config +{ + public: + using ConfigKey = std::string; + using ConfigValue = std::int32_t; // TODO: consider renaming this or the struct below...? + + ///// COMPILE TIME OPTIONS - CHANGES REQUIRE REBUILDING + static constexpr std::uint16_t page_size = 4096; + static constexpr std::uint16_t b_plus_tree_page_size = 1024; + static constexpr auto max_clients = 64u; + static constexpr auto cli_history_file = "beedb-cli.txt"; + + public: + // defining constants and other values, for convenience and ease-of-reading + enum BufferReplacementStrategy + { + Random, + LRU, + LRU_K, + LFU, + Clock + }; + + // important key's for non-string based notation: + static constexpr auto k_PageSize = "page_size"; + static constexpr auto k_BPlusTreePageSize = "b_plus_tree_page_size"; + static constexpr auto k_ScanPageLimit = "scan_page_limit"; + + static constexpr auto k_BufferFrames = "buffer_frames"; + static constexpr auto k_BufferReplacementStrategy = "buffer_replacement_strategy"; + static constexpr auto k_LRU_K = "lru_k"; + + static constexpr auto k_CheckFinalPlan = "check_final_plan"; + + static constexpr auto k_OptimizationEnableHashJoin = "enable_hash_join"; + static constexpr auto k_OptimizationEnableIndexScan = "enable_index_scan"; + static constexpr auto k_OptimizationEnablePredicatePushDown = "enable_predicate_push_down"; + static constexpr auto k_OptimizationDisableOptimization = "no_optimization"; + + static constexpr auto k_PrintExecutionStatistics = "print_execution_statistics"; + + // this object represents a ConfigValue in the map and stores some meta information + struct ConfigMapValue + { + ConfigValue value; + bool is_mutable = true; + bool requires_restart = false; + + explicit operator ConfigValue() const + { + return value; + } + + explicit operator std::size_t() const + { + return static_cast(value); + } + + explicit operator std::uint32_t() const + { + return static_cast(value); + } + + explicit operator bool() const + { + return value != 0u; + } + + explicit operator BufferReplacementStrategy() const + { + return static_cast(value); + } + + static constexpr bool immutable = false; + }; + + Config() + { + // for transparency, we make compile-time options available in the config map (read only) + _configuration.insert({k_PageSize, {page_size, ConfigMapValue::immutable}}); + _configuration.insert({k_BPlusTreePageSize, {b_plus_tree_page_size, ConfigMapValue::immutable}}); + } + + ~Config() = default; // TODO: persist changes in config + + /** + * @brief operator [] Read only access to values of the Configuration! + * + * This method is identical to get. + * + * @param key + * @return + */ + ConfigMapValue operator[](const ConfigKey &key) const + { + if (_configuration.find(key) == _configuration.end()) + { + throw exception::ConfigException(key); + } + return _configuration.at(key); + } + + ConfigMapValue get(const ConfigKey &key) const + { + return this->operator[](key); + } + + /** + * @brief set sets a configuration value. Can override existing values, if the flag ConfigMapValue.is_mutable is not + * set! + * @param key a new or existing key + * @param value the new value + * @param is_mutable defaults to true + * @param requires_restart defaults to false. currently unused. + * @return the input value, returned from the map + */ + void set(const ConfigKey &key, ConfigValue value, bool is_mutable = true, bool requires_restart = false) + { + if (_configuration.find(key) != _configuration.end()) + { + // if this key already exists, check if it is read only + if (!_configuration.at(key).is_mutable) + { + throw exception::CanNotModifyAtRuntimeException(key); + } + } + // // TODO implement: persist config map on shutdown and uncomment this + // if (_configuration[key].requires_restart) { + // std::cout << "Note: " << "This option requires a restart of the application to take effect!" + // << std::endl; + // } + + _configuration[key] = ConfigMapValue{value, is_mutable, requires_restart}; + } + + bool contains(const ConfigKey &key) const + { + return _configuration.find(key) != _configuration.end(); + } + + explicit operator std::string() + { + auto str = std::string{"Current Configuration:\n"}; + for (const auto &[k, value] : _configuration) + { + str += ""; + str += std::to_string(value.value); + // str += ", " + (value.is_mutable ? std::string("r/w") : std::string("r") ); + + str += "\t<- " + k + (!value.is_mutable ? " (immutable)" : "") + "\n"; + } + return str; + } + + private: + using ConfigurationMap = std::unordered_map; + + // holds the actual configuration values + ConfigurationMap _configuration; +}; +} // namespace beedb diff --git a/include/database.h b/include/database.h new file mode 100644 index 0000000..7d9c4f7 --- /dev/null +++ b/include/database.h @@ -0,0 +1,203 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include
+#include
+#include
+#include +#include + +namespace beedb +{ +class Database +{ + public: + Database(Config &config, const std::string &file_name); + ~Database(); + + /** + * Boots the database management system. + * During the boot, all persisted tables, their schemas and indices + * will be loaded to memory. + * All indices will be filled with data from disk. + */ + void boot(); + + /** + * @return Instance of the TableDiskManager. + */ + [[nodiscard]] table::TableDiskManager &table_disk_manager() + { + return _table_disk_manager; + } + + /** + * @return Instance of the BufferManager. + */ + [[nodiscard]] buffer::Manager &buffer_manager() + { + return _buffer_manager; + } + + /** + * @return Instance of the transaction manager. + */ + [[nodiscard]] concurrency::TransactionManager &transaction_manager() + { + return _transaction_manager; + } + + /** + * @return Immutable instance of the config. + */ + [[nodiscard]] const Config &config() const + { + return _config; + } + + /** + * @return Mutable instance of the config. + */ + [[nodiscard]] Config &config() + { + return _config; + } + + [[nodiscard]] statistic::SystemStatistics &system_statistics() + { + return _statistics; + } + + /** + * Checks whether a table exists. + * + * @param name Name of the table. + * @return True, if the table exists. + */ + [[nodiscard]] bool table_exists(const std::string &name) + { + std::shared_lock _{_tables_latch}; + return _tables.find(name) != _tables.end(); + } + + /** + * Returns a pointer to the requested table. + * + * @param name Name of the requested table. + * @return Pointer to the table. + */ + [[nodiscard]] table::Table *table(const std::string &name) + { + std::shared_lock _{_tables_latch}; + + if (table_exists(name)) + { + return _tables[name]; + } + + return nullptr; + } + + table::Table *operator[](const std::string &table_name) + { + std::shared_lock _{_tables_latch}; + + return table(table_name); + } + + /** + * Creates a table with a given schema. + * The table will be persisted and available after creation. + * + * @param schema Schema for the table. + */ + void create_table(concurrency::Transaction *transaction, const table::Schema &schema); + + /** + * Creates an index for a specific column. + * The index will be persisted, filled, and available after creation. + * + * @param column Column to be indexed. + * @param type Type of the index. + * @param name Name of the index. + * @param is_unique True, when the index is a unique index. + */ + void create_index(concurrency::Transaction *transaction, const table::Column &column, index::Type type, + const std::string &name, bool is_unique); + + private: + enum SystemPageIds : storage::Page::id_t + { + Metadata = 0, + Tables = 1, + Columns = 2, + Indices = 3, + Statistics = 4, + }; + + Config &_config; + storage::Manager _storage_manager; + buffer::Manager _buffer_manager; + table::TableDiskManager _table_disk_manager; + concurrency::TransactionManager _transaction_manager; + + std::unordered_map _tables; + std::shared_mutex _tables_latch; + + std::atomic_uint32_t _next_table_id = 1; + std::atomic_uint32_t _next_column_id = 1; + std::atomic_uint32_t _next_index_id = 1; + + statistic::SystemStatistics _statistics; + + /** + * Initializes the database. When the database is empty, + * we will create a new database schema containing all meta tables. + * + * @param create_schema True, when a database schema should be created. + */ + void initialize_database(bool create_schema); + + /** + * Persists the table statistics. + * + * @param table Table + * @param cardinality Cardinality + */ + void persist_table_statistics(table::Table *table, std::uint64_t cardinality); +}; + +} // namespace beedb diff --git a/include/exception/command_exception.h b/include/exception/command_exception.h new file mode 100644 index 0000000..16bf0c8 --- /dev/null +++ b/include/exception/command_exception.h @@ -0,0 +1,88 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::exception +{ +class CommandException : public std::exception +{ + public: + explicit CommandException(const std::string &message) : _message(message) + { + } + + ~CommandException() override = default; + + [[nodiscard]] const char *what() const noexcept override + { + return _message.c_str(); + } + + private: + const std::string _message; +}; + +class UnknownCommandException final : public CommandException +{ + public: + explicit UnknownCommandException(const std::string &command) + : CommandException("Unknown command '" + command + "'.") + { + } + + UnknownCommandException() : UnknownCommandException("Unknown command") + { + } + + ~UnknownCommandException() override = default; +}; + +class CommandSyntaxException final : public CommandException +{ + public: + CommandSyntaxException(const std::string &command, const std::string &syntax_hint) + : CommandException("Command syntax exception '" + command + "'.\n" + syntax_hint) + { + } + + CommandSyntaxException() : CommandException("Command syntax exception") + { + } + + ~CommandSyntaxException() override = default; +}; + +class UnknownCommandInputException final : public CommandException +{ + public: + explicit UnknownCommandInputException(const std::string &input) + : CommandException("Not supported input '" + input + "'") + { + } + + ~UnknownCommandInputException() override = default; +}; +} // namespace beedb::exception diff --git a/include/exception/concurrency_exception.h b/include/exception/concurrency_exception.h new file mode 100644 index 0000000..142f60b --- /dev/null +++ b/include/exception/concurrency_exception.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" + +namespace beedb::exception +{ +class AbortTransactionException final : public DatabaseException +{ + public: + AbortTransactionException() : DatabaseException(DatabaseException::Concurrency, "Transaction aborted") + { + } + + ~AbortTransactionException() override = default; +}; + +class TransactionDisabledException final : public DatabaseException +{ + public: + TransactionDisabledException() : DatabaseException(DatabaseException::Concurrency, "Transactions are disabled") + { + } + + ~TransactionDisabledException() override = default; +}; +} // namespace beedb::exception \ No newline at end of file diff --git a/include/exception/config_exception.h b/include/exception/config_exception.h new file mode 100644 index 0000000..018b50f --- /dev/null +++ b/include/exception/config_exception.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" +#include + +namespace beedb::exception +{ +class ConfigException : public DatabaseException +{ + public: + explicit ConfigException(const std::string &message) : DatabaseException(DatabaseException::Configuration, message) + { + } + ~ConfigException() override = default; +}; + +class KeyNotFoundException final : public ConfigException +{ + public: + explicit KeyNotFoundException(const std::string &key) : ConfigException("Option " + key + " not found.") + { + } + + ~KeyNotFoundException() override = default; +}; + +class CanNotModifyAtRuntimeException final : public ConfigException +{ + public: + explicit CanNotModifyAtRuntimeException(const std::string &key) + : ConfigException("Option " + key + " can not be changed at runtime.") + { + } + + ~CanNotModifyAtRuntimeException() override = default; +}; + +} // namespace beedb::exception \ No newline at end of file diff --git a/include/exception/disk_exception.h b/include/exception/disk_exception.h new file mode 100644 index 0000000..96d4830 --- /dev/null +++ b/include/exception/disk_exception.h @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" +#include + +namespace beedb::exception +{ +class DiskException : public DatabaseException +{ + public: + explicit DiskException(const std::string &message) : DatabaseException(DatabaseException::Disk, message) + { + } + ~DiskException() override = default; +}; + +class EvictedPagePinnedException final : public DiskException +{ + public: + explicit EvictedPagePinnedException(const std::uint64_t frame_index) + : DiskException("Can not evict page, frame " + std::to_string(frame_index) + " is pinned.") + { + } + + ~EvictedPagePinnedException() override = default; +}; + +class PageWasNotPinnedException final : public DiskException +{ + public: + explicit PageWasNotPinnedException(const std::uint64_t disk_id) + : DiskException("Page " + std::to_string(disk_id) + " is not pinned, but unpin() called.") + { + } + + ~PageWasNotPinnedException() override = default; +}; + +class NoFreeFrameException final : public DiskException +{ + public: + NoFreeFrameException() : DiskException("No free frame found for eviction.") + { + } + + ~NoFreeFrameException() override = default; +}; + +class CanNotOpenStorageFile final : public DiskException +{ + public: + explicit CanNotOpenStorageFile(const std::string &file_name) + : DiskException("Can not open storage file '" + file_name + "'.") + { + } + + ~CanNotOpenStorageFile() override = default; +}; +} // namespace beedb::exception \ No newline at end of file diff --git a/include/exception/exception.h b/include/exception/exception.h new file mode 100644 index 0000000..2eac298 --- /dev/null +++ b/include/exception/exception.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::exception +{ +/** + * Generic exception for plan building and execution. + */ +class DatabaseException : public std::exception +{ + public: + enum Layer + { + Parser, + LogicalPlan, + Execution, + Index, + Disk, + OutputHandler, + Configuration, + Concurrency + }; + + ~DatabaseException() override = default; + + [[nodiscard]] const char *what() const noexcept override + { + return _message.c_str(); + } + + protected: + DatabaseException(const Layer layer, const std::string &message) : _layer(layer), _message(message) + { + } + + private: + const Layer _layer; + const std::string _message; +}; +} // namespace beedb::exception diff --git a/include/exception/execution_exception.h b/include/exception/execution_exception.h new file mode 100644 index 0000000..fff5d75 --- /dev/null +++ b/include/exception/execution_exception.h @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" +#include + +namespace beedb::exception +{ +class ExecutionException : public DatabaseException +{ + public: + explicit ExecutionException(const std::string &message) : DatabaseException(DatabaseException::Execution, message) + { + } + ~ExecutionException() override = default; +}; + +class NoPhysicalOperatorForNode final : public ExecutionException +{ + public: + explicit NoPhysicalOperatorForNode(const std::string &operator_name) + : ExecutionException("Operator " + operator_name + " is not implemented physically.") + { + } + + ~NoPhysicalOperatorForNode() override = default; +}; + +class NotInTransactionException final : public ExecutionException +{ + public: + NotInTransactionException() : ExecutionException("No active transaction. Please BEGIN TRANSACTION first.") + { + } + + ~NotInTransactionException() override = default; +}; +} // namespace beedb::exception \ No newline at end of file diff --git a/include/exception/logical_exception.h b/include/exception/logical_exception.h new file mode 100644 index 0000000..cfd7f6f --- /dev/null +++ b/include/exception/logical_exception.h @@ -0,0 +1,215 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" +#include + +namespace beedb::exception +{ +class LogicalException : public DatabaseException +{ + public: + explicit LogicalException(const std::string &message) : DatabaseException(DatabaseException::LogicalPlan, message) + { + } + ~LogicalException() override = default; +}; + +class ColumnCanNotBeNull final : public LogicalException +{ + public: + ColumnCanNotBeNull(const std::string &table_name, const std::string &column_name) + : LogicalException("Value for column " + table_name + "." + column_name + " can not be NULL.") + { + } + + ~ColumnCanNotBeNull() override = default; +}; + +class ColumnNotFoundException final : public LogicalException +{ + public: + ColumnNotFoundException(const std::string &table_name, const std::string &column_name) + : ColumnNotFoundException(table_name + "." + column_name) + { + } + + explicit ColumnNotFoundException(const std::string &table_and_column_name) + : LogicalException("Column " + table_and_column_name + " not found.") + { + } + + ~ColumnNotFoundException() override = default; +}; + +class ColumnNotGroupedException final : public LogicalException +{ + public: + ColumnNotGroupedException(const std::string &table_name, const std::string &column_name) + : LogicalException("Column " + table_name + "." + column_name + " is neither aggregated nor grouped.") + { + } + + ~ColumnNotGroupedException() override = default; +}; + +class ColumnNotIndexed final : public LogicalException +{ + public: + ColumnNotIndexed(const std::string &table_name, const std::string &column_name) + : LogicalException("Index for " + table_name + "." + column_name + " not found.") + { + } + + ~ColumnNotIndexed() override = default; +}; + +class IndexAlreadyExistsException final : public LogicalException +{ + public: + IndexAlreadyExistsException(const std::string &table_name, const std::string &column_name) + : LogicalException("Index for " + table_name + "." + column_name + " already exists.") + { + } + + ~IndexAlreadyExistsException() override = default; +}; + +class IndexUnsupportedTypeException final : public LogicalException +{ + public: + explicit IndexUnsupportedTypeException(const std::string &type_name) + : LogicalException("Type " + type_name + " can not be indexed, Type is unsupported.") + { + } + + ~IndexUnsupportedTypeException() override = default; +}; + +class MultipleGroupByException final : public LogicalException +{ + public: + MultipleGroupByException() : LogicalException("Only one GROUP BY argument supported!") + { + } + + ~MultipleGroupByException() override = default; +}; + +class MultipleTableReferences final : public LogicalException +{ + public: + explicit MultipleTableReferences(const std::string &table_name) + : LogicalException("Multiple references to table " + table_name + " without disambiguation.") + { + } + + ~MultipleTableReferences() override = default; +}; + +class TableAlreadyExists final : public LogicalException +{ + public: + explicit TableAlreadyExists(const std::string &table_name) + : LogicalException("Table " + table_name + " already exists.") + { + } + + ~TableAlreadyExists() override = default; +}; + +class TableNotFoundException final : public LogicalException +{ + public: + explicit TableNotFoundException(const std::string &table_name) + : LogicalException("Table " + table_name + " not found.") + { + } + + TableNotFoundException(const std::string &table_name, const std::string &reference_name) + : LogicalException("Can not resolve table reference " + table_name + " in statement " + reference_name + ".") + { + } + + ~TableNotFoundException() override = default; +}; + +class CanNotResolveColumnException final : public LogicalException +{ + public: + CanNotResolveColumnException(const std::string &column_name, const std::string &statement) + : LogicalException("Can not resolve attribute reference " + column_name + " to a table in statement " + + statement + ".") + { + } + + explicit CanNotResolveColumnException(const std::string &column_name) + : LogicalException("Can not resolve attribute reference " + column_name + ".") + { + } + + ~CanNotResolveColumnException() override = default; +}; + +class NoUniqueReferenceException final : public LogicalException +{ + public: + NoUniqueReferenceException(const std::string &attribute_name, const std::string &table1, const std::string &table2) + : LogicalException("Can not uniquely reference attribute " + attribute_name + ". Both table " + table1 + + " and " + table2 + " include this attribute!") + { + } + + ~NoUniqueReferenceException() override = default; +}; + +class CanNotCreateTableException final : public LogicalException +{ + public: + CanNotCreateTableException() : LogicalException("Can not create table.") + { + } + + ~CanNotCreateTableException() override = default; +}; + +class CanNotCreateIndexException final : public LogicalException +{ + public: + CanNotCreateIndexException() : LogicalException("Can not create index.") + { + } + + ~CanNotCreateIndexException() override = default; +}; + +class CanNotInsertException final : public LogicalException +{ + public: + CanNotInsertException() : LogicalException("Can not insert.") + { + } + + ~CanNotInsertException() override = default; +}; +} // namespace beedb::exception \ No newline at end of file diff --git a/include/exception/parser_exception.h b/include/exception/parser_exception.h new file mode 100644 index 0000000..d09fadb --- /dev/null +++ b/include/exception/parser_exception.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "exception.h" +#include + +namespace beedb::exception +{ +class ParserException : public DatabaseException +{ + public: + explicit ParserException(const std::string &message) : DatabaseException(DatabaseException::Parser, message) + { + } + ~ParserException() override = default; +}; + +class SqlException final : public ParserException +{ + public: + SqlException(const std::string &parser_error, const std::size_t line, const std::size_t column) + : ParserException(parser_error + " in line " + std::to_string(line) + ":" + std::to_string(column) + ".") + { + } + + ~SqlException() override = default; +}; + +class UnsupportedStatementException final : public ParserException +{ + public: + explicit UnsupportedStatementException(const std::string &statement) + : ParserException(statement + " is not supported.") + { + } + + ~UnsupportedStatementException() override = default; +}; + +class CanNotConvertNullptrException final : public ParserException +{ + public: + CanNotConvertNullptrException() : ParserException("Can not convert nulltptr expression.") + { + } + + ~CanNotConvertNullptrException() override = default; +}; + +class UnsupportedOperatorException final : public ParserException +{ + public: + explicit UnsupportedOperatorException(const std::string &operator_name) + : ParserException("Unsupported predicate operator: " + operator_name + ".") + { + } + + ~UnsupportedOperatorException() override = default; +}; + +class UnsupportedColumnType final : public ParserException +{ + public: + UnsupportedColumnType() : ParserException("Unsupported column type.") + { + } + + ~UnsupportedColumnType() override = default; +}; +} // namespace beedb::exception \ No newline at end of file diff --git a/include/execution/add_to_index_operator.h b/include/execution/add_to_index_operator.h new file mode 100644 index 0000000..d7bed84 --- /dev/null +++ b/include/execution/add_to_index_operator.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "unary_operator.h" +#include +#include + +namespace beedb::execution +{ +/** + * Operator for adding tuples to an existing index. + */ +class AddToIndexOperator final : public UnaryOperator +{ + public: + AddToIndexOperator(concurrency::Transaction *transaction, const std::uint32_t column_index, + const std::shared_ptr index); + ~AddToIndexOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + }; + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + table::Schema _schema; + const std::uint32_t _column_index; + const std::shared_ptr _index; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/aggregate_operator.h b/include/execution/aggregate_operator.h new file mode 100644 index 0000000..39a226d --- /dev/null +++ b/include/execution/aggregate_operator.h @@ -0,0 +1,145 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "unary_operator.h" +#include +#include +#include +#include
+#include +#include +#include + +namespace beedb::execution +{ +class GroupByKey +{ + public: + GroupByKey(const std::vector> &grouped_columns, + const std::uint16_t grouped_row_size, const table::Tuple &tuple) + { + _data = new std::byte[grouped_row_size]; + auto current_index = 0u; + for (const auto &[column_id, _] : grouped_columns) + { + const auto &column = tuple.schema().column(column_id); + const auto size = column.type().size(); + std::memcpy(&_data[current_index], &tuple.data()[tuple.schema().offset(column_id)], size); + current_index += size; + } + } + + GroupByKey(const GroupByKey &) = delete; + + GroupByKey(GroupByKey &&other) noexcept : _data(other._data) + { + other._data = nullptr; + } + + ~GroupByKey() + { + delete[] _data; + } + + bool operator==(const GroupByKey &other) const + { + return std::strcmp(reinterpret_cast(_data), reinterpret_cast(other._data)) == 0; + } + + explicit operator std::string_view() const + { + return std::string_view{reinterpret_cast(this->_data)}; + } + + private: + std::byte *_data; +}; + +class AggregateOperator : public UnaryOperator +{ + public: + AggregateOperator(concurrency::Transaction *transaction, table::Schema &&schema, + std::vector> &&groups, + std::unordered_map> &&aggregators); + ~AggregateOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + // The output schema, generated by this operator. + const table::Schema _schema; + + // List of [output tuple schema index, aggregator]. The aggregator will produce + // a value for the tuple at the given index. + std::vector>> _aggregators; + + // Map of child attribute index to this attribute index. + std::vector> _group_attribute_map; + + // Because next() will be called multiple times, we have to + // remember if we have grouped, yet. + bool _has_grouped = false; + + // Because next() will be called multiple times, we have to + // remember if we have aggregated, yet. + bool _has_aggregated = false; + + // List of grouped tiles. May be empty, even if we have grouped. + std::vector _tiles; + + // Because next() will be called multiple times, we have to remember + // the index of the tile we want to aggregate. + std::size_t _current_tile = 0u; + + /** + * Groups the tuples returned by the given child into "tiles", which are + * memory tables, containing all tuples with the identical group key. + * + * @param group_attribute_map List of indices to group. + * @param source Child that produces tuples. + * @return List of tiles. + */ + static std::vector group( + const std::vector> &group_attribute_map, + const std::unique_ptr &source); +}; +} // namespace beedb::execution + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::execution::GroupByKey &key) const + { + return std::hash()(static_cast(key)); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/execution/aggregator.h b/include/execution/aggregator.h new file mode 100644 index 0000000..bb2de4b --- /dev/null +++ b/include/execution/aggregator.h @@ -0,0 +1,222 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include
+#include
+ +namespace beedb::execution +{ +class AggregatorInterface +{ + public: + explicit AggregatorInterface(const std::uint16_t schema_index) : _schema_index(schema_index) + { + } + virtual ~AggregatorInterface() = default; + + virtual void aggregate(const table::Tuple &tuple) = 0; + virtual table::Value value() = 0; + virtual void reset() = 0; + + protected: + [[nodiscard]] std::uint16_t schema_index() const + { + return _schema_index; + } + + private: + const std::uint16_t _schema_index; +}; + +class CountAggregator : public AggregatorInterface +{ + public: + explicit CountAggregator(const std::uint16_t schema_index) : AggregatorInterface(schema_index) + { + } + ~CountAggregator() override = default; + + void aggregate(const table::Tuple &) override + { + ++_count; + } + + table::Value value() override + { + return table::Value(table::Type{table::Type::LONG}, {static_cast(_count)}); + } + + void reset() override + { + _count = 0u; + } + + private: + std::uint64_t _count = 0u; +}; + +class SumAggregator : public AggregatorInterface +{ + public: + SumAggregator(const std::uint16_t schema_index, const table::Type::Id type) + : AggregatorInterface(schema_index), _type(type), _sum(table::Value::make_zero(_type)) + { + } + ~SumAggregator() override = default; + + void aggregate(const table::Tuple &tuple) override + { + _sum += tuple.get(AggregatorInterface::schema_index()); + } + + table::Value value() override + { + return _sum; + } + + void reset() override + { + _sum = table::Value::make_zero(_type); + } + + private: + const table::Type::Id _type; + table::Value _sum; +}; + +class AverageAggregator : public AggregatorInterface +{ + public: + AverageAggregator(const std::uint16_t schema_index, const table::Type::Id type) + : AggregatorInterface(schema_index), _type(type), _sum(table::Value::make_zero(_type)) + { + } + ~AverageAggregator() override = default; + + void aggregate(const table::Tuple &tuple) override + { + _sum += tuple.get(AggregatorInterface::schema_index()); + _count++; + } + + table::Value value() override + { + if (_type == table::Type::Id::LONG) + { + return table::Value(table::Type{table::Type::Id::DECIMAL}, + _sum.get() / static_cast(_count)); + } + else if (_type == table::Type::Id::INT) + { + return table::Value(table::Type{table::Type::Id::DECIMAL}, + _sum.get() / static_cast(_count)); + } + else if (_type == table::Type::Id::DECIMAL) + { + return table::Value(table::Type{table::Type::Id::DECIMAL}, + _sum.get() / static_cast(_count)); + } + + return table::Value::make_zero(_type); + } + + void reset() override + { + _sum = table::Value::make_zero(_type); + _count = 0u; + } + + private: + const table::Type::Id _type; + table::Value _sum; + std::uint64_t _count = 0u; +}; + +class MinAggregator : public AggregatorInterface +{ + public: + MinAggregator(const std::uint16_t schema_index, const table::Type::Id type) + : AggregatorInterface(schema_index), _type(type), _min(table::Value::make_null(_type)) + { + } + ~MinAggregator() override = default; + + void aggregate(const table::Tuple &tuple) override + { + const auto value = tuple.get(AggregatorInterface::schema_index()); + if ((value == nullptr) == false && (_min == nullptr || value < _min)) + { + _min = value; + } + } + + table::Value value() override + { + return _min; + } + + void reset() override + { + _min = table::Value::make_null(_type); + } + + private: + const table::Type::Id _type; + table::Value _min; +}; + +class MaxAggregator : public AggregatorInterface +{ + public: + MaxAggregator(const std::uint16_t schema_index, const table::Type::Id type) + : AggregatorInterface(schema_index), _type(type), _max(table::Value::make_null(_type)) + { + } + ~MaxAggregator() override = default; + + void aggregate(const table::Tuple &tuple) override + { + const auto value = tuple.get(AggregatorInterface::schema_index()); + if ((value == nullptr) == false && (_max == nullptr || value > _max)) + { + _max = value; + } + } + + table::Value value() override + { + return _max; + } + + void reset() override + { + _max = table::Value::make_null(_type); + } + + private: + const table::Type::Id _type; + table::Value _max; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/arithmetic_calculator.h b/include/execution/arithmetic_calculator.h new file mode 100644 index 0000000..3f8e51c --- /dev/null +++ b/include/execution/arithmetic_calculator.h @@ -0,0 +1,211 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include
+#include
+ +namespace beedb::execution +{ +/** + * TODO: + * The operation(+,-,*,/) on two values tries to find the matching underlying type for both. + * We could make a huge optimization by injecting the correct types and only do the operator + * for one type (depending on the type of one of the values), which will never fail if both + * hold the same type. + */ +class ArithmeticCalculatorInterface +{ + public: + enum Type + { + Add, + Sub, + Multiply, + Divide + }; + ArithmeticCalculatorInterface() = default; + virtual ~ArithmeticCalculatorInterface() = default; + + virtual table::Value calculate(const table::Tuple &tuple) = 0; +}; + +template +class ValueArithmeticCalculator final : public ArithmeticCalculatorInterface +{ + public: + ValueArithmeticCalculator(table::Value &&left_value, table::Value &&right_value) + : _result_value(calculate(std::move(left_value), std::move(right_value))) + { + } + ~ValueArithmeticCalculator() override = default; + + table::Value calculate(const table::Tuple &) override + { + return _result_value; + } + + private: + table::Value _result_value; + + table::Value calculate(table::Value &&left_value, table::Value &&right_value) + { + if constexpr (T == ArithmeticCalculatorInterface::Add) + { + return left_value + right_value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Sub) + { + return left_value - right_value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Multiply) + { + return left_value * right_value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Divide) + { + return left_value / right_value; + } + + return std::move(left_value); + } +}; + +template +class AttributeValueArithmeticCalculator final : public ArithmeticCalculatorInterface +{ + public: + AttributeValueArithmeticCalculator(std::uint16_t index, table::Value &&value) + : _index(index), _value(std::move(value)) + { + } + ~AttributeValueArithmeticCalculator() override = default; + + table::Value calculate(const table::Tuple &tuple) override + { + if constexpr (T == ArithmeticCalculatorInterface::Add) + { + return tuple.get(_index) + _value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Sub) + { + return tuple.get(_index) - _value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Multiply) + { + return tuple.get(_index) * _value; + } + else if constexpr (T == ArithmeticCalculatorInterface::Divide) + { + return tuple.get(_index) / _value; + } + else + { + return tuple.get(_index); + } + } + + private: + const std::uint16_t _index; + const table::Value _value; +}; + +template +class ValueAttributeArithmeticCalculator final : public ArithmeticCalculatorInterface +{ + public: + ValueAttributeArithmeticCalculator(table::Value &&value, std::uint16_t index) + : _value(std::move(value)), _index(index) + { + } + ~ValueAttributeArithmeticCalculator() override = default; + + table::Value calculate(const table::Tuple &tuple) override + { + if constexpr (T == ArithmeticCalculatorInterface::Add) + { + return _value + tuple.get(_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Sub) + { + return _value - tuple.get(_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Multiply) + { + return _value * tuple.get(_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Divide) + { + return _value / tuple.get(_index); + } + else + { + return _value; + } + } + + private: + const table::Value _value; + const std::uint16_t _index; +}; + +template +class AttributeArithmeticCalculator final : public ArithmeticCalculatorInterface +{ + public: + AttributeArithmeticCalculator(std::uint16_t left_index, std::uint16_t right_index) + : _left_index(left_index), _right_index(right_index) + { + } + ~AttributeArithmeticCalculator() override = default; + + table::Value calculate(const table::Tuple &tuple) override + { + if constexpr (T == ArithmeticCalculatorInterface::Add) + { + return tuple.get(_left_index) + tuple.get(_right_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Sub) + { + return tuple.get(_left_index) - tuple.get(_right_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Multiply) + { + return tuple.get(_left_index) * tuple.get(_right_index); + } + else if constexpr (T == ArithmeticCalculatorInterface::Divide) + { + return tuple.get(_left_index) / tuple.get(_right_index); + } + else + { + return tuple.get(_left_index); + } + } + + private: + const std::uint16_t _left_index; + const std::uint16_t _right_index; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/arithmetic_operator.h b/include/execution/arithmetic_operator.h new file mode 100644 index 0000000..1a4f4fc --- /dev/null +++ b/include/execution/arithmetic_operator.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "arithmetic_calculator.h" +#include "unary_operator.h" +#include +#include + +namespace beedb::execution +{ +class ArithmeticOperator : public UnaryOperator +{ + public: + ArithmeticOperator( + concurrency::Transaction *transaction, table::Schema &&schema, + std::unordered_map> &&arithmetic_calculators); + ~ArithmeticOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + table::Schema _schema; + std::unordered_map> _arithmetic_calculators; + std::unordered_map _child_schema_map; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/binary_operator.h b/include/execution/binary_operator.h new file mode 100644 index 0000000..187f566 --- /dev/null +++ b/include/execution/binary_operator.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "operator_interface.h" +#include +#include
+#include
+ +namespace beedb::execution +{ +/** + * Abstract operator that has to children (left and right) + * for operators JOIN-like operators. + */ +class BinaryOperator : public OperatorInterface +{ + public: + explicit BinaryOperator(concurrency::Transaction *transaction) : OperatorInterface(transaction) + { + } + ~BinaryOperator() override = default; + + void left_child(std::unique_ptr child) + { + _left_child = std::move(child); + } + void right_child(std::unique_ptr child) + { + _right_child = std::move(child); + } + + [[nodiscard]] const std::unique_ptr &left_child() const + { + return _left_child; + } + [[nodiscard]] const std::unique_ptr &right_child() const + { + return _right_child; + } + + protected: + [[nodiscard]] table::Tuple combine(const table::Schema &new_schema, const table::Tuple &left, + const table::Tuple &right) const; + + private: + std::unique_ptr _left_child; + std::unique_ptr _right_child; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/build_index_operator.h b/include/execution/build_index_operator.h new file mode 100644 index 0000000..2284ce0 --- /dev/null +++ b/include/execution/build_index_operator.h @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "binary_operator.h" +#include +#include +#include +#include +#include
+ +namespace beedb::execution +{ +/** + * Fills an existing index with values. + * May have two children: One operator creating the index (left) + * which is called once and one operator (right) which provides the + * data for the index. + */ +class BuildIndexOperator final : public BinaryOperator +{ + public: + BuildIndexOperator(Database &database, concurrency::Transaction *transaction, const std::string &table_name, + const table::Schema::ColumnIndexType column_index, const std::string &index_name); + ~BuildIndexOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + void create_index_operator(std::unique_ptr op) + { + this->left_child(std::move(op)); + } + void data_operator(std::unique_ptr op) + { + this->right_child(std::move(op)); + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema; + Database &_database; + const std::string _table_name; + const std::uint32_t _column_index; + const std::string _index_name; +}; +} // namespace beedb::execution diff --git a/include/execution/create_index_operator.h b/include/execution/create_index_operator.h new file mode 100644 index 0000000..08e3fba --- /dev/null +++ b/include/execution/create_index_operator.h @@ -0,0 +1,78 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "operator_interface.h" +#include +#include +#include +#include
+#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Operator that creates and persists a new index for a given + * column with given index attributes (type, unique or non-unique). + */ +class CreateIndexOperator final : public OperatorInterface +{ + public: + CreateIndexOperator(Database &database, concurrency::Transaction *transaction, const std::string &table_name, + const std::string &column_name, const std::string &index_name, const bool is_unique, + const index::Type type); + ~CreateIndexOperator() override = default; + + void open() override + { + } + + util::optional next() override; + + void close() override + { + } + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema; + Database &_database; + const std::string _table_name; + const std::string _column_name; + const std::string _index_name; + const bool _is_unique; + const index::Type _index_type; +}; +} // namespace beedb::execution diff --git a/include/execution/create_table_operator.h b/include/execution/create_table_operator.h new file mode 100644 index 0000000..58f72bd --- /dev/null +++ b/include/execution/create_table_operator.h @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "operator_interface.h" +#include +#include +#include
+#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Creates and persists a new table. + */ +class CreateTableOperator final : public OperatorInterface +{ + public: + CreateTableOperator(Database &database, concurrency::Transaction *transaction, table::Schema &&schema_to_create); + + ~CreateTableOperator() override = default; + + void open() override{}; + util::optional next() override; + void close() override{}; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + }; + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + Database &_database; + const table::Schema _schema; + const table::Schema _schema_to_create; +}; +} // namespace beedb::execution diff --git a/include/execution/cross_product_operator.h b/include/execution/cross_product_operator.h new file mode 100644 index 0000000..7dc3d7d --- /dev/null +++ b/include/execution/cross_product_operator.h @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "binary_operator.h" +#include +#include
+#include
+#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Generates a cross product of two sources. + */ +class CrossProductOperator final : public BinaryOperator +{ + public: + CrossProductOperator(concurrency::Transaction *transaction, table::Schema &&schema); + ~CrossProductOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema _schema; + util::optional _next_left_tuple; +}; +} // namespace beedb::execution diff --git a/include/execution/delete_operator.h b/include/execution/delete_operator.h new file mode 100644 index 0000000..2ca4c51 --- /dev/null +++ b/include/execution/delete_operator.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "unary_operator.h" +#include +#include +#include
+#include
+#include +#include + +namespace beedb::execution +{ +class DeleteOperator : public UnaryOperator +{ + public: + DeleteOperator(concurrency::Transaction *transaction, table::Table &table, + table::TableDiskManager &table_disk_manager, buffer::Manager &buffer_manager) + : UnaryOperator(transaction), _table(table), _table_disk_manager(table_disk_manager), + _buffer_manager(buffer_manager) + { + } + ~DeleteOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _table.schema(); + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + table::Table &_table; + table::TableDiskManager _table_disk_manager; + buffer::Manager &_buffer_manager; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/hash_join_operator.h b/include/execution/hash_join_operator.h new file mode 100644 index 0000000..c0368dd --- /dev/null +++ b/include/execution/hash_join_operator.h @@ -0,0 +1,109 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "binary_operator.h" +#include "tuple_buffer.h" +#include +#include +#include
+#include
+#include
+#include +#include +#include + +namespace beedb::execution +{ +/** + * Hash table for hash join. + */ +class HashTable +{ + public: + explicit HashTable(const std::uint32_t key_index) : _key_index(key_index) + { + } + + ~HashTable() = default; + + bool contains(const table::Value &key) + { + return this->_map.find(key) != _map.end(); + } + + void put(const table::Tuple &tuple) + { + table::Tuple in_memory_tuple(tuple); + this->_map[tuple.get(_key_index)].push_back(std::move(in_memory_tuple)); + } + + const std::vector &get(const table::Value &key) + { + return _map[key]; + } + + private: + const std::uint32_t _key_index; + std::unordered_map> _map; +}; + +/** + * Operator that joins two sources using a hash table over + * the left source. + */ +class HashJoinOperator final : public BinaryOperator +{ + public: + HashJoinOperator(concurrency::Transaction *transaction, table::Schema &&schema, const std::uint32_t left_index, + const std::uint32_t right_index); + ~HashJoinOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema _schema; + const std::uint32_t _left_index; + const std::uint32_t _right_index; + HashTable _hash_table; + bool _is_built = false; + TupleBuffer _tuple_buffer; + + /** + * Builds the hash table. + */ + void build_hash_table(); + + /** + * Probes the hash table. + */ + util::optional probe_hash_table(); +}; +} // namespace beedb::execution diff --git a/include/execution/index_scan_operator.h b/include/execution/index_scan_operator.h new file mode 100644 index 0000000..029b0b7 --- /dev/null +++ b/include/execution/index_scan_operator.h @@ -0,0 +1,141 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "operator_interface.h" +#include "tuple_buffer.h" +#include +#include +#include +#include +#include +#include
+#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Key (range) that have to be looked up in the. + * May be a range or a single key. + */ +class KeyRange +{ + public: + explicit KeyRange(const std::int64_t single_key) : _from(single_key), _to(std::numeric_limits::max()) + { + } + + KeyRange(const std::int64_t from, const std::int64_t to) : _from(from), _to(to) + { + } + + ~KeyRange() = default; + + [[nodiscard]] bool is_single_key() const + { + return _to == std::numeric_limits::max(); + } + [[nodiscard]] std::int64_t single_key() const + { + return _from; + } + [[nodiscard]] std::int64_t from() const + { + return _from; + } + [[nodiscard]] std::int64_t to() const + { + return _to; + } + + bool operator<(const KeyRange &other) const + { + return _from < other._from; + } + + bool operator==(const KeyRange &other) const + { + return _from == other._from && _to == other._to; + } + + private: + const std::int64_t _from; + const std::int64_t _to; +}; +} // namespace beedb::execution + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::execution::KeyRange &range) const + { + return std::hash()(range.from()) ^ std::hash()(range.to()); + } +}; +} // namespace std + +namespace beedb::execution +{ + +/** + * Takes an index and keys to be looked up in the index + * and scans only over pages found in the index instead + * of scanning all pages from the table. + */ +class IndexScanOperator final : public OperatorInterface +{ + public: + IndexScanOperator(concurrency::Transaction *transaction, const std::uint32_t scan_page_limit, + const table::Schema &schema, buffer::Manager &buffer_manager, + table::TableDiskManager &table_disk_manager, std::unordered_set &&key_ranges, + std::shared_ptr index); + ~IndexScanOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const std::uint32_t _scan_page_limit; + const table::Schema _schema; + buffer::Manager &_buffer_manager; + table::TableDiskManager &_table_disk_manager; + std::unordered_set _key_ranges; + std::shared_ptr _index; + + std::queue _pages_to_scan; + std::vector _pinned_pages; + + TupleBuffer _buffer; +}; +} // namespace beedb::execution diff --git a/include/execution/insert_operator.h b/include/execution/insert_operator.h new file mode 100644 index 0000000..d335aa8 --- /dev/null +++ b/include/execution/insert_operator.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "unary_operator.h" +#include +#include +#include +#include
+#include
+#include
+ +namespace beedb::execution +{ +/** + * Inserts all tuples provided by the children operator. + * The child may be a tuple buffer or a subquery. + */ +class InsertOperator final : public UnaryOperator +{ + public: + InsertOperator(concurrency::Transaction *transaction, buffer::Manager &buffer_manager, + table::TableDiskManager &table_disk_manager, statistic::SystemStatistics &statistics, + table::Table &table); + ~InsertOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema; + buffer::Manager &_buffer_manager; + table::TableDiskManager &_table_disk_manager; + statistic::SystemStatistics &_statistics; + table::Table &_table; + storage::Page::id_t _last_pinned_page = storage::Page::INVALID_PAGE_ID; +}; +} // namespace beedb::execution diff --git a/include/execution/limit_operator.h b/include/execution/limit_operator.h new file mode 100644 index 0000000..3faadd6 --- /dev/null +++ b/include/execution/limit_operator.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "unary_operator.h" +#include +#include
+#include
+#include + +namespace beedb::execution +{ +/** + * Limits the output result. + */ +class LimitOperator final : public UnaryOperator +{ + public: + LimitOperator(concurrency::Transaction *transaction, const table::Schema &schema, const std::uint64_t limit, + const std::uint64_t offset); + + ~LimitOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + }; + + private: + const table::Schema &_schema; + const std::uint64_t _limit; + const std::uint64_t _offset; + bool _has_skipped = false; + std::uint64_t _count = 0; +}; +} // namespace beedb::execution diff --git a/include/execution/nested_loops_join_operator.h b/include/execution/nested_loops_join_operator.h new file mode 100644 index 0000000..a057d51 --- /dev/null +++ b/include/execution/nested_loops_join_operator.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "binary_operator.h" +#include "predicate_matcher.h" +#include +#include
+#include
+#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Joins two sources by outer-looping over the left + * and inner looping over the right children tuples. + */ +class NestedLoopsJoinOperator final : public BinaryOperator +{ + public: + NestedLoopsJoinOperator(concurrency::Transaction *transaction, table::Schema &&schema, + std::unique_ptr &&predicate_matcher); + ~NestedLoopsJoinOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema _schema; + std::unique_ptr _predicate_matcher; + util::optional _next_left_tuple; + + [[nodiscard]] bool matches(const table::Tuple &left, const table::Tuple &right) + { + return this->_predicate_matcher->matches(left, right); + } +}; +} // namespace beedb::execution diff --git a/include/execution/operator_interface.h b/include/execution/operator_interface.h new file mode 100644 index 0000000..d2e1fa5 --- /dev/null +++ b/include/execution/operator_interface.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include
+#include
+#include + +namespace beedb::execution +{ +/** + * Interface for all physical execution operators. + * The interface is volcano-style (using open, next, and close). + */ +class OperatorInterface +{ + public: + explicit OperatorInterface(concurrency::Transaction *transaction) : _transaction(transaction) + { + } + virtual ~OperatorInterface() = default; + + virtual void open() = 0; + virtual util::optional next() = 0; + virtual void close() = 0; + + [[nodiscard]] virtual bool yields_data() const + { + return true; + } + + [[nodiscard]] virtual const table::Schema &schema() const = 0; + + protected: + [[nodiscard]] concurrency::Transaction *transaction() const + { + return _transaction; + } + + private: + concurrency::Transaction *_transaction; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/order_operator.h b/include/execution/order_operator.h new file mode 100644 index 0000000..72ded71 --- /dev/null +++ b/include/execution/order_operator.h @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "unary_operator.h" +#include +#include +#include
+#include
+#include +#include + +namespace beedb::execution +{ +/** + * Comparator for comparing two tuples during + * sort. + */ +class TupleComparator +{ + public: + explicit TupleComparator(const std::vector> &indices) : _indices(indices) + { + } + ~TupleComparator() = default; + + bool operator()(const table::Tuple &left, const table::Tuple &right) const + { + for (auto &[index, is_ascending] : _indices) + { + const auto value_left = left.get(index); + const auto value_right = right.get(index); + if (is_ascending) + { + if (value_left < value_right) + { + return true; + } + else if (value_left > value_right) + { + return false; + } + } + else + { + if (value_right < value_left) + { + return true; + } + else if (value_right > value_left) + { + return false; + } + } + } + + return false; + } + + private: + const std::vector> &_indices; +}; + +/** + * Sorts the result provided by a child using quicksort. + */ +class OrderOperator final : public UnaryOperator +{ + public: + OrderOperator(concurrency::Transaction *transaction, const table::Schema &schema, + std::vector> &&order_columns); + ~OrderOperator() override = default; + + void open() override; + + util::optional next() override; + + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema &_schema; + const std::vector> _order_columns; + std::unique_ptr _result_table; + std::size_t _stack_index = 0u; +}; +} // namespace beedb::execution diff --git a/include/execution/predicate_matcher.h b/include/execution/predicate_matcher.h new file mode 100644 index 0000000..8a2f782 --- /dev/null +++ b/include/execution/predicate_matcher.h @@ -0,0 +1,299 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include + +#include
+#include
+ +namespace beedb::execution +{ + +/** + * Interface for predicate matcher. The predicate matcher + * takes one or two tuples and compares them with a constant + * or a value from another tuple. + */ +class PredicateMatcherInterface +{ + public: + enum Comparison + { + EQ, + LE, + LT, + GE, + GT, + NEQ + }; + virtual ~PredicateMatcherInterface() = default; + virtual bool matches(const table::Tuple &tuple) = 0; + virtual bool matches(const table::Tuple &left, const table::Tuple &right) = 0; + virtual std::unique_ptr clone() = 0; +}; + +/** + * The given tuple will always match. + */ +class AlwaysTrueMatcher final : public PredicateMatcherInterface +{ + public: + AlwaysTrueMatcher() = default; + + ~AlwaysTrueMatcher() override = default; + + bool matches(const table::Tuple &) override + { + return true; + } + + bool matches(const table::Tuple &, const table::Tuple &) override + { + return true; + } + + std::unique_ptr clone() override + { + return std::make_unique(); + } +}; + +/** + * Takes two predicate matchers p1 and p2 and connects them using AND. + * Tuple matches when p1 AND p2 matches. + */ +class AndMatcher final : public PredicateMatcherInterface +{ + public: + AndMatcher(std::unique_ptr &&left, std::unique_ptr &&right) + : _left(std::move(left)), _right(std::move(right)) + { + } + + ~AndMatcher() override = default; + + bool matches(const table::Tuple &tuple) override + { + return _left->matches(tuple) && _right->matches(tuple); + } + + bool matches(const table::Tuple &left, const table::Tuple &right) override + { + return _left->matches(left, right) && _right->matches(left, right); + } + + std::unique_ptr clone() override + { + return std::make_unique(_left->clone(), _right->clone()); + } + + private: + std::unique_ptr _left; + std::unique_ptr _right; +}; + +/** + * Takes two predicate matchers p1 and p2 and connects them using OR. + * Tuple matches when p1 OR p2 matches. + */ +class OrMatcher final : public PredicateMatcherInterface +{ + public: + OrMatcher(std::unique_ptr &&left, std::unique_ptr &&right) + : _left(std::move(left)), _right(std::move(right)) + { + } + + ~OrMatcher() override = default; + + bool matches(const table::Tuple &tuple) override + { + return _left->matches(tuple) || _right->matches(tuple); + } + + bool matches(const table::Tuple &left, const table::Tuple &right) override + { + return _left->matches(left, right) || _right->matches(left, right); + } + + std::unique_ptr clone() override + { + return std::make_unique(_left->clone(), _right->clone()); + } + + private: + std::unique_ptr _left; + std::unique_ptr _right; +}; + +/** + * Compares a specific column in a tuple with a constant. + */ +template class AttributeValueMatcher final : public PredicateMatcherInterface +{ + public: + AttributeValueMatcher(const table::Schema::ColumnIndexType schema_index, table::Value &&value) + : _schema_index(schema_index), _value(std::move(value)) + { + } + + AttributeValueMatcher(const table::Schema::ColumnIndexType schema_index, const table::Value &value) + : _schema_index(schema_index), _value(value) + { + } + + ~AttributeValueMatcher() override = default; + + bool matches(const table::Tuple &tuple) override + { + if constexpr (C == EQ) + { + return tuple.get(_schema_index) == _value; + } + else if constexpr (C == LE) + { + return tuple.get(_schema_index) <= _value; + } + else if constexpr (C == LT) + { + return tuple.get(_schema_index) < _value; + } + else if constexpr (C == GE) + { + return tuple.get(_schema_index) >= _value; + } + else if constexpr (C == GT) + { + return tuple.get(_schema_index) > _value; + } + else + { + return tuple.get(_schema_index) != _value; + } + } + + bool matches(const table::Tuple &, const table::Tuple &) override + { + return false; + } + + std::unique_ptr clone() override + { + return std::make_unique>(_schema_index, _value); + } + + protected: + const table::Schema::ColumnIndexType _schema_index; + const table::Value _value; +}; + +/** + * Compares two columns in a tuple or one column of two tuples. + */ +template class AttributeMatcher final : public PredicateMatcherInterface +{ + public: + AttributeMatcher(const table::Schema::ColumnIndexType schema_index_left, + const table::Schema::ColumnIndexType schema_index_right) + : _schema_index_left(schema_index_left), _schema_index_right(schema_index_right) + { + } + + ~AttributeMatcher() override = default; + + bool matches(const table::Tuple &tuple) override + { + if constexpr (C == EQ) + { + return tuple.get(_schema_index_left) == tuple.get(_schema_index_right); + } + else if constexpr (C == LE) + { + return tuple.get(_schema_index_left) <= tuple.get(_schema_index_right); + } + else if constexpr (C == LT) + { + return tuple.get(_schema_index_left) < tuple.get(_schema_index_right); + } + else if constexpr (C == GE) + { + return tuple.get(_schema_index_left) >= tuple.get(_schema_index_right); + } + else if constexpr (C == GT) + { + return tuple.get(_schema_index_left) > tuple.get(_schema_index_right); + } + else + { + return tuple.get(_schema_index_left) != tuple.get(_schema_index_right); + } + } + + bool matches(const table::Tuple &left, const table::Tuple &right) override + { + if constexpr (C == EQ) + { + return left.get(_schema_index_left) == right.get(_schema_index_right); + } + else if constexpr (C == LE) + { + return left.get(_schema_index_left) <= right.get(_schema_index_right); + } + else if constexpr (C == LT) + { + return left.get(_schema_index_left) < right.get(_schema_index_right); + } + else if constexpr (C == GE) + { + return left.get(_schema_index_left) >= right.get(_schema_index_right); + } + else if constexpr (C == GT) + { + return left.get(_schema_index_left) > right.get(_schema_index_right); + } + else + { + return left.get(_schema_index_left) != right.get(_schema_index_right); + } + } + + std::unique_ptr clone() override + { + return std::make_unique>(_schema_index_left, _schema_index_right); + } + + [[nodiscard]] table::Schema::ColumnIndexType left_index() const + { + return _schema_index_left; + } + + [[nodiscard]] table::Schema::ColumnIndexType right_index() const + { + return _schema_index_right; + } + + protected: + const table::Schema::ColumnIndexType _schema_index_left; + const table::Schema::ColumnIndexType _schema_index_right; +}; +} // namespace beedb::execution diff --git a/include/execution/projection_operator.h b/include/execution/projection_operator.h new file mode 100644 index 0000000..30498f2 --- /dev/null +++ b/include/execution/projection_operator.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "unary_operator.h" +#include +#include +#include
+#include +#include + +namespace beedb::execution +{ +/** + * Applies a new schema to all tuples provided by the children. + * May hide or re-order columns. + */ +class ProjectionOperator final : public UnaryOperator +{ + public: + ProjectionOperator(concurrency::Transaction *transaction, table::Schema &&schema); + ~ProjectionOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema _schema; +}; +} // namespace beedb::execution diff --git a/include/execution/selection_operator.h b/include/execution/selection_operator.h new file mode 100644 index 0000000..28cea25 --- /dev/null +++ b/include/execution/selection_operator.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "predicate_matcher.h" +#include "unary_operator.h" +#include +#include +#include +#include
+#include
+#include
+ +namespace beedb::execution +{ +/** + * Selects tuples matching a given predicate. + * Some tuples may be filtered. + */ +class SelectionOperator final : public UnaryOperator +{ + public: + SelectionOperator(concurrency::Transaction *transaction, const table::Schema &schema, + std::unique_ptr &&predicate_matcher); + + ~SelectionOperator() override = default; + + void open() override; + + util::optional next() override; + + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const table::Schema &_schema; + std::unique_ptr _predicate_matcher; + + [[nodiscard]] bool matches(const table::Tuple &tuple) + { + return this->_predicate_matcher->matches(tuple); + } +}; +} // namespace beedb::execution diff --git a/include/execution/sequential_scan_operator.h b/include/execution/sequential_scan_operator.h new file mode 100644 index 0000000..d7ec872 --- /dev/null +++ b/include/execution/sequential_scan_operator.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "tuple_buffer.h" +#include "unary_operator.h" +#include +#include +#include
+#include
+#include
+#include + +namespace beedb::execution +{ +/** + * Scans all pages of a given table and returns all tuples. + */ +class SequentialScanOperator final : public UnaryOperator +{ + public: + SequentialScanOperator(concurrency::Transaction *transaction, std::uint32_t scan_page_limit, table::Schema &&schema, + buffer::Manager &buffer_manager, table::TableDiskManager &table_disk_manager, + const table::Table &table); + ~SequentialScanOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + private: + const std::uint32_t _scan_page_limit; + const table::Schema _schema; + buffer::Manager &_buffer_manager; + table::TableDiskManager &_table_disk_manager; + const table::Table &_table; + + storage::Page::id_t _next_page_id_to_scan = storage::Page::INVALID_PAGE_ID; + std::vector _pinned_pages; + + TupleBuffer _buffer; +}; +} // namespace beedb::execution diff --git a/include/execution/transaction_operator.h b/include/execution/transaction_operator.h new file mode 100644 index 0000000..c72e70f --- /dev/null +++ b/include/execution/transaction_operator.h @@ -0,0 +1,125 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "operator_interface.h" +#include +#include +#include + +namespace beedb::execution +{ +class BeginTransactionOperator : public OperatorInterface +{ + public: + BeginTransactionOperator(concurrency::TransactionManager &transaction_manager, + concurrency::TransactionCallback &transaction_callback) + : OperatorInterface(nullptr), _transaction_manager(transaction_manager), + _transaction_callback(transaction_callback) + { + } + ~BeginTransactionOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema = {}; + concurrency::TransactionManager &_transaction_manager; + concurrency::TransactionCallback &_transaction_callback; +}; + +class AbortTransactionOperator : public OperatorInterface +{ + public: + AbortTransactionOperator(concurrency::TransactionManager &transaction_manager, + concurrency::Transaction *transaction, + concurrency::TransactionCallback &transaction_callback) + : OperatorInterface(transaction), _transaction_manager(transaction_manager), + _transaction_callback(transaction_callback) + { + } + ~AbortTransactionOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema = {}; + concurrency::TransactionManager &_transaction_manager; + concurrency::TransactionCallback &_transaction_callback; +}; + +class CommitTransactionOperator : public OperatorInterface +{ + public: + CommitTransactionOperator(concurrency::TransactionManager &transaction_manager, + concurrency::Transaction *transaction, + concurrency::TransactionCallback &transaction_callback) + : OperatorInterface(transaction), _transaction_manager(transaction_manager), + _transaction_callback(transaction_callback) + { + } + ~CommitTransactionOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + const table::Schema _schema = {}; + concurrency::TransactionManager &_transaction_manager; + concurrency::TransactionCallback &_transaction_callback; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/tuple_buffer.h b/include/execution/tuple_buffer.h new file mode 100644 index 0000000..dff15c5 --- /dev/null +++ b/include/execution/tuple_buffer.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include
+#include + +namespace beedb::execution +{ +/** + * Buffers tuples; needed by blocking operators. + */ +class TupleBuffer +{ + public: + TupleBuffer() = default; + ~TupleBuffer() = default; + + void add(table::Tuple &&tuple) + { + _buffer.push_back(std::move(tuple)); + } + + void add(std::vector &&tuples) + { + std::move(tuples.begin(), tuples.end(), std::back_inserter(_buffer)); + } + + [[nodiscard]] bool empty() const + { + return _buffer.empty() || _head > _buffer.size() - 1; + } + + [[nodiscard]] table::Tuple &&pop() + { + return std::move(_buffer[_head++]); + } + + void clear() + { + _buffer.clear(); + _head = 0u; + } + + private: + std::vector _buffer; + std::size_t _head = 0u; +}; +} // namespace beedb::execution diff --git a/include/execution/tuple_buffer_operator.h b/include/execution/tuple_buffer_operator.h new file mode 100644 index 0000000..d467b71 --- /dev/null +++ b/include/execution/tuple_buffer_operator.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "operator_interface.h" +#include "tuple_buffer.h" +#include
+#include
+#include + +namespace beedb::execution +{ +/** + * Buffers tuples and pops the on next(). + */ +class TupleBufferOperator final : public OperatorInterface +{ + public: + TupleBufferOperator(concurrency::Transaction *transaction, const table::Schema &schema); + ~TupleBufferOperator() override = default; + + void open() override + { + } + + util::optional next() override; + + void close() override + { + } + + [[nodiscard]] const table::Schema &schema() const override + { + return _schema; + } + + void add(table::Tuple &tuple) + { + _tuple_buffer.add(std::move(tuple)); + } + void add(std::vector &tuples) + { + _tuple_buffer.add(std::move(tuples)); + } + + private: + const table::Schema _schema; + TupleBuffer _tuple_buffer; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/unary_operator.h b/include/execution/unary_operator.h new file mode 100644 index 0000000..7a83e48 --- /dev/null +++ b/include/execution/unary_operator.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "operator_interface.h" +#include + +namespace beedb::execution +{ +/** + * Interface for operators providing a single child. + */ +class UnaryOperator : public OperatorInterface +{ + public: + explicit UnaryOperator(concurrency::Transaction *transaction) : OperatorInterface(transaction) + { + } + + ~UnaryOperator() override = default; + + void child(std::unique_ptr &&child) + { + _child = std::move(child); + } + + [[nodiscard]] const std::unique_ptr &child() const + { + return _child; + } + + private: + std::unique_ptr _child; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/execution/update_operator.h b/include/execution/update_operator.h new file mode 100644 index 0000000..815400d --- /dev/null +++ b/include/execution/update_operator.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "unary_operator.h" +#include +#include +#include
+#include
+#include +#include + +namespace beedb::execution +{ +class UpdateOperator : public UnaryOperator +{ + public: + UpdateOperator(concurrency::Transaction *transaction, table::Table &table, + table::TableDiskManager &table_disk_manager, buffer::Manager &buffer_manager, + std::vector> &&values) + : UnaryOperator(transaction), _table(table), _table_disk_manager(table_disk_manager), + _buffer_manager(buffer_manager), _new_column_values(std::move(values)) + { + } + ~UpdateOperator() override = default; + + void open() override; + util::optional next() override; + void close() override; + + [[nodiscard]] const table::Schema &schema() const override + { + return _table.schema(); + } + + [[nodiscard]] bool yields_data() const override + { + return false; + } + + private: + table::Table &_table; + table::TableDiskManager _table_disk_manager; + buffer::Manager &_buffer_manager; + std::vector> _new_column_values; +}; +} // namespace beedb::execution \ No newline at end of file diff --git a/include/expression/attribute.h b/include/expression/attribute.h new file mode 100644 index 0000000..1e50676 --- /dev/null +++ b/include/expression/attribute.h @@ -0,0 +1,130 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::expression +{ +class Attribute +{ + public: + Attribute() = default; + explicit Attribute(std::string &&column_name) : _table_name{std::nullopt}, _column_name(std::move(column_name)) + { + } + explicit Attribute(const std::string &column_name) : _table_name{std::nullopt}, _column_name(column_name) + { + } + Attribute(std::string &&table_name, std::string &&column_name) + : _table_name(std::move(table_name)), _column_name(std::move(column_name)) + { + } + Attribute(const std::string &table_name, const std::string &column_name) + : _table_name(table_name), _column_name(column_name) + { + } + Attribute(const std::string &table_name, std::string &&column_name) + : _table_name(table_name), _column_name(std::move(column_name)) + { + } + Attribute(std::optional &&table_name, std::string &&column_name) + : _table_name(std::move(table_name)), _column_name(std::move(column_name)) + { + } + Attribute(std::optional &&table_name, std::string &&column_name, const bool print_table_name) + : _table_name(std::move(table_name)), _column_name(std::move(column_name)), _print_table_name(print_table_name) + { + } + Attribute(const std::optional &table_name, std::string &&column_name) + : _table_name(table_name), _column_name(std::move(column_name)) + { + } + Attribute(const Attribute &other, const bool print_table_name) + : _table_name(other._table_name), _column_name(other._column_name), _print_table_name(print_table_name) + { + } + + Attribute(Attribute &&) = default; + Attribute(const Attribute &) = default; + ~Attribute() = default; + + Attribute &operator=(const Attribute &) = default; + Attribute &operator=(Attribute &&) = default; + + bool operator==(const Attribute &other) const + { + return _table_name == other._table_name && _column_name == other._column_name; + } + + void table_name(const std::string &table_name) + { + _table_name = table_name; + } + [[nodiscard]] const std::optional &table_name() const + { + return _table_name; + } + [[nodiscard]] const std::string &column_name() const + { + return _column_name; + } + [[nodiscard]] bool is_asterisk() const + { + return _column_name == "*"; + } + [[nodiscard]] bool is_print_table_name() const + { + return _print_table_name; + } + + explicit operator std::string() const + { + if (_table_name.has_value() && _print_table_name) + { + return _table_name.value() + "." + _column_name; + } + + return _column_name; + } + + private: + std::optional _table_name; + std::string _column_name; + bool _print_table_name = false; +}; +} // namespace beedb::expression + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::expression::Attribute &attribute) const + { + return std::hash()( + std::string{attribute.table_name().value_or("") + "." + attribute.column_name()}); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/expression/operation.h b/include/expression/operation.h new file mode 100644 index 0000000..f12e86e --- /dev/null +++ b/include/expression/operation.h @@ -0,0 +1,384 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "term.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::expression +{ +class Operation +{ + public: + enum Type : std::uint8_t + { + Identity = 0, + Count = 4, + Average = 5, + Sum = 6, + Min = 7, + Max = 8, + Add = 16, + Sub = 17, + Multiply = 18, + Divide = 19, + And = 32, + Or = 33, + Equals = 64, + NotEquals = 65, + Lesser = 66, + LesserEquals = 67, + Greater = 68, + GreaterEquals = 69 + }; + + [[nodiscard]] bool is_nullary() const + { + return _type == Identity; + } + [[nodiscard]] bool is_unary() const + { + return _type >= static_cast(Type::Count) && _type <= static_cast(Type::Max); + } + [[nodiscard]] bool is_aggregation() const + { + return _type >= static_cast(Type::Count) && _type <= static_cast(Type::Max); + } + [[nodiscard]] bool is_logical_connective() const + { + return _type == Type::And || _type == Type::Or; + } + [[nodiscard]] bool is_comparison() const + { + return _type >= static_cast(Type::Equals) && + _type <= static_cast(Type::GreaterEquals); + } + [[nodiscard]] bool is_arithmetic() const + { + return _type >= static_cast(Type::Add) && _type <= static_cast(Type::Divide); + } + [[nodiscard]] bool is_binary() const + { + return is_logical_connective() || is_comparison() || is_arithmetic(); + } + + [[nodiscard]] Type type() const + { + return _type; + } + void type(Type type) + { + _type = type; + } + [[nodiscard]] const std::optional &result() const + { + return _result; + } + [[nodiscard]] std::optional &result() + { + return _result; + } + + void alias(std::string &&alias) + { + _result->alias(std::move(alias)); + } + + [[nodiscard]] virtual std::unique_ptr copy() const = 0; + + virtual ~Operation() = default; + + protected: + explicit Operation(const Type type) : _type(type), _result(std::nullopt) + { + } + Operation(const Type type, Term &&result) : _type(type), _result(std::move(result)) + { + } + Operation(const Type type, std::optional &&result) : _type(type), _result(std::move(result)) + { + } + + Type _type; + std::optional _result; +}; + +class NullaryOperation : public Operation +{ + public: + explicit NullaryOperation(Term &&term) : Operation(Operation::Identity, term) + { + } + NullaryOperation(const NullaryOperation &) = default; + NullaryOperation(NullaryOperation &&) = default; + + ~NullaryOperation() override = default; + + [[nodiscard]] const Term &term() const + { + return _result.value(); + } + [[nodiscard]] Term &term() + { + return _result.value(); + } + + [[nodiscard]] std::unique_ptr copy() const override + { + return std::make_unique(Term{_result.value()}); + } +}; + +class UnaryOperation : public Operation +{ + public: + [[nodiscard]] static std::unique_ptr make_count(std::unique_ptr &&child) + { + return UnaryOperation::make_operation(Operation::Type::Count, "COUNT", std::move(child)); + } + + [[nodiscard]] static std::unique_ptr make_avg(std::unique_ptr &&child) + { + return UnaryOperation::make_operation(Operation::Type::Average, "AVG", std::move(child)); + } + + [[nodiscard]] static std::unique_ptr make_sum(std::unique_ptr &&child) + { + return UnaryOperation::make_operation(Operation::Type::Sum, "SUM", std::move(child)); + } + + [[nodiscard]] static std::unique_ptr make_min(std::unique_ptr &&child) + { + return UnaryOperation::make_operation(Operation::Type::Min, "MIN", std::move(child)); + } + + [[nodiscard]] static std::unique_ptr make_max(std::unique_ptr &&child) + { + return UnaryOperation::make_operation(Operation::Type::Max, "MAX", std::move(child)); + } + + UnaryOperation(const Type type, Term &&result, std::unique_ptr &&child) + : Operation(type, std::move(result)), _child(std::move(child)) + { + } + UnaryOperation(const Type type, std::optional &&result, std::unique_ptr &&child) + : Operation(type, std::move(result)), _child(std::move(child)) + { + } + UnaryOperation(const Type type, std::unique_ptr &&child) : Operation(type), _child(std::move(child)) + { + } + + ~UnaryOperation() override = default; + + [[nodiscard]] const std::unique_ptr &child() const + { + return _child; + } + [[nodiscard]] std::unique_ptr &child() + { + return _child; + } + + [[nodiscard]] std::unique_ptr copy() const override + { + return std::make_unique( + _type, _result.has_value() ? std::make_optional(Term{_result.value()}) : std::nullopt, _child->copy()); + } + + private: + std::unique_ptr _child; + + [[nodiscard]] static std::unique_ptr make_operation(const Operation::Type type, std::string &&name, + std::unique_ptr &&child) + { + return std::make_unique( + type, + expression::Term{ + expression::Attribute{std::move(name) + "(" + static_cast(child->result().value()) + ")"}, + std::nullopt, true}, + std::move(child)); + } +}; + +class BinaryOperation : public Operation +{ + public: + [[nodiscard]] static std::unique_ptr make_and(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::And, " AND ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_or(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Or, " OR ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_equals(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Equals, " = ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_not_equals(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::NotEquals, " != ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_lesser(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Lesser, " < ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_lesser_equals(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::LesserEquals, " <= ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_greater(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Greater, " > ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_greater_equals(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::GreaterEquals, " >= ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_add(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Add, " + ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_sub(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Sub, " - ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_mul(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Multiply, " * ", std::move(left_child), + std::move(right_child)); + } + + [[nodiscard]] static std::unique_ptr make_div(std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + return BinaryOperation::make_operation(Operation::Type::Divide, " / ", std::move(left_child), + std::move(right_child)); + } + + BinaryOperation(const Type type, Term &&result, std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + : Operation(type, std::move(result)), _left_child(std::move(left_child)), _right_child(std::move(right_child)) + { + } + BinaryOperation(const Type type, std::optional &&result, std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + : Operation(type, std::move(result)), _left_child(std::move(left_child)), _right_child(std::move(right_child)) + { + } + BinaryOperation(const Type type, std::unique_ptr &&left_child, std::unique_ptr &&right_child) + : Operation(type), _left_child(std::move(left_child)), _right_child(std::move(right_child)) + { + } + + ~BinaryOperation() override = default; + + [[nodiscard]] const std::unique_ptr &left_child() const + { + return _left_child; + } + [[nodiscard]] const std::unique_ptr &right_child() const + { + return _right_child; + } + [[nodiscard]] std::unique_ptr &left_child() + { + return _left_child; + } + [[nodiscard]] std::unique_ptr &right_child() + { + return _right_child; + } + + [[nodiscard]] std::unique_ptr copy() const override + { + return std::make_unique( + _type, _result.has_value() ? std::make_optional(Term{_result.value()}) : std::nullopt, _left_child->copy(), + _right_child->copy()); + } + + private: + std::unique_ptr _left_child; + std::unique_ptr _right_child; + + [[nodiscard]] static std::unique_ptr make_operation(const Operation::Type type, std::string &&name, + std::unique_ptr &&left_child, + std::unique_ptr &&right_child) + { + auto attribute = + expression::Attribute{"(" + static_cast(left_child->result().value()) + std::move(name) + + static_cast(right_child->result().value()) + ")"}; + + return std::make_unique(type, expression::Term{std::move(attribute), std::nullopt, true}, + std::move(left_child), std::move(right_child)); + } +}; + +void visit(std::function &)> &&nullary_callback, + std::function &)> &&unary_callback, + std::function &)> &&binary_callback, + const std::unique_ptr &operation); +void for_attribute(const std::unique_ptr &operation, std::function &&callback); +std::vector attributes(const std::unique_ptr &operation); +std::vector attributes(std::unique_ptr &&operation); +std::vector nullaries(const std::unique_ptr &operation, + const bool attribute_required = false); +std::vector nullaries(std::unique_ptr &&operation, const bool attribute_required = false); +} // namespace beedb::expression \ No newline at end of file diff --git a/include/expression/term.h b/include/expression/term.h new file mode 100644 index 0000000..ca28918 --- /dev/null +++ b/include/expression/term.h @@ -0,0 +1,241 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "attribute.h" +#include +#include +#include +#include
+#include + +namespace beedb::expression +{ +using NullValue = std::nullptr_t; +class Term +{ + public: + Term() = default; + + explicit Term(Attribute &&reference) : _attribute_or_value(std::move(reference)) + { + } + explicit Term(const Attribute &reference) : _attribute_or_value(reference) + { + } + explicit Term(std::string &&value) : _attribute_or_value(std::move(value)) + { + } + explicit Term(std::int64_t value) : _attribute_or_value(value) + { + } + explicit Term(std::int32_t value) : _attribute_or_value(value) + { + } + explicit Term(double value) : _attribute_or_value(value) + { + } + explicit Term(NullValue) : _attribute_or_value(NullValue{}) + { + } + + explicit Term(table::Value &&value) + { + std::visit([&attr_or_value = _attribute_or_value](auto &v) { attr_or_value = std::move(v); }, value.value()); + } + + Term(Attribute &&reference, std::optional &&alias) + : _attribute_or_value(std::move(reference)), _alias(std::move(alias)) + { + } + + Term(Attribute &&reference, std::optional &&alias, bool is_generated) + : _attribute_or_value(std::move(reference)), _alias(std::move(alias)), _is_generated(is_generated) + { + } + + Term(const Attribute &reference, std::optional &&alias) + : _attribute_or_value(reference), _alias(std::move(alias)) + { + } + Term(std::string &&value, std::optional &&alias) + : _attribute_or_value(std::move(value)), _alias(std::move(alias)) + { + } + Term(std::int64_t value, std::optional &&alias) : _attribute_or_value(value), _alias(std::move(alias)) + { + } + Term(std::int32_t value, std::optional &&alias) : _attribute_or_value(value), _alias(std::move(alias)) + { + } + Term(double value, std::optional &&alias) : _attribute_or_value(value), _alias(std::move(alias)) + { + } + Term(NullValue, std::optional &&alias) : _attribute_or_value(NullValue{}), _alias(std::move(alias)) + { + } + Term(Term &&) = default; + Term(const Term &) = default; + + ~Term() = default; + + Term &operator=(const Term &) = default; + Term &operator=(Term &&) = default; + + [[nodiscard]] static Term make_attribute(std::string &&table_name, std::string &&column_name, + const bool display_table = false) + { + return Term{Attribute{std::move(table_name), std::move(column_name), display_table}}; + } + + [[nodiscard]] static Term make_attribute(const std::string &table_name, std::string &&column_name, + const bool display_table = false) + { + return Term{Attribute{table_name, std::move(column_name), display_table}}; + } + + [[nodiscard]] static Term make_attribute(const std::string &table_name, const std::string &column_name) + { + return Term{Attribute{table_name, column_name}}; + } + + [[nodiscard]] static Term make_attribute(std::string &&column_name) + { + return Term{Attribute{std::nullopt, std::move(column_name)}}; + } + + [[nodiscard]] const auto &attribute_or_value() const + { + return _attribute_or_value; + } + + [[nodiscard]] const std::optional alias() const + { + return _alias; + } + void alias(const std::string &alias) + { + _alias = alias; + } + void alias(std::string &&alias) + { + _alias = std::move(alias); + } + + [[nodiscard]] bool is_attribute() const + { + return std::holds_alternative(_attribute_or_value); + } + [[nodiscard]] bool is_null() const + { + return std::holds_alternative(_attribute_or_value); + } + [[nodiscard]] bool is_value() const + { + return std::holds_alternative(_attribute_or_value) || + std::holds_alternative(_attribute_or_value) || + std::holds_alternative(_attribute_or_value) || + std::holds_alternative(_attribute_or_value); + } + + template [[nodiscard]] const T &get() const + { + return std::get(_attribute_or_value); + } + + template [[nodiscard]] T &get() + { + return std::get(_attribute_or_value); + } + + [[nodiscard]] bool is_generated() const + { + return _is_generated; + } + + bool operator==(const Term &other) const + { + return _attribute_or_value == other._attribute_or_value && _alias == other._alias; + } + + explicit operator std::string() const + { + if (_alias.has_value()) + { + return _alias.value(); + } + + if (std::holds_alternative(_attribute_or_value)) + { + return static_cast(std::get(_attribute_or_value)); + } + else if (std::holds_alternative(_attribute_or_value)) + { + return std::get(_attribute_or_value); + } + else if (std::holds_alternative(_attribute_or_value)) + { + return std::to_string(std::get(_attribute_or_value)); + } + else if (std::holds_alternative(_attribute_or_value)) + { + return std::to_string(std::get(_attribute_or_value)); + } + else if (std::holds_alternative(_attribute_or_value)) + { + return std::to_string(std::get(_attribute_or_value)); + } + else if (std::holds_alternative(_attribute_or_value)) + { + return std::string{"NULL"}; + } + + return ""; + } + + private: + std::variant + _attribute_or_value{NullValue{}}; + std::optional _alias{std::nullopt}; + bool _is_generated{false}; +}; +} // namespace beedb::expression + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::expression::Term &term) const + { + std::size_t h = std::hash()(term.alias().value_or("")); + std::visit( + [&h](const auto &v) { + using T = std::decay_t; + h ^= std::hash()(v); + }, + term.attribute_or_value()); + return h; + } +}; +} // namespace std \ No newline at end of file diff --git a/include/index/b_plus_tree/b_plus_tree.h b/include/index/b_plus_tree/b_plus_tree.h new file mode 100644 index 0000000..848927b --- /dev/null +++ b/include/index/b_plus_tree/b_plus_tree.h @@ -0,0 +1,287 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "b_plus_tree_node.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::index::bplustree +{ +template class BPlusTree +{ + public: + using Node = BPlusTreeNode; + + BPlusTree() : _root(new Node(true)) + { + } + + ~BPlusTree() + { + delete _root; + } + + /** + * Inserts the given key-value-pair into the tree. + * + * @param key + * @param value + */ + void put(const K key, V value); + + /** + * Finds the value by the given key. + * + * @param key + * @return The found value. + */ + [[nodiscard]] std::optional::type> get(const K key) const; + + [[nodiscard]] std::optional> get(const K key_from, const K key_to) const; + + [[nodiscard]] Node *root() const + { + return _root; + } + + [[nodiscard]] size_type height() const + { + return _height; + } + + private: + Node *_root; + size_type _height = 1; + + /** + * Locates a leaf node for a given key. + * + * @param key + * @param node_path + * @return + */ + Node *locate_leaf(const K key, std::vector *node_path = nullptr) const; + + /** + * Inserts the given key-value-tuple into the give leaf node. + * + * @param leaf_node + * @param key + * @param value + * @return + */ + Node *insert_into_leaf(Node *leaf_node, const K key, const V value); + + /** + * Inserts the given key and separator into the given inner node. + * + * @param inner_node + * @param key + * @param separator + * @return + */ + std::pair insert_into_inner(Node *inner_node, const K key, Node *separator); + + /** + * Creates a new root with pointer to the two given new child nodes. + * + * @param left + * @param right + * @param key + */ + void install_new_root_node(Node *left, Node *right, const K key); + + /** + * Splits the given inner node and returns the new node and a key, + * that has to be inserted into the parent node. + * + * @param inner_node + * @param key + * @param separator + * @return + */ + std::pair split_inner_node(Node *inner_node, const K key, Node *separator); + + /** + * Splits the given leaf node and returns the new node. + * + * @param leaf_node + * @return + */ + Node *split_leaf_node(Node *leaf_node); + + friend std::ostream &operator<<(std::ostream &stream, const BPlusTree &tree) + { + Node *root = tree.root(); + if (root == nullptr) + { + return stream; + } + + const auto items = tree.root()->size_include_children(); + const auto nodes = tree.root()->count_children(); + + return stream << "Height = " << tree.height() << "\n" + << "Key-Value-Pairs = " << items.second << "\n" + << "Inner-Nodes = " << nodes.first << "\n" + << "Leaf-Nodes = " << nodes.second << "\n" + << "Memory = " + << ((nodes.first + nodes.second) * Config::b_plus_tree_page_size) / 1024 / 1024 << " MB\n"; + } +}; + +template void BPlusTree::put(const K key, V value) +{ + // Path for traversal. All nodes from root excluding the leaf node will be stored. + std::vector path; + path.reserve(6); + + // Locate the possible leaf. + Node *leaf = this->locate_leaf(key, &path); + + // Insert into leaf + K up_key; + Node *new_node = this->insert_into_leaf(leaf, key, value); + if (new_node != nullptr) + { + up_key = new_node->leaf_key(0u); + } + + // Propagate up. + while (new_node != nullptr && path.empty() == false) + { + Node *parent = path.back(); + path.pop_back(); + auto [n, u] = this->insert_into_inner(parent, up_key, new_node); + new_node = n; + up_key = u; + } + + // Create new root + if (new_node != nullptr) + { + this->install_new_root_node(_root, new_node, up_key); + } +} + +template +std::pair *, K> BPlusTree::insert_into_inner(BPlusTree::Node *inner_node, + const K key, + BPlusTree::Node *separator) +{ + if (inner_node->is_full() == false) + { + const size_type index = inner_node->index(key); + inner_node->insert_separator(index, separator, key); + return {static_cast(nullptr), 0}; + } + else + { + return this->split_inner_node(inner_node, key, separator); + } +} + +template +void BPlusTree::install_new_root_node(BPlusTree::Node *left, BPlusTree::Node *right, + const K key) +{ + Node *new_root = new Node(false); + new_root->separator(0, left); + new_root->insert_separator(0, right, key); + _height++; + _root = new_root; +} + +template +std::pair *, K> BPlusTree::split_inner_node(BPlusTree::Node *inner_node, + const K key, + BPlusTree::Node *separator) +{ + constexpr size_type left_size = BPlusTreeInnerNode::max_keys / 2; + constexpr size_type right_size = BPlusTreeInnerNode::max_keys - left_size; + + K key_up; + Node *new_inner_node = new Node(false); + new_inner_node->right(inner_node->right()); + inner_node->right(new_inner_node); + + if (key < inner_node->inner_key(left_size - 1)) + { + inner_node->copy(new_inner_node, left_size, right_size); + new_inner_node->separator(0, inner_node->separator(left_size)); + new_inner_node->size(right_size); + + key_up = inner_node->inner_key(left_size - 1); + inner_node->size(left_size - 1); + + const size_type index = inner_node->index(key); + inner_node->insert_separator(index, separator, key); + } + else if (key < inner_node->inner_key(left_size)) + { + inner_node->copy(new_inner_node, left_size, right_size); + new_inner_node->separator(0, separator); + key_up = key; + inner_node->size(left_size); + new_inner_node->size(right_size); + } + else + { + inner_node->copy(new_inner_node, left_size + 1, right_size - 1); + new_inner_node->separator(0, inner_node->separator(left_size + 1)); + inner_node->size(left_size); + new_inner_node->size(right_size - 1); + key_up = inner_node->inner_key(left_size); + + const size_type index = new_inner_node->index(key); + new_inner_node->insert_separator(index, separator, key); + } + + return {new_inner_node, key_up}; +} + +template +BPlusTreeNode *BPlusTree::split_leaf_node(BPlusTree::Node *leaf_node) +{ + constexpr size_type left_size = BPlusTreeLeafNode::max_items / 2; + constexpr size_type right_size = BPlusTreeLeafNode::max_items - left_size; + + Node *new_leaf_node = new Node(true); + new_leaf_node->right(leaf_node->right()); + leaf_node->right(new_leaf_node); + + leaf_node->copy(new_leaf_node, left_size, right_size); + new_leaf_node->size(right_size); + leaf_node->size(left_size); + + return new_leaf_node; +} +} // namespace beedb::index::bplustree + +#include "b_plus_tree.hpp" diff --git a/include/index/b_plus_tree/b_plus_tree.hpp b/include/index/b_plus_tree/b_plus_tree.hpp new file mode 100644 index 0000000..368b98f --- /dev/null +++ b/include/index/b_plus_tree/b_plus_tree.hpp @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#include +#include +#include +#include +#include +#include + +namespace beedb::index::bplustree +{ +template +BPlusTreeNode *BPlusTree::locate_leaf( + [[maybe_unused]] const K key, [[maybe_unused]] std::vector *> *node_path) const +{ + /** + * Assignment (2): Implement a B+-Tree + * + * The B+-Tree is used for indexing files. Using the index + * for bigger data sets will reduce the amount of scanned disk pages + * in case the query wants to filter the data. + * + * This method is used to traverse the tree and locate a leaf that may + * contain the wanted key. During the traversal for inserts, all visited nodes + * are stored in the node_path container. For lookups, the container is null. + * + * Hints for implementation: + * - "this->_root" stores the root node of the tree, where every + * lookup for a leaf is started. + * - Every inner node (also the root) has a "child(k)" method, which returns + * the next node on the way for the leaf, that may contain the key "k". + * - To check a node whether it is a leaf or inner node, use the "is_leaf()" + * method on a node, which returns true if the node is a leaf. + * - If "node_path" is not a "nullptr", push all nodes during traversal + * to that vector (also the root). + * + * + * Procedure: + * - Start the traversal at the root node. + * - Get the next node using "current_node->child(key)" while "current_node" + * is not a leaf. + * - Push every node to the "node_path", if "node_path != nullptr". + * - Return the leaf you found during traversal. + */ + + Node *current_node = this->_root; + + // TODO: Insert your code here. + + return current_node; +} + +template +std::optional::type> BPlusTree::get([[maybe_unused]] const K key) const +{ + /** + * Assignment (2): Implement a B+-Tree + * + * This method tries to find the value for a given key. + * The tree is a very generic data structure which can hold + * one value per key or multiple values per key. The specific + * variant is given by the template parameter U which is a bool + * and stands for Unique. True means: Return one value of type V; + * false means: Return a set of values of type V. + * + * Hints for implementation: + * - You have already implemented "locate_leaf(k)" which returns the + * leaf that may contain the searched key-value pair. + * - Every leaf node provides a method "index(k)" which returns the index + * of the key "k". + * - Every leaf node provides a method "leaf_key(i)" which returns the + * key at index "i". + * - Every leaf node provides a method "value(i)" which returns the value + * at index "i". The "value(i)" method will automatically pick the correct + * return type, depending on the U-template-parameter. + * + * Procedure: + * - Locate the leaf node that may contain the wanted key. + * - Check the leaf node: Is the wanted key available? + * - If yes: return the value of the key. + * - Otherwise return an empty result, using "return std::nullopt;". + */ + + // TODO: Insert your code here. + + return std::nullopt; +} + +template +std::optional> BPlusTree::get([[maybe_unused]] const K key_from, + [[maybe_unused]] const K key_to) const +{ + /** + * Assignment (2): Implement a B+-Tree + * + * This method tries to find one or multiple values for a given + * range of keys. + * The tree is a very generic data structure which can hold + * one value per key or multiple values per key. The specific + * variant is given by the template parameter U which is a bool + * and stands for Unique. True means: Return one value of type V; + * false means: Return a set of values of type V. + * + * Hints for implementation: + * - You have already implemented "locate_leaf(k)" which returns the + * leaf that may contain the searched key-value pair. + * - Every node provides a method "right()" which returns a pointer + * to the right neighbour node. + * - Every node provides a method "size()" which returns the number of + * items that are stored in the node. + * - Every leaf node provides a method "index(k)" which returns the index + * of the key "k". + * - Every leaf node provides a method "leaf_key(i)" which returns the + * key at index "i". + * - Every leaf node provides a method "value(i)" which returns the value + * at index "i". The "value(i)" method will automatically pick the correct + * return type, depending on the U-template-parameter. + * - You can test whether it is a unique or non-unique tree, using + * "if constexpr(U) { code for unique... } else { boot for non-unique... }. + * - Both containers std::set (https://en.cppreference.com/w/cpp/container/set) + * and std::optional (https://en.cppreference.com/w/cpp/utility/optional) may + * be helpful on compiler errors. + * + * Procedure: + * - Locate the leaf node that may contain the wanted key. + * - Add all keys that are equal or greater than the key "key_from" + * and equal or lesser than the key "key_to" to a set of values. + * - When the last key of the node matches that predicate, also + * take a look to the right neighbour using the "right()" method + * (and also the rights right,...). + */ + + std::set values; + + // TODO: Insert your code here. + + return values; +} + +template +BPlusTreeNode *BPlusTree::insert_into_leaf([[maybe_unused]] BPlusTreeNode *leaf_node, + [[maybe_unused]] const K key, + [[maybe_unused]] const V value) +{ + /** + * Assignment (2): Implement a B+-Tree + * + * This method adds a value to a leaf node. The correct leaf node, key and + * value are all given. When inserting results in splitting the leaf, + * the pointer to the new created node is returned. + * + * Hints for implementation: + * - Every node provides a method "full()" which returns true, if there + * is no more place for a new item. + * - Every leaf node provides a method "index(k)" which returns the index + * of the key "k". + * - Every leaf node provides a method "leaf_key(i)" which returns the + * key at index "i". + * - Every leaf node provides a method "insert_value(i, v, k)" which adds + * a key-value pair (k,v) to the leaf at index i. + * - The tree has a method "this->split_leaf_node(l)" which splits the leaf + * node l and returns a pointer to the new node. + * - You can test whether it is a unique or non-unique tree, using + * "if constexpr(U) { code for unique... } else { boot for non-unique... }. + * + * Procedure: + * - Check if the leaf node already contains the key + * - If yes and the tree is non-unique: add the value to the list of values + * in the node and return a "nullptr". + * - If yey and the tree is unique: Just return a "nullptr". + * - If the key is not in the node, check for space for a new (key,value) pair. + * - If the node is not full, insert the new pair and return a "nullptr" + * - Otherwise, we have to split the node. Splitting will create a new leaf node, + * the new right neighbour of the given leaf node. + * - After splitting, we have enough space to insert the pair. Check whether the key + * should take place in the given leaf or the new leaf, created on splitting: + * When the key is lower than the first key of the new leaf, the key should be insert + * into the given leaf, otherwise in the new leaf. + * - After splitting, return the pointer to the new leaf. + */ + + // TODO: Insert your code here. + + return nullptr; +} +} // namespace beedb::index::bplustree diff --git a/include/index/b_plus_tree/b_plus_tree_node.h b/include/index/b_plus_tree/b_plus_tree_node.h new file mode 100644 index 0000000..ebc7de9 --- /dev/null +++ b/include/index/b_plus_tree/b_plus_tree_node.h @@ -0,0 +1,294 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace beedb::index::bplustree +{ +using size_type = std::size_t; + +template class BPlusTreeNode; + +template struct BPlusTreeNodeHeader +{ + size_type size = 0; + bool is_leaf; + BPlusTreeNode *right = nullptr; + + explicit BPlusTreeNodeHeader(const bool is_leaf_) : is_leaf(is_leaf_) + { + } +}; + +template struct BPlusTreeLeafNode +{ + static constexpr size_type max_items = (Config::b_plus_tree_page_size - sizeof(BPlusTreeNodeHeader)) / + (sizeof(K) + sizeof(typename ReturnValue::type)); + + std::array keys; + std::array::type, BPlusTreeLeafNode::max_items> values; +}; + +template struct BPlusTreeInnerNode +{ + static constexpr size_type max_keys = + (Config::b_plus_tree_page_size - sizeof(BPlusTreeNodeHeader) - sizeof(BPlusTreeNode *)) / + (sizeof(K) + sizeof(BPlusTreeInnerNode *)); + static constexpr size_type max_separators = max_keys + 1; + + std::array keys; + std::array *, BPlusTreeInnerNode::max_separators> separators; +}; + +template class BPlusTreeNode +{ + public: + explicit BPlusTreeNode(const bool is_leaf) : _header(is_leaf) + { + } + ~BPlusTreeNode(); + + [[nodiscard]] bool is_leaf() const + { + return _header.is_leaf; + } + [[nodiscard]] bool is_inner() const + { + return is_leaf() == false; + } + [[nodiscard]] size_type size() const + { + return _header.size; + } + void size(const size_type size) + { + _header.size = size; + } + [[nodiscard]] BPlusTreeNode *right() + { + return _header.right; + } + [[nodiscard]] bool has_right() const + { + return _header.right != nullptr; + } + void right(BPlusTreeNode *right) + { + _header.right = right; + } + + [[nodiscard]] typename ReturnValue::type &value(const size_type index) + { + return _leaf_node.values[index]; + } + [[nodiscard]] BPlusTreeNode *separator(const size_type index) + { + return _inner_node.separators[index]; + } + void separator(const size_type index, BPlusTreeNode *separator) + { + _inner_node.separators[index] = separator; + } + + [[nodiscard]] K leaf_key(const size_type index) + { + return _leaf_node.keys[index]; + } + [[nodiscard]] K inner_key(const size_type index) + { + return _inner_node.keys[index]; + } + + [[nodiscard]] bool is_full() const + { + const size_type max_size = + is_leaf() ? BPlusTreeLeafNode::max_items : BPlusTreeInnerNode::max_keys; + return size() >= max_size; + } + + size_type index(const K key); + BPlusTreeNode *child(const K key); + + void insert_separator(const size_type index, BPlusTreeNode *separator, const K key); + void insert_value(const size_type index, const V value, const K key); + void copy(BPlusTreeNode *other, const size_type from_index, const size_type count); + + std::pair size_include_children(); + std::pair count_children(); + + private: + BPlusTreeNodeHeader _header; + + union { + BPlusTreeInnerNode _inner_node; + BPlusTreeLeafNode _leaf_node; + }; +}; + +template BPlusTreeNode::~BPlusTreeNode() +{ + if (is_leaf() == false) + { + for (size_type i = 0; i < size(); i++) + { + delete _inner_node.separators[i]; + } + } +} + +template size_type BPlusTreeNode::index(const K key) +{ + auto keys = is_leaf() ? _leaf_node.keys.begin() : _inner_node.keys.begin(); + auto iterator = std::lower_bound(keys, keys + size(), key); + + return std::distance(keys, iterator); +} + +template BPlusTreeNode *BPlusTreeNode::child(const K key) +{ + std::int32_t low = 0, high = size() - 1; + while (low <= high) + { + const std::int32_t mid = (low + high) / 2; + if (_inner_node.keys[mid] <= key) + { + low = mid + 1; + } + else + { + high = mid - 1; + } + } + + return _inner_node.separators[high + 1]; +} + +template +void BPlusTreeNode::insert_separator(const size_type index, BPlusTreeNode *separator, const K key) +{ + if (index < size()) + { + const size_type offset = size() - index; + std::memmove(&_inner_node.keys[index + 1], &_inner_node.keys[index], offset * sizeof(K)); + std::memmove(&_inner_node.separators[index + 2], &_inner_node.separators[index + 1], + offset * sizeof(BPlusTreeNode *)); + } + + _inner_node.keys[index] = key; + _inner_node.separators[index + 1] = separator; + _header.size++; +} + +template +void BPlusTreeNode::insert_value(const size_type index, const V value, const K key) +{ + if (index < size()) + { + const size_type offset = size() - index; + std::memmove(&_leaf_node.keys[index + 1], &_leaf_node.keys[index], offset * sizeof(K)); + std::memmove(static_cast(&_leaf_node.values[index + 1]), &_leaf_node.values[index], + offset * sizeof(typename ReturnValue::type)); + } + + _leaf_node.keys[index] = key; + + if constexpr (U) + { + _leaf_node.values[index] = value; + } + else + { + new (&_leaf_node.values[index]) typename ReturnValue::type(); + _leaf_node.values[index].insert(value); + } + + _header.size++; +} + +template +void BPlusTreeNode::copy(BPlusTreeNode *other, const size_type from_index, const size_type count) +{ + if (is_leaf()) + { + std::memcpy(&other->_leaf_node.keys[0], &_leaf_node.keys[from_index], count * sizeof(K)); + std::memcpy(static_cast(&other->_leaf_node.values[0]), &_leaf_node.values[from_index], + count * sizeof(typename ReturnValue::type)); + } + else + { + std::memcpy(&other->_inner_node.keys[0], &_inner_node.keys[from_index], count * sizeof(K)); + std::memcpy(&other->_inner_node.separators[1], &_inner_node.separators[from_index + 1], + count * sizeof(BPlusTreeNode *)); + } +} + +template +std::pair BPlusTreeNode::size_include_children() +{ + if (is_leaf()) + { + return {0u, size()}; + } + + std::size_t leaf_sizes = 0, inner_sizes = 0; + for (auto i = 0u; i <= size(); i++) + { + BPlusTreeNode *child = _inner_node.separators[i]; + const auto child_size = child->size_include_children(); + inner_sizes += child_size.first; + leaf_sizes += child_size.second; + } + + return {inner_sizes, leaf_sizes}; +} + +template std::pair BPlusTreeNode::count_children() +{ + if (is_leaf()) + { + return {0u, 0u}; + } + + if (_inner_node.separators[0]->is_leaf()) + { + return {0u, size() + 1u}; + } + + std::size_t leaf_children = 0, inner_children = 0; + for (auto i = 0u; i <= size(); i++) + { + BPlusTreeNode *child = _inner_node.separators[i]; + + const auto child_size = child->count_children(); + inner_children += child_size.first; + leaf_children += child_size.second; + } + + return {inner_children + size(), leaf_children}; +} +} // namespace beedb::index::bplustree diff --git a/include/index/b_plus_tree/non_unique_b_plus_tree_index.h b/include/index/b_plus_tree/non_unique_b_plus_tree_index.h new file mode 100644 index 0000000..a6ffa61 --- /dev/null +++ b/include/index/b_plus_tree/non_unique_b_plus_tree_index.h @@ -0,0 +1,74 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "b_plus_tree.h" +#include +#include +#include +#include + +namespace beedb::index::bplustree +{ +/** + * B+-Tree implementation for non-unique (key,value) pairs. + * Can store multiple values for each key. + * Supports range queries. + */ +class NonUniqueBPlusTreeIndex : public IndexInterface, public NonUniqueIndexInterface, public RangeIndexInterface +{ + public: + explicit NonUniqueBPlusTreeIndex(const std::string &name) : IndexInterface(name) + { + } + ~NonUniqueBPlusTreeIndex() override = default; + + [[nodiscard]] bool supports_range() const override + { + return true; + } + [[nodiscard]] bool is_unique() const override + { + return false; + } + + void put(const std::int64_t key, storage::Page::id_t page_pointer) override + { + _tree.put(key, page_pointer); + } + + [[nodiscard]] std::optional> get(const std::int64_t key) const override + { + return _tree.get(key); + } + + [[nodiscard]] std::optional> get(const std::int64_t key_from, + const std::int64_t key_to) override + { + return _tree.get(key_from, key_to); + } + + private: + BPlusTree _tree; +}; +} // namespace beedb::index::bplustree \ No newline at end of file diff --git a/include/index/b_plus_tree/unique_b_plus_tree_index.h b/include/index/b_plus_tree/unique_b_plus_tree_index.h new file mode 100644 index 0000000..0ce9e4e --- /dev/null +++ b/include/index/b_plus_tree/unique_b_plus_tree_index.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "b_plus_tree.h" +#include +#include +#include +#include + +namespace beedb::index::bplustree +{ +/** + * B+-Tree implementation for unique (key,value) pairs. + * Supports range queries. + */ +class UniqueBPlusTreeIndex : public IndexInterface, public UniqueIndexInterface, public RangeIndexInterface +{ + public: + explicit UniqueBPlusTreeIndex(const std::string &name) : IndexInterface(name) + { + } + ~UniqueBPlusTreeIndex() override = default; + + [[nodiscard]] bool supports_range() const override + { + return true; + } + [[nodiscard]] bool is_unique() const override + { + return true; + } + + void put(const std::int64_t key, storage::Page::id_t page_pointer) override + { + _tree.put(key, page_pointer); + } + + [[nodiscard]] std::optional get(const std::int64_t key) const override + { + return _tree.get(key); + } + + [[nodiscard]] std::optional> get(const std::int64_t key_from, + const std::int64_t key_to) override + { + return _tree.get(key_from, key_to); + } + + private: + BPlusTree _tree; +}; +} // namespace beedb::index::bplustree \ No newline at end of file diff --git a/include/index/index_factory.h b/include/index/index_factory.h new file mode 100644 index 0000000..53521d6 --- /dev/null +++ b/include/index/index_factory.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "b_plus_tree/non_unique_b_plus_tree_index.h" +#include "b_plus_tree/unique_b_plus_tree_index.h" +#include "index_interface.h" +#include "type.h" +#include + +namespace beedb::index +{ +class IndexFactory +{ + public: + /** + * Builds an empty index. + * + * @param name Name for the index. + * @param type The index type identifies the underlying data structure like BTree, Hashtable, ... + * @param is_unique Identifies whether the index can hold multiple values for one key. + * @return Pointer to the in-memory index structure. + */ + static std::shared_ptr new_index(const std::string &name, const Type type, const bool is_unique) + { + if (type == Type::BTree) + { + if (is_unique) + { + return std::make_shared(name); + } + else + { + return std::make_shared(name); + } + } + + return {}; + } +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/index_interface.h b/include/index/index_interface.h new file mode 100644 index 0000000..153d839 --- /dev/null +++ b/include/index/index_interface.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include + +namespace beedb::index +{ +/** + * The index interface offers an API for multiple indices, whether + * they are range-indices, support multiple values for one key. + */ +class IndexInterface +{ + public: + explicit IndexInterface(const std::string &name) : _name(name) + { + } + virtual ~IndexInterface() = default; + + /** + * @return True, if the index supports just one value per key. + */ + [[nodiscard]] virtual bool is_unique() const = 0; + + /** + * @return True, if the index supports range queries (like B+Trees). + */ + [[nodiscard]] virtual bool supports_range() const = 0; + + /** + * Stores a (Key,Page) pair in the index. + * + * @param key Key for lookups. + * @param page_id Value. + */ + virtual void put(const std::int64_t key, storage::Page::id_t page_id) = 0; + + /** + * @return Name of the index. + */ + [[nodiscard]] const std::string &name() const + { + return _name; + } + + private: + const std::string _name; +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/non_unique_index_interface.h b/include/index/non_unique_index_interface.h new file mode 100644 index 0000000..a95b81d --- /dev/null +++ b/include/index/non_unique_index_interface.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include + +namespace beedb::index +{ +/** + * Interface for non-unique indices. + * The index can store multiple values per key. + */ +class NonUniqueIndexInterface +{ + public: + NonUniqueIndexInterface() = default; + virtual ~NonUniqueIndexInterface() = default; + + /** + * Lookup for all values of a key. + * + * @param key Key to lookup. + * @return All stored values for the key. + */ + [[nodiscard]] virtual std::optional> get(const std::int64_t key) const = 0; +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/range_index_interface.h b/include/index/range_index_interface.h new file mode 100644 index 0000000..968008a --- /dev/null +++ b/include/index/range_index_interface.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include + +namespace beedb::index +{ +/** + * Interface for range indices. + * The index can lookup a range of keys for range-queries. + */ +class RangeIndexInterface +{ + public: + RangeIndexInterface() = default; + ~RangeIndexInterface() = default; + + /** + * Lookup of a key-range. + * + * @param key_from From key. + * @param key_to To key. + * @return All values for the given key-range. + */ + [[nodiscard]] virtual std::optional> get(const std::int64_t key_from, + const std::int64_t key_to) = 0; +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/return_value.h b/include/index/return_value.h new file mode 100644 index 0000000..ffd674f --- /dev/null +++ b/include/index/return_value.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::index +{ +template struct ReturnValue +{ + using type = Value; +}; +template struct ReturnValue +{ + using type = std::set; +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/type.h b/include/index/type.h new file mode 100644 index 0000000..f68ad85 --- /dev/null +++ b/include/index/type.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include + +namespace beedb::index +{ +/** + * Possible index types. + */ +enum Type +{ + None, + BTree, + Hashtable, + Skiplist, + Bitmap /*, some more like: SkipList,... */ +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/index/unique_index_interface.h b/include/index/unique_index_interface.h new file mode 100644 index 0000000..e05a11d --- /dev/null +++ b/include/index/unique_index_interface.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include + +namespace beedb::index +{ +/** + * Interface for unique indices. + * The index can store only one value per key. + */ +class UniqueIndexInterface +{ + public: + UniqueIndexInterface() = default; + virtual ~UniqueIndexInterface() = default; + + /** + * Lookup for a value for a given key. + * + * @param key Key to lookup. + * @return The stored value or an empty optional. + */ + [[nodiscard]] virtual std::optional get(const std::int64_t key) const = 0; +}; +} // namespace beedb::index \ No newline at end of file diff --git a/include/io/client_console.h b/include/io/client_console.h new file mode 100644 index 0000000..a74f9d5 --- /dev/null +++ b/include/io/client_console.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace beedb::io +{ +class ClientConsole +{ + public: + ClientConsole(std::string &&server, const std::uint16_t port); + ~ClientConsole() = default; + + void run(); + + private: + network::Client _client; + + static bool handle_response(const std::string &response); + static void plan_to_table(util::TextTable &table, nlohmann::json &layer, std::uint16_t depth = 0u); +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/client_handler.h b/include/io/client_handler.h new file mode 100644 index 0000000..bf9f4a1 --- /dev/null +++ b/include/io/client_handler.h @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::io +{ +class ClientHandler : public network::ClientHandler +{ + public: + ClientHandler(Database &database) noexcept; + ~ClientHandler() noexcept override = default; + std::optional handle_message(const std::uint32_t client_id, const std::string &message) override; + void on_client_connected(const std::uint32_t id) override; + void on_client_disconnected(const std::uint32_t id) override; + void server(network::Server *server) override + { + network::ClientHandler::server(server); + _commander.register_command("stop", std::make_unique(server)); + } + + private: + Database &_database; + command::Commander _commander; + std::array _client_transactions; +}; + +class TransactionCallback : public concurrency::TransactionCallback +{ + public: + TransactionCallback(concurrency::Transaction *&transaction) : _transaction(transaction) + { + } + ~TransactionCallback() override = default; + + void on_begin(concurrency::Transaction *transaction) override + { + _transaction = transaction; + } + void on_end(concurrency::Transaction *, const bool) override + { + _transaction = nullptr; + } + + private: + concurrency::Transaction *&_transaction; +}; + +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/client_message_serializer.h b/include/io/client_message_serializer.h new file mode 100644 index 0000000..34ba460 --- /dev/null +++ b/include/io/client_message_serializer.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "execution_callback.h" +#include "query_result_serializer.h" +#include +#include +#include + +namespace beedb::io +{ +class ClientMessageSerializer final : public ExecutionCallback +{ + public: + ClientMessageSerializer() = default; + ~ClientMessageSerializer() override = default; + + void on_schema(const table::Schema &schema) override + { + _query_result_serializer.serialize(schema); + } + + void on_tuple(const table::Tuple &tuple) override + { + _query_result_serializer.serialize(tuple); + } + + void on_plan(const std::unique_ptr &plan) override + { + _query_plan = plan->to_json(); + } + + [[nodiscard]] const QueryResultSerializer &query_result() const + { + return _query_result_serializer; + } + [[nodiscard]] QueryResultSerializer &query_result() + { + return _query_result_serializer; + } + + [[nodiscard]] const nlohmann::json &query_plan() const + { + return _query_plan; + } + + private: + QueryResultSerializer _query_result_serializer; + nlohmann::json _query_plan; +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/command/commander.h b/include/io/command/commander.h new file mode 100644 index 0000000..76776f2 --- /dev/null +++ b/include/io/command/commander.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "custom_command_interface.h" +#include +#include +#include +#include + +namespace beedb::io::command +{ +class Commander +{ + public: + explicit Commander(Database &database); + ~Commander() = default; + + [[nodiscard]] static bool is_command(const std::string &input) + { + return input.at(0u) == ':'; + } + + void register_command(std::string &&name, std::unique_ptr &&command) + { + _registered_commands.insert(std::make_pair(std::move(name), std::move(command))); + } + + std::optional execute(const std::string &user_input, Executor &executor, + ExecutionCallback &execution_callback); + + private: + std::unordered_map> _registered_commands; + Database &_database; +}; +} // namespace beedb::io::command diff --git a/include/io/command/custom_command_interface.h b/include/io/command/custom_command_interface.h new file mode 100644 index 0000000..c2089d9 --- /dev/null +++ b/include/io/command/custom_command_interface.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include + +namespace beedb::io::command +{ +class CustomCommandInterface +{ + public: + CustomCommandInterface() = default; + virtual ~CustomCommandInterface() = default; + + [[nodiscard]] virtual std::optional execute(const std::string &input, Executor &executor, + ExecutionCallback &execution_callback) = 0; + [[nodiscard]] virtual std::string help() = 0; +}; +} // namespace beedb::io::command diff --git a/include/io/command/custom_commands.h b/include/io/command/custom_commands.h new file mode 100644 index 0000000..8e82b74 --- /dev/null +++ b/include/io/command/custom_commands.h @@ -0,0 +1,142 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "custom_command_interface.h" +#include + +namespace beedb::io::command +{ +class ShowCommand final : public CustomCommandInterface +{ + public: + ShowCommand() = default; + ~ShowCommand() override = default; + + [[nodiscard]] std::optional execute(const std::string ¶meters, Executor &executor, + ExecutionCallback &execution_callback) override; + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :show [tables,indices,columns]{1}"); + }; +}; + +class ExplainCommand final : public CustomCommandInterface +{ + public: + ExplainCommand() = default; + ~ExplainCommand() override = default; + [[nodiscard]] std::optional execute(const std::string &input, Executor &executor, + ExecutionCallback &execution_callback) override; + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :explain "); + }; +}; + +class SetCommand final : public CustomCommandInterface +{ + public: + explicit SetCommand(Config &config) : _config(config) + { + } + ~SetCommand() override = default; + + [[nodiscard]] std::optional execute(const std::string &input, Executor &executor, + ExecutionCallback &execution_callback) override; + + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :set "); + }; + + private: + Config &_config; +}; + +class GetCommand final : public CustomCommandInterface +{ + public: + explicit GetCommand(Config &config) : _config(config) + { + } + ~GetCommand() override = default; + + [[nodiscard]] std::optional execute(const std::string &input, Executor &executor, + ExecutionCallback &execution_callback) override; + + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :get []?"); + }; + + private: + Config &_config; +}; + +class StatsCommand final : public CustomCommandInterface +{ + public: + explicit StatsCommand(Database &db) : _stats(db.system_statistics()), _db(db) + { + } + ~StatsCommand() override = default; + + /** + * @brief execute This triggers computation of statistics for a specified, + * existing table. After confirming that the table exists, + * computations are triggered. + * @param input the table name + * @return the plan that computes the statistics + */ + [[nodiscard]] std::optional execute(const std::string &input, Executor &executor, + ExecutionCallback &execution_callback) override; + + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :stats
"); + }; + + private: + statistic::SystemStatistics &_stats; + Database &_db; +}; + +class StopServerCommand final : public CustomCommandInterface +{ + public: + explicit StopServerCommand(network::Server *server) : _server(server) + { + } + ~StopServerCommand() override = default; + + [[nodiscard]] std::optional execute(const std::string ¶meters, Executor &executor, + ExecutionCallback &execution_callback) override; + [[nodiscard]] std::string help() override + { + return std::string("Syntax> :stop"); + } + + private: + network::Server *_server; +}; +} // namespace beedb::io::command diff --git a/include/io/execution_callback.h b/include/io/execution_callback.h new file mode 100644 index 0000000..cde0fdc --- /dev/null +++ b/include/io/execution_callback.h @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include
+#include
+ +namespace beedb::plan::logical +{ +class NodeInterface; +} + +namespace beedb::io +{ +class ExecutionCallback +{ + public: + ExecutionCallback() = default; + virtual ~ExecutionCallback() = default; + + virtual void on_schema(const table::Schema &schema) = 0; + virtual void on_tuple(const table::Tuple &tuple) = 0; + virtual void on_plan(const std::unique_ptr &plan) = 0; +}; + +class SilentExecutionCallback final : public ExecutionCallback +{ + public: + SilentExecutionCallback() = default; + ~SilentExecutionCallback() override = default; + + void on_schema(const table::Schema &) override + { + } + void on_tuple(const table::Tuple &) override + { + } + void on_plan(const std::unique_ptr &) override + { + } +}; + +class FunctionalExecutionCallback final : public ExecutionCallback +{ + public: + FunctionalExecutionCallback( + std::function &&schema_callback, + std::function &&tuple_callback, + std::function &)> &&plan_callback) + : _schema_callback(std::move(schema_callback)), _tuple_callback(std::move(tuple_callback)), + _plan_callback(std::move(plan_callback)) + { + } + + FunctionalExecutionCallback(std::function &&schema_callback, + std::function &&tuple_callback) + : FunctionalExecutionCallback(std::move(schema_callback), std::move(tuple_callback), + [](const std::unique_ptr &) {}) + { + } + + ~FunctionalExecutionCallback() override = default; + + void on_schema(const table::Schema &schema) override + { + _schema_callback(schema); + } + + void on_tuple(const table::Tuple &tuple) override + { + _tuple_callback(tuple); + } + + void on_plan(const std::unique_ptr &plan) override + { + _plan_callback(plan); + } + + private: + std::function _schema_callback; + std::function _tuple_callback; + std::function &)> _plan_callback; +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/executor.h b/include/io/executor.h new file mode 100644 index 0000000..8f9aa32 --- /dev/null +++ b/include/io/executor.h @@ -0,0 +1,157 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "execution_callback.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::io +{ +/** + * @brief The Query struct contains the query-string and some parameters + * that influence the execution of a query, e.g. printing the logical plan + */ +struct Query +{ + enum ExplainLevel + { + None, + Plan, + Graph + }; + + const std::string query_string; + ExplainLevel explain = None; +}; + +/** + * Wrapper for time and performance statistics, measured + * while query execution. + */ +class ExecutionResult +{ + public: + explicit ExecutionResult(std::string &&error) : _is_successful(false), _error(std::move(error)) + { + } + + ExecutionResult(const std::uint64_t count_tuples, const std::chrono::milliseconds build_time, + const std::chrono::milliseconds execution_time, const std::size_t evicted_pages) + : _is_successful(true), _count_tuples(count_tuples), _build_ms(build_time), _execution_ms(execution_time), + _evicted_pages(evicted_pages) + { + } + + ~ExecutionResult() = default; + + [[nodiscard]] std::chrono::milliseconds build_time() const + { + return _build_ms; + } + + [[nodiscard]] std::chrono::milliseconds execution_time() const + { + return _execution_ms; + } + + [[nodiscard]] std::size_t evicted_pages() const + { + return _evicted_pages; + } + + [[nodiscard]] bool is_successful() const + { + return _is_successful; + } + + [[nodiscard]] const std::string &error() const + { + return _error; + } + + [[nodiscard]] std::uint64_t count_tuples() const + { + return _count_tuples; + } + + private: + const bool _is_successful = false; + const std::string _error; + const std::uint64_t _count_tuples = 0u; + const std::chrono::milliseconds _build_ms{}; + const std::chrono::milliseconds _execution_ms{}; + const std::size_t _evicted_pages = 0u; +}; + +/** + * Executes queries and query plans. + */ +class Executor +{ + public: + explicit Executor(Database &database, concurrency::Transaction *transaction = nullptr) + : _database(database), _transaction(transaction) + { + } + + virtual ~Executor() = default; + + ExecutionResult execute(const Query &query, ExecutionCallback &execution_callback, + concurrency::TransactionCallback &transaction_callback); + + ExecutionResult execute(const Query &query, concurrency::TransactionCallback &transaction_callback) + { + SilentExecutionCallback execution_callback; + return execute(query, execution_callback, transaction_callback); + } + + ExecutionResult execute(const Query &query, ExecutionCallback &execution_callback) + { + concurrency::SilentTransactionCallback silent_transaction_callback; + return execute(query, execution_callback, silent_transaction_callback); + } + + ExecutionResult execute(const Query &query) + { + SilentExecutionCallback silent_execution_callback; + concurrency::SilentTransactionCallback silent_transaction_callback; + return execute(query, silent_execution_callback, silent_transaction_callback); + } + + ExecutionResult execute(plan::physical::Plan &plan, ExecutionCallback &execution_callback); + + ExecutionResult execute(plan::physical::Plan &plan) + { + SilentExecutionCallback execution_callback; + return execute(plan, execution_callback); + } + + protected: + Database &_database; + concurrency::Transaction *_transaction; +}; +} // namespace beedb::io diff --git a/include/io/file_executor.h b/include/io/file_executor.h new file mode 100644 index 0000000..f1f1ce0 --- /dev/null +++ b/include/io/file_executor.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "executor.h" +#include +#include +#include + +namespace beedb::io +{ +/** + * Parses files for SQL-queries and executes them. + */ +class FileExecutor : private Executor +{ + public: + FileExecutor(Database &database) : Executor(database, database.transaction_manager().new_transaction()) + { + } + virtual ~FileExecutor() = default; + + void execute(const std::string &file_name); + + protected: + void execute_sql_file(std::ifstream &&file); + void execute_tbl_file(const std::string &file_name, std::ifstream &&file); + void execute_statements(std::vector &&statements); +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/query_result_serializer.h b/include/io/query_result_serializer.h new file mode 100644 index 0000000..3f95296 --- /dev/null +++ b/include/io/query_result_serializer.h @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "execution_callback.h" +#include +#include +#include
+#include
+#include + +namespace beedb::io +{ + +/** + * Results of queries are serialized as follows: + * - First 2 bytes: Number of columns + * - For every column: + * - the type (table::Type) + * - the offset (std::size_t) + * - the column order index (std::uint16_t) + * - the null-terminated name of the column + * - After the schema, the bytes of the tuples will serialized. + */ +class QueryResultSerializer +{ + public: + QueryResultSerializer(); + ~QueryResultSerializer(); + + void serialize(const table::Schema &schema); + void serialize(const table::Tuple &tuple); + + [[nodiscard]] std::byte *data() + { + return _data; + } + [[nodiscard]] std::size_t size() const + { + return _size; + } + + [[nodiscard]] bool empty() const + { + return _size == 0u; + } + + private: + std::byte *_data = nullptr; + std::size_t _size = 0u; + std::size_t _capacity = 0u; + + void append(const std::byte *data, const std::uint16_t size); + void prepend(const std::byte *data, const std::uint16_t size); + + void allocate(const std::size_t capacity); +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/result_output_formatter.h b/include/io/result_output_formatter.h new file mode 100644 index 0000000..e0a657d --- /dev/null +++ b/include/io/result_output_formatter.h @@ -0,0 +1,109 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "execution_callback.h" +#include +#include +#include +#include
+#include
+#include +#include + +namespace beedb::io +{ +/** + * Formats the result of a query, given by schema and set of tuples. + */ +class ResultOutputFormatter final : public ExecutionCallback +{ + friend std::ostream &operator<<(std::ostream &stream, const ResultOutputFormatter &result_output_formatter); + + public: + ResultOutputFormatter() = default; + ~ResultOutputFormatter() override = default; + + static ResultOutputFormatter from_serialized_data(const std::size_t count_tuples, const std::byte *data); + + void on_schema(const table::Schema &schema) override + { + header(schema); + } + void on_tuple(const table::Tuple &tuple) override + { + push_back(tuple); + } + + void on_plan(const std::unique_ptr &) override + { + } + + /** + * Set the header for the output. + * + * @param schema Schema for the header. + */ + void header(const table::Schema &schema); + + /** + * Add a set of tuples to the result. + * @param tuples Set of tuples. + */ + void push_back(const std::vector &tuples); + + /** + * Add a single tuple to the result. + * @param tuple + */ + void push_back(const table::Tuple &tuple); + + /** + * Clear the formatter. + */ + inline void clear() + { + _table.clear(); + } + + /** + * @return True, when no tuple as added. + */ + inline bool empty() const + { + return _table.empty(); + } + + /** + * @return Number of added tuples. + */ + inline std::size_t count() const + { + return _count_tuples; + } + + private: + util::TextTable _table; + std::size_t _count_tuples = 0u; +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/io/server_response.h b/include/io/server_response.h new file mode 100644 index 0000000..149011d --- /dev/null +++ b/include/io/server_response.h @@ -0,0 +1,198 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "query_result_serializer.h" +#include +#include +#include +#include +#include + +namespace beedb::io +{ + +class ServerResponse +{ + public: + enum Type : std::uint8_t + { + Empty, + Error, + AnalyticalResult, + TransactionalResult, + PlanExplanation, + ServerClosed + }; + + ServerResponse(const Type type, const std::uint64_t execution_time_in_ms) + : _type(type), _execution_time_in_ms(execution_time_in_ms) + { + } + + virtual ~ServerResponse() = default; + + [[nodiscard]] Type type() const + { + return _type; + } + [[nodiscard]] std::uint64_t execution_time_in_ms() const + { + return _execution_time_in_ms; + } + + private: + const Type _type; + const std::uint64_t _execution_time_in_ms; +}; + +class EmptyResponse final : public ServerResponse +{ + public: + EmptyResponse() : ServerResponse(Type::Empty, 0u) + { + } + ~EmptyResponse() override = default; + + static std::string build() + { + std::string response = std::string(sizeof(EmptyResponse), '\0'); + new (response.data()) EmptyResponse(); + return response; + } +}; + +class ErrorResponse final : public ServerResponse +{ + public: + ErrorResponse() : ServerResponse(Type::Error, 0u) + { + } + + ~ErrorResponse() override = default; + + static std::string build(std::string_view &&error_message) + { + std::string response = std::string(sizeof(ErrorResponse) + error_message.length(), '\0'); + new (response.data()) ErrorResponse(); + std::memcpy(response.data() + sizeof(ErrorResponse), error_message.data(), error_message.size()); + return response; + } + + [[nodiscard]] const char *message() const + { + return reinterpret_cast(this + 1); + } +}; + +class AnalyticalResponse final : public ServerResponse +{ + public: + AnalyticalResponse(const std::uint64_t execution_time_in_ms, const std::uint64_t count_rows) + : ServerResponse(Type::AnalyticalResult, execution_time_in_ms), _count_rows(count_rows) + { + } + + ~AnalyticalResponse() override = default; + + [[nodiscard]] std::uint64_t count_rows() const + { + return _count_rows; + } + + static std::string build(const std::uint64_t execution_time_in_ms, const std::uint64_t count_rows, + QueryResultSerializer &&query_serializer) + { + std::string response = std::string(sizeof(AnalyticalResponse) + query_serializer.size(), '\0'); + new (response.data()) AnalyticalResponse(execution_time_in_ms, count_rows); + std::memmove(response.data() + sizeof(AnalyticalResponse), query_serializer.data(), query_serializer.size()); + return response; + } + + [[nodiscard]] const std::byte *data() const + { + return reinterpret_cast(this + 1); + } + + private: + const std::uint64_t _count_rows; +}; + +class TransactionalResponse final : public ServerResponse +{ + public: + TransactionalResponse(const std::uint64_t execution_time_in_ms, const std::uint64_t count_rows) + : ServerResponse(Type::TransactionalResult, execution_time_in_ms), _count_rows(count_rows) + { + } + + ~TransactionalResponse() override = default; + + [[nodiscard]] std::uint64_t affected_rows() const + { + return _count_rows; + } + + private: + const std::uint64_t _count_rows; +}; + +class QueryPlanResponse final : public ServerResponse +{ + public: + QueryPlanResponse() : ServerResponse(Type::PlanExplanation, 0u) + { + } + + ~QueryPlanResponse() override = default; + + static std::string build(std::string &&plan) + { + std::string response = std::string(sizeof(QueryPlanResponse) + plan.length(), '\0'); + new (response.data()) QueryPlanResponse(); + std::memmove(response.data() + sizeof(QueryPlanResponse), plan.data(), plan.size()); + return response; + } + + const char *payload() const + { + return reinterpret_cast(this + 1); + } +}; + +class ServerClosedResponse final : public ServerResponse +{ + public: + ServerClosedResponse() : ServerResponse(Type::ServerClosed, 0U) + { + } + + ~ServerClosedResponse() override = default; + + static std::string build() + { + std::string response = std::string(sizeof(ServerClosedResponse), '\0'); + new (response.data()) ServerClosedResponse(); + return response; + } +}; +} // namespace beedb::io \ No newline at end of file diff --git a/include/network/client.h b/include/network/client.h new file mode 100644 index 0000000..ed1fe35 --- /dev/null +++ b/include/network/client.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include + +namespace beedb::network +{ +class Client +{ + public: + Client(std::string &&server, std::uint16_t port); + ~Client() = default; + + bool connect(); + void disconnect() const; + std::string send(const std::string &message); + + [[nodiscard]] const std::string &server() const + { + return _server; + } + + [[nodiscard]] std::uint16_t port() const + { + return _port; + } + + private: + const std::string _server; + const std::uint16_t _port; + std::int32_t _socket; + + std::uint64_t read_into_buffer(std::uint64_t length, void *buffer) const; +}; +} // namespace beedb::network \ No newline at end of file diff --git a/include/network/server.h b/include/network/server.h new file mode 100644 index 0000000..995e1ef --- /dev/null +++ b/include/network/server.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace beedb::network +{ +class Server; +class ClientHandler +{ + public: + ClientHandler() noexcept = default; + virtual ~ClientHandler() noexcept = default; + virtual std::optional handle_message(const std::uint32_t client_id, const std::string &message) = 0; + virtual void on_client_connected(const std::uint32_t id) = 0; + virtual void on_client_disconnected(const std::uint32_t id) = 0; + virtual void server(Server *server) + { + _server = server; + } + + protected: + [[nodiscard]] Server *server() const + { + return _server; + } + + private: + Server *_server = nullptr; +}; + +class Server +{ + public: + Server(ClientHandler &message_handler, std::uint16_t port) noexcept; + ~Server() noexcept = default; + + [[nodiscard]] std::uint16_t port() const noexcept + { + return _port; + } + [[nodiscard]] bool is_running() const noexcept + { + return _is_running; + } + void stop() noexcept + { + _is_running = false; + } + void send(std::uint32_t client_id, std::string &&message); + bool listen(); + + private: + const std::uint16_t _port; + std::int32_t _socket; + std::array _client_sockets; + std::array _buffer; + ClientHandler &_handler; + + alignas(64) bool _is_running = true; + + std::uint16_t add_client(std::int32_t client_socket); +}; +} // namespace beedb::network \ No newline at end of file diff --git a/include/parser/.gitignore b/include/parser/.gitignore new file mode 100644 index 0000000..4b8bd76 --- /dev/null +++ b/include/parser/.gitignore @@ -0,0 +1,2 @@ +location.hh +parser.hpp \ No newline at end of file diff --git a/include/parser/driver.h b/include/parser/driver.h new file mode 100644 index 0000000..bbbaca0 --- /dev/null +++ b/include/parser/driver.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "location.hh" +#include "node.h" +#include +#include +#include + +namespace beedb::parser +{ + +class Parser; + +class Driver +{ + public: + Driver() noexcept = default; + ~Driver() noexcept = default; + + int parse(std::istream &&in); + + [[nodiscard]] const std::unique_ptr &ast() const + { + return _root; + } + [[nodiscard]] std::unique_ptr &ast() + { + return _root; + } + + void ast(std::unique_ptr &&root) + { + _root = std::move(root); + } + friend class Parser; + friend class Scanner; + + private: + std::unique_ptr _root; +}; +} // namespace beedb::parser diff --git a/include/parser/node.h b/include/parser/node.h new file mode 100644 index 0000000..e1a1321 --- /dev/null +++ b/include/parser/node.h @@ -0,0 +1,291 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include
+#include
+#include +namespace beedb::parser +{ + +using Alias = std::optional; +using TableDescr = std::pair; + +using JoinDescr = std::pair>; + +using WhereExpression = std::unique_ptr; + +using GroupByExpression = std::vector; + +using OrderByItem = std::pair, bool>; +using OrderByExpression = std::vector; + +struct LimitExpression +{ + std::uint64_t limit; + std::uint64_t offset; +}; + +class NodeInterface +{ + public: + NodeInterface() noexcept = default; + virtual ~NodeInterface() noexcept = default; +}; + +class CreateTableStatement final : public NodeInterface +{ + public: + CreateTableStatement(std::string &&table_name, const bool if_not_exists, table::Schema &&schema) noexcept + : _table_name(std::move(table_name)), _if_not_exists(if_not_exists), _schema(std::move(schema)) + { + } + ~CreateTableStatement() noexcept override = default; + + [[nodiscard]] std::string &table_name() noexcept + { + return _table_name; + } + [[nodiscard]] bool if_not_exists() const noexcept + { + return _if_not_exists; + } + [[nodiscard]] table::Schema &schema() + { + return _schema; + } + + private: + std::string _table_name; + bool _if_not_exists; + table::Schema _schema; +}; + +class CreateIndexStatement final : public NodeInterface +{ + public: + CreateIndexStatement(std::string &&index_name, std::string &&table_name, std::string &&column_name, + const bool is_unique) noexcept + : _index_name(std::move(index_name)), _table_name(std::move(table_name)), _column_name(std::move(column_name)), + _is_unique(is_unique) + { + } + ~CreateIndexStatement() noexcept override = default; + + [[nodiscard]] std::string &index_name() noexcept + { + return _index_name; + } + [[nodiscard]] std::string &table_name() noexcept + { + return _table_name; + } + [[nodiscard]] std::string &column_name() noexcept + { + return _column_name; + } + [[nodiscard]] bool is_unique() const noexcept + { + return _is_unique; + } + + private: + std::string _index_name; + std::string _table_name; + std::string _column_name; + bool _is_unique; +}; + +class InsertStatement final : public NodeInterface +{ + public: + InsertStatement(std::string &&table_name, std::vector &&column_names, + std::vector> &&values) noexcept + : _table_name(std::move(table_name)), _column_names(std::move(column_names)), _values(std::move(values)) + { + } + ~InsertStatement() noexcept override = default; + + [[nodiscard]] std::string &table_name() noexcept + { + return _table_name; + } + [[nodiscard]] std::vector &column_names() noexcept + { + return _column_names; + } + [[nodiscard]] std::vector> &values() noexcept + { + return _values; + } + + private: + std::string _table_name; + std::vector _column_names; + std::vector> _values; +}; + +class UpdateStatement final : public NodeInterface +{ + public: + UpdateStatement(std::string &&table_name, + std::vector>> &&updates, + WhereExpression &&where) noexcept + : _table_name(std::move(table_name)), _updates(std::move(updates)), _where(std::move(where)) + { + } + ~UpdateStatement() noexcept override = default; + + [[nodiscard]] std::string &table_name() noexcept + { + return _table_name; + } + [[nodiscard]] std::vector>> + &updates() noexcept + { + return _updates; + } + [[nodiscard]] WhereExpression &where() noexcept + { + return _where; + } + + private: + std::string _table_name; + std::vector>> _updates; + WhereExpression _where; +}; + +class DeleteStatement final : public NodeInterface +{ + public: + DeleteStatement(std::string &&table_name, WhereExpression &&where) noexcept + : _table_name(std::move(table_name)), _where(std::move(where)) + { + } + ~DeleteStatement() noexcept override = default; + + [[nodiscard]] std::string &table_name() noexcept + { + return _table_name; + } + [[nodiscard]] WhereExpression &where() noexcept + { + return _where; + } + + private: + std::string _table_name; + WhereExpression _where; +}; + +class TransactionStatement final : public NodeInterface +{ + public: + enum Type + { + BeginTransaction, + CommitTransaction, + AbortTransaction + }; + + explicit TransactionStatement(const Type type) : _type(type) + { + } + + ~TransactionStatement() override = default; + + [[nodiscard]] bool is_begin() const noexcept + { + return _type == Type::BeginTransaction; + } + [[nodiscard]] bool is_commit() const noexcept + { + return _type == Type::CommitTransaction; + } + [[nodiscard]] bool is_abort() const noexcept + { + return _type == Type::AbortTransaction; + } + + private: + const Type _type; +}; + +class SelectQuery final : public NodeInterface +{ + public: + SelectQuery(std::vector> &&attributes, std::vector &&from, + std::optional> &&join, WhereExpression &&where, + std::optional &&group_by, std::optional &&order_by, + std::optional &&limit) noexcept + : _attributes(std::move(attributes)), _from(std::move(from)), _join(std::move(join)), _where(std::move(where)), + _group_by(std::move(group_by)), _order_by(std::move(order_by)), _limit(limit) + { + } + + ~SelectQuery() noexcept override = default; + + [[nodiscard]] std::vector> &attributes() noexcept + { + return _attributes; + } + [[nodiscard]] std::vector &from() noexcept + { + return _from; + } + [[nodiscard]] std::optional> &join() noexcept + { + return _join; + } + [[nodiscard]] WhereExpression &where() noexcept + { + return _where; + } + [[nodiscard]] std::optional &group_by() noexcept + { + return _group_by; + } + [[nodiscard]] std::optional &order_by() noexcept + { + return _order_by; + } + [[nodiscard]] std::optional &limit() noexcept + { + return _limit; + } + + private: + std::vector> _attributes; + std::vector _from; + std::optional> _join; + WhereExpression _where; + std::optional _group_by; + std::optional _order_by; + std::optional _limit; +}; +} // namespace beedb::parser \ No newline at end of file diff --git a/include/parser/scanner.hpp b/include/parser/scanner.hpp new file mode 100644 index 0000000..dbf5e91 --- /dev/null +++ b/include/parser/scanner.hpp @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#undef YY_DECL +#define YY_DECL db::parser::symbol_type db::Scanner::lex() + +#include + +#ifndef yyFlexLexerOnce +#include +#endif + +#include "location.hh" +#include "parser.hpp" + +namespace beedb::parser +{ + +// forward declare to avoid an include +class Driver; + +class Scanner : public yyFlexLexer +{ + public: + explicit Scanner(std::istream &stream) : yyFlexLexer(stream, std::cout) + { + } + ~Scanner() override + { + } + Parser::symbol_type lex(Driver &driver); +}; +} // namespace beedb::parser diff --git a/include/parser/sql_parser.h b/include/parser/sql_parser.h new file mode 100644 index 0000000..0e03cd0 --- /dev/null +++ b/include/parser/sql_parser.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "driver.h" +#include "node.h" +#include +#include + +namespace beedb::parser +{ +class SQLParser +{ + public: + SQLParser() noexcept = default; + ~SQLParser() noexcept = default; + + std::unique_ptr parse(std::string &&query); + + private: + Driver _driver; +}; +} // namespace beedb::parser \ No newline at end of file diff --git a/include/plan/graph/basic_graph.h b/include/plan/graph/basic_graph.h new file mode 100644 index 0000000..f528740 --- /dev/null +++ b/include/plan/graph/basic_graph.h @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::util +{ +/** + * @brief The Graph class represents a directed graph with "weighted edges". + * + * Individual node store objects of type NodeData and have a unique id of type NodeIDType. + * Edges are directional and carry objects of type EdgeData. + * There can only be one edge from node A to node B. + * + * This class makes extensive use of the subscript operator[]. Edges and nodes can be created and accessed by simply + * using the operator. For example: `graph[edge] = edge_value`, `NodeDataType value = graph[node]` or + * `graph[edge].remove()`. + */ +template class Graph +{ + public: + using EdgeID = std::pair; // edge is a pair: + using NodeIDs = std::vector; // a collection of node id's + + Graph() = default; + Graph(Graph &&) noexcept = default; + Graph(const Graph &) = delete; + virtual ~Graph() = default; + + protected: + struct EdgeIdHash + { + std::size_t operator()(const EdgeID &id) const + { + return std::hash()(std::get<0>(id)) ^ std::hash()(std::get<1>(id)); + } + }; + + using NodeData = std::unordered_map>; // holds actual node data + using EdgeData = std::unordered_map; // holds actual edge data + using NodeIDMap = std::unordered_map; // used for aux. data structures + + /** + * @brief The Edges struct Proxy data structure that helps implement + * the subscript-assignement operators for edges. This is more complicated + * (compared to node data), because we make use of auxillary data + * structures for edges (incoming and outgoing edges). + */ + class Edge + { + public: + Edge(Graph *ref, const EdgeID &edge) : _ref(ref), _edge(edge) + { + } + ~Edge() = default; + /** + * @brief operator = + * This gets called, when a call similar to + * `graph[{A,B}] = edge_value;` (clone-assignement) is made. + * + * This structure is required to keep auxillary structures (incomin- and outgoing nodes) consistent! + * + * @param data + */ + void operator=(const EdgeDataType &data) + { + // check graph consistency: we can not add an edge to nodes that dont exist: + if (_ref->_node_data.find(_edge.first) == _ref->_node_data.end() || + _ref->_node_data.find(_edge.second) == _ref->_node_data.end()) + { + + std::cerr << "ERROR: Trying to add edge, but nodes do not exist in graph!" + << " Edge is < " << _edge.first << " , " << _edge.second << " >" << std::endl; + + return; + } + + // the assertions test, if all auxillary structures are properly initialized: + assert(_ref->_incoming_node_ids.find(_edge.first) != _ref->_incoming_node_ids.end()); + assert(_ref->_incoming_node_ids.find(_edge.second) != _ref->_incoming_node_ids.end()); + assert(_ref->_outgoing_node_ids.find(_edge.first) != _ref->_outgoing_node_ids.end()); + assert(_ref->_outgoing_node_ids.find(_edge.second) != _ref->_outgoing_node_ids.end()); + + // create edge: + _ref->_outgoing_node_ids[_edge.first].push_back(_edge.second); + _ref->_incoming_node_ids[_edge.second].push_back(_edge.first); + _ref->_edge_data.insert({_edge, data}); + } + /** + * @brief operator EdgeData & conversion operator, that enables the + * subscript operator to be used as a simple getter for the data behind + * this proxy object. + */ + explicit operator const EdgeDataType &() const + { + return _ref->_edge_data[_edge]; + } + /** + * @brief remove just removes this edge and its data. + * + * We also remove it from auxillary data structures ("incoming" and "outgoing" nodes). + */ + void remove() + { + // first, remove edge and its data + _ref->_edge_data.erase(_edge); + // then remove id from auxillary edge-structures: + auto &out_nodes = _ref->_outgoing_node_ids[_edge.first]; + out_nodes.erase(std::remove(out_nodes.begin(), out_nodes.end(), _edge.second), out_nodes.end()); + + auto &in_nodes = _ref->_incoming_node_ids[_edge.second]; + in_nodes.erase(std::remove(in_nodes.begin(), in_nodes.end(), _edge.second), in_nodes.end()); + } + + private: + Graph *_ref; + const EdgeID &_edge; + }; + + class Node + { + public: + Node(Graph *ref, const NodeIDType &nid) : _ref(ref), _nid(nid) + { + } + /** + * @brief operator = + * This gets called, when a call similar to + * `graph[node_id] = node_data;` is made. + * + * This structure is required to keep auxillary structures (incomin- and outgoing nodes) consistent! + * + * @param data + */ + void operator=(std::unique_ptr data) + { + _ref->insert({_nid, std::move(data)}); + } + /** + * @brief operator NodeDataType & conversion operator, that enables the + * subscript operator to be used as a simple getter for the data behind + * this proxy object. + */ + explicit operator std::unique_ptr &() + { + return _ref->_node_data[_nid]; + } + + explicit operator const NodeDataType &() const + { + return **(_ref->_node_data[_nid]); + } + + [[maybe_unused]] const NodeIDs &incomingNodes() const + { + return _ref->_incoming_node_ids[_nid]; + } + [[maybe_unused]] const NodeIDs &outgoingNodes() const + { + return _ref->_outgoing_node_ids[_nid]; + } + /** + * @brief remove removes this node and all connected edges. + */ + void remove() + { + // remove node data: + _ref->_node_data.erase(_nid); + + // remove incoming edges: + for (auto &other_node_id : _ref->_incoming_node_ids[_nid]) + { + _ref->_edge_data.erase({other_node_id, _nid}); // actual edge + + // remove edges to this node from _outgoing_node_ids of other_node: + NodeIDs &out_nodes = _ref->_outgoing_node_ids[other_node_id]; + out_nodes.erase(std::remove(out_nodes.begin(), out_nodes.end(), _nid), out_nodes.end()); + } + // remove outgoing edges: + for (auto &other_node_id : _ref->_outgoing_node_ids[_nid]) + { + _ref->_edge_data.erase({_nid, other_node_id}); // actual edge + + // remove edges to this node from _incoming_node_ids of other_node: + NodeIDs &in_nodes = _ref->_incoming_node_ids[other_node_id]; + in_nodes.erase(std::remove(in_nodes.begin(), in_nodes.end(), _nid), in_nodes.end()); + } + // remove auxillary entries for this node: + _ref->_incoming_node_ids.erase(_nid); + _ref->_outgoing_node_ids.erase(_nid); + } + + private: + Graph *_ref; + const NodeIDType &_nid; + }; + + public: + // getter and setter for nodes/their data. can change node's data + Node operator[](const NodeIDType &nid) + { + return Node(this, nid); + } + + const NodeIDType &insert( + typename NodeData::value_type &&map_pair) // parameter type is a std::pair + { + const NodeIDType &id = _node_data.insert({map_pair.first, std::move(map_pair.second)}).first->first; + _incoming_node_ids.insert({id, {}}); + _outgoing_node_ids.insert({id, {}}); + return id; + } + + // simple read-only getter for node-data + const std::unique_ptr &operator[](const NodeIDType &nid) const + { + return _node_data.at(nid); + } + + // getter for mutable edge-data. Access via pair + Edge operator[](const EdgeID &edge) + { + return Edge(this, edge); + } + // simple read-only getter for edge-data. Access via pair + const EdgeDataType &operator[](const EdgeID &edge_id) const + { + return _edge_data[edge_id]; + } + /** + * @brief toConsole exhaustive dump of all node- and edge ids to console, without data. + */ + void toConsole() const + { + std::cout << "Nodes:" << std::endl; + for (const auto &node : _node_data) + { + std::cout << "\t{" << node.first << ": " << static_cast(*(node.second)) << "}" << std::endl; + } + std::cout << "Edges:" << std::endl; + for (auto const &edge : _edge_data) + { + std::cout << "\t<" << edge.first.first << "," << edge.first.second << ">" << std::endl; + } + } + + [[maybe_unused]] [[nodiscard]] const NodeIDs &outgoing_nodes(const NodeIDType &nid) const + { + return _outgoing_node_ids.at(nid); + } + + [[nodiscard]] const NodeIDs &incoming_nodes(const NodeIDType &nid) const + { + return _incoming_node_ids.at(nid); + } + + [[nodiscard]] bool empty() const + { + return _node_data.empty(); + } + + protected: // actual graph data should be available to daughter classes + NodeData _node_data; // contains node data. key is a NodeIDType + EdgeData _edge_data; // contains edge data. key is a pair + NodeIDMap _outgoing_node_ids; // for efficient connectivity lookup + NodeIDMap _incoming_node_ids; // for efficient connectivity lookup +}; + +} // namespace beedb::util diff --git a/include/plan/logical/builder.h b/include/plan/logical/builder.h new file mode 100644 index 0000000..0c1287c --- /dev/null +++ b/include/plan/logical/builder.h @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "node/node_interface.h" +#include +#include + +namespace beedb::plan::logical +{ +class Builder +{ + public: + static std::unique_ptr build(Database &database, + const std::unique_ptr &query); + static std::unique_ptr build(Database &database, parser::SelectQuery *select_query); + static std::unique_ptr build(Database &database, parser::CreateTableStatement *create_statement); + static std::unique_ptr build(Database &database, + parser::CreateIndexStatement *create_index_statement); + static std::unique_ptr build(Database &database, parser::InsertStatement *insert_statement); + static std::unique_ptr build(Database &database, parser::UpdateStatement *update_statement); + static std::unique_ptr build(Database &database, parser::DeleteStatement *delete_statement); + static std::unique_ptr build(Database &database, + parser::TransactionStatement *transaction_statement); + + private: + [[nodiscard]] static std::unique_ptr create_from( + Database &database, std::vector &&from, + std::optional> &&join); + [[nodiscard]] static std::unique_ptr parse_select_expression( + std::unique_ptr &&top_node, std::vector &projection_terms, + std::vector> &aggregations, + std::unique_ptr &&select_expression, bool add_to_projection); + [[nodiscard]] static std::vector> split_logical_and( + std::unique_ptr &&root); + static void split_logical_and(std::unique_ptr &&root, + std::vector> &container); +}; +} // namespace beedb::plan::logical diff --git a/include/plan/logical/node/aggregation_node.h b/include/plan/logical/node/aggregation_node.h new file mode 100644 index 0000000..269687e --- /dev/null +++ b/include/plan/logical/node/aggregation_node.h @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class AggregationNode final : public UnaryNode +{ + public: + AggregationNode(std::vector> &&aggregations, + std::vector &&group_by) + : UnaryNode("Aggregation"), _aggregation_expressions(std::move(aggregations)), + _group_expressions(std::move(group_by)) + { + } + ~AggregationNode() override = default; + + [[nodiscard]] const std::vector> &aggregation_expressions() const + { + return _aggregation_expressions; + } + [[nodiscard]] const std::vector &group_expressions() const + { + return _group_expressions; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + child()->check_and_emit_schema(tables); + + for (auto &group : _group_expressions) + { + tables.check_and_replace_table(group.get()); + } + + for (const auto &aggregation : _aggregation_expressions) + { + if (aggregation->result().has_value()) + { + _schema.push_back(aggregation->result().value()); + } + } + + std::copy(_group_expressions.begin(), _group_expressions.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + std::vector> _aggregation_expressions; + std::vector _group_expressions; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/arithmetic_node.h b/include/plan/logical/node/arithmetic_node.h new file mode 100644 index 0000000..ad25beb --- /dev/null +++ b/include/plan/logical/node/arithmetic_node.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class ArithmeticNode final : public UnaryNode +{ + public: + explicit ArithmeticNode(std::unique_ptr &&expression) + : UnaryNode("Arithmetic"), _expression(std::move(expression)) + { + } + ~ArithmeticNode() override = default; + + [[nodiscard]] const std::unique_ptr &expression() const + { + return _expression; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &child_schema = child()->check_and_emit_schema(tables); + + // TODO: Check + tables.check_and_replace_table(_expression); + + std::copy(child_schema.begin(), child_schema.end(), std::back_inserter(_schema)); + if (_expression->result().has_value()) + { + _schema.push_back(_expression->result().value()); + } + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + std::unique_ptr _expression{}; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/create_index_node.h b/include/plan/logical/node/create_index_node.h new file mode 100644 index 0000000..9db135e --- /dev/null +++ b/include/plan/logical/node/create_index_node.h @@ -0,0 +1,79 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" +#include + +namespace beedb::plan::logical +{ +class CreateIndexNode final : public NotSchematizedNode +{ + public: + CreateIndexNode(Database &database, expression::Attribute &&attribute, std::string &&index_name, + const index::Type index_type, const bool is_unique) + : NotSchematizedNode("Create Index"), _database(database), _attribute(std::move(attribute)), + _index_name(std::move(index_name)), _index_type(index_type), _is_unique(is_unique) + { + } + ~CreateIndexNode() override = default; + + [[nodiscard]] const expression::Attribute &attribute() const + { + return _attribute; + } + [[nodiscard]] const std::string &index_name() const + { + return _index_name; + } + [[nodiscard]] bool is_unique() const + { + return _is_unique; + } + [[nodiscard]] index::Type index_type() const + { + return _index_type; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + if (_database.table_exists(_attribute.table_name().value()) == false) + { + throw exception::TableNotFoundException(_attribute.table_name().value()); + } + + tables.insert(_database.table(_attribute.table_name().value()), _attribute.table_name().value()); + + tables.check_and_replace_table(_attribute); + + return NotSchematizedNode::check_and_emit_schema(tables); + } + + private: + Database &_database; + expression::Attribute _attribute; + std::string _index_name; + index::Type _index_type; + bool _is_unique; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/create_table_node.h b/include/plan/logical/node/create_table_node.h new file mode 100644 index 0000000..0217a90 --- /dev/null +++ b/include/plan/logical/node/create_table_node.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class CreateTableNode final : public NotSchematizedNode +{ + public: + CreateTableNode(Database &database, std::string &&table_name, table::Schema &&schema) + : NotSchematizedNode("Create Table"), _database(database), _table_name(std::move(table_name)), + _schema(std::move(schema)) + { + } + ~CreateTableNode() override = default; + + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + + [[nodiscard]] std::string &table_name() + { + return _table_name; + } + + [[nodiscard]] const table::Schema &table_schema() const + { + return _schema; + } + [[nodiscard]] table::Schema &table_schema() + { + return _schema; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + if (_database.table_exists(_table_name)) + { + throw exception::TableAlreadyExists(_table_name); + } + + return NotSchematizedNode::check_and_emit_schema(tables); + } + + private: + Database &_database; + std::string _table_name; + table::Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/cross_product_node.h b/include/plan/logical/node/cross_product_node.h new file mode 100644 index 0000000..2aca118 --- /dev/null +++ b/include/plan/logical/node/cross_product_node.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class CrossProductNode final : public BinaryNode +{ + public: + CrossProductNode() : BinaryNode("Cross Product") + { + } + + ~CrossProductNode() override = default; + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &left_schema = left_child()->check_and_emit_schema(tables); + const auto &right_schema = right_child()->check_and_emit_schema(tables); + + _schema.reserve(left_schema.size() + right_schema.size()); + std::copy(left_schema.begin(), left_schema.end(), std::back_inserter(_schema)); + std::copy(right_schema.begin(), right_schema.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/delete_node.h b/include/plan/logical/node/delete_node.h new file mode 100644 index 0000000..10ad6a2 --- /dev/null +++ b/include/plan/logical/node/delete_node.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class DeleteNode final : public UnaryNode +{ + public: + DeleteNode() : UnaryNode("Delete") + { + } + + ~DeleteNode() override = default; + + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + const auto &child = UnaryNode::child()->check_and_emit_schema(tables); + + const auto all_tables = tables.tables(); + _table_name = all_tables.front(); + + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + Schema _schema; + std::string _table_name; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/insert_node.h b/include/plan/logical/node/insert_node.h new file mode 100644 index 0000000..c22e6b4 --- /dev/null +++ b/include/plan/logical/node/insert_node.h @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class InsertNode final : public NotSchematizedNode +{ + public: + InsertNode(Database &database, std::string &&table_name, std::vector &&column_names, + std::vector> &&value_lists) + : NotSchematizedNode("Insert"), _database(database), _table_name(std::move(table_name)), + _column_names(std::move(column_names)), _value_lists(std::move(value_lists)) + { + } + ~InsertNode() override = default; + + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + [[nodiscard]] const std::vector &column_names() const + { + return _column_names; + } + [[nodiscard]] const std::vector> &value_lists() const + { + return _value_lists; + } + [[nodiscard]] std::vector> &value_lists() + { + return _value_lists; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + if (_database.table_exists(_table_name) == false) + { + throw exception::TableNotFoundException(_table_name); + } + + const auto &table_schema = _database.table(_table_name)->schema(); + for (const auto &column_name : _column_names) + { + if (table_schema.contains(column_name) == false) + { + throw exception::ColumnNotFoundException(_table_name, column_name); + } + } + + for (const auto &term : table_schema.terms()) + { + const auto &column_name = term.get().column_name(); + const auto column = std::find(_column_names.cbegin(), _column_names.cend(), column_name); + if (column == _column_names.cend()) + { + throw exception::ColumnCanNotBeNull(_table_name, column_name); + } + } + + return NotSchematizedNode::check_and_emit_schema(tables); + } + + private: + Database &_database; + std::string _table_name; + std::vector _column_names; + std::vector> _value_lists; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/join_node.h b/include/plan/logical/node/join_node.h new file mode 100644 index 0000000..54dbc7d --- /dev/null +++ b/include/plan/logical/node/join_node.h @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" +#include + +namespace beedb::plan::logical +{ +class AbstractJoinNode : public BinaryNode +{ + public: + explicit AbstractJoinNode(std::string &&name, std::unique_ptr &&predicate) + : BinaryNode(std::move(name)), _predicate(std::move(predicate)) + { + } + + AbstractJoinNode(const AbstractJoinNode &other, std::string &&name) + : AbstractJoinNode(std::move(name), other._predicate->copy()) + { + std::copy(other._schema.begin(), other._schema.end(), std::back_inserter(_schema)); + } + + AbstractJoinNode(const AbstractJoinNode &other, std::string &&name, + std::unique_ptr &&predicate) + : AbstractJoinNode(std::move(name), std::move(predicate)) + { + std::copy(other._schema.begin(), other._schema.end(), std::back_inserter(_schema)); + } + + ~AbstractJoinNode() override = default; + + [[nodiscard]] const std::unique_ptr &predicate() const + { + return _predicate; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &left_schema = left_child()->check_and_emit_schema(tables); + const auto &right_schema = right_child()->check_and_emit_schema(tables); + + expression::for_attribute(_predicate, [&tables](auto &attribute) { + if (attribute.is_asterisk()) + { + throw exception::LogicalException("* is not a valid join predicate."); + } + + tables.check_and_replace_table(attribute); + }); + + _schema.reserve(left_schema.size() + right_schema.size()); + std::copy(left_schema.begin(), left_schema.end(), std::back_inserter(_schema)); + std::copy(right_schema.begin(), right_schema.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + std::unique_ptr _predicate; + Schema _schema; +}; + +class NestedLoopsJoinNode final : public AbstractJoinNode +{ + public: + explicit NestedLoopsJoinNode(std::unique_ptr &&predicate) + : AbstractJoinNode("Nested Loops Join", std::move(predicate)) + { + } + + NestedLoopsJoinNode(const NestedLoopsJoinNode &other, std::unique_ptr &&predicate) + : AbstractJoinNode(other, "Nested Loops Join", std::move(predicate)) + { + } + + ~NestedLoopsJoinNode() override = default; +}; + +class HashJoinNode final : public AbstractJoinNode +{ + public: + explicit HashJoinNode(const NestedLoopsJoinNode &other) : AbstractJoinNode(other, "Hash Join") + { + } + + HashJoinNode(const HashJoinNode &other, std::unique_ptr &&predicate) + : AbstractJoinNode(other, "Hash Join", std::move(predicate)) + { + } + + ~HashJoinNode() override = default; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/limit_node.h b/include/plan/logical/node/limit_node.h new file mode 100644 index 0000000..347132a --- /dev/null +++ b/include/plan/logical/node/limit_node.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class LimitNode final : public UnaryNode +{ + public: + LimitNode(std::uint64_t limit, std::uint64_t offset) : UnaryNode("Limit"), _limit(limit), _offset(offset) + { + } + ~LimitNode() override = default; + + [[nodiscard]] std::uint64_t limit() const + { + return _limit; + } + [[nodiscard]] std::uint64_t offset() const + { + return _offset; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &child_schema = child()->check_and_emit_schema(tables); + std::copy(child_schema.begin(), child_schema.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + [[nodiscard]] nlohmann::json to_json() const override + { + auto json = UnaryNode::to_json(); + json["data"] = std::to_string(_offset) + "," + std::to_string(_limit); + return json; + } + + private: + std::uint64_t _limit; + std::uint64_t _offset; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/node_interface.h b/include/plan/logical/node/node_interface.h new file mode 100644 index 0000000..095e5ac --- /dev/null +++ b/include/plan/logical/node/node_interface.h @@ -0,0 +1,196 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "schema.h" +#include "table.h" +#include +#include +#include +#include +#include + +namespace beedb::plan::logical +{ +class NodeInterface +{ + public: + explicit NodeInterface(std::string &&name) : _name(std::move(name)) + { + } + virtual ~NodeInterface() = default; + + [[nodiscard]] virtual bool is_unary() const + { + return false; + } + [[nodiscard]] virtual bool is_binary() const + { + return false; + } + + [[nodiscard]] const std::string &name() const + { + return _name; + } + virtual const Schema &check_and_emit_schema(TableMap &tables) = 0; + [[nodiscard]] virtual const Schema &schema() const = 0; + + [[nodiscard]] virtual nlohmann::json to_json() const + { + auto json = nlohmann::json{}; + json["name"] = _name; + + std::stringstream schema_stream; + for (auto i = 0u; i < schema().size(); ++i) + { + const auto &term = schema()[i]; + if (i > 0) + { + schema_stream << ","; + } + schema_stream << static_cast(term); + } + schema_stream << std::flush; + json["output"] = schema_stream.str(); + + return json; + } + + private: + std::string _name; +}; + +class UnaryNode : public NodeInterface +{ + public: + explicit UnaryNode(std::string &&name) : NodeInterface(std::move(name)) + { + } + ~UnaryNode() override = default; + + [[nodiscard]] bool is_unary() const override + { + return true; + } + + void child(std::unique_ptr &&child) + { + _child = std::move(child); + } + [[nodiscard]] const std::unique_ptr &child() const + { + return _child; + } + + [[nodiscard]] std::unique_ptr &child() + { + return _child; + } + + [[nodiscard]] nlohmann::json to_json() const override + { + auto json = NodeInterface::to_json(); + json["childs"][0] = _child->to_json(); + return json; + } + + private: + std::unique_ptr _child{nullptr}; +}; + +class BinaryNode : public NodeInterface +{ + public: + explicit BinaryNode(std::string &&name) : NodeInterface(std::move(name)) + { + } + ~BinaryNode() override = default; + + [[nodiscard]] bool is_binary() const override + { + return true; + } + + void left_child(std::unique_ptr &&child) + { + _left_child = std::move(child); + } + [[nodiscard]] const std::unique_ptr &left_child() const + { + return _left_child; + } + + [[nodiscard]] std::unique_ptr &left_child() + { + return _left_child; + } + + void right_child(std::unique_ptr &&child) + { + _right_child = std::move(child); + } + [[nodiscard]] const std::unique_ptr &right_child() const + { + return _right_child; + } + + [[nodiscard]] std::unique_ptr &right_child() + { + return _right_child; + } + + [[nodiscard]] nlohmann::json to_json() const override + { + auto json = NodeInterface::to_json(); + json["childs"][0] = _left_child->to_json(); + json["childs"][1] = _right_child->to_json(); + return json; + } + + private: + std::unique_ptr _left_child{nullptr}; + std::unique_ptr _right_child{nullptr}; +}; + +class NotSchematizedNode : public NodeInterface +{ + public: + explicit NotSchematizedNode(std::string &&name) : NodeInterface(std::move(name)) + { + } + ~NotSchematizedNode() override = default; + + const Schema &check_and_emit_schema(TableMap &) override + { + return _schema; + } + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/order_by_node.h b/include/plan/logical/node/order_by_node.h new file mode 100644 index 0000000..5af67fb --- /dev/null +++ b/include/plan/logical/node/order_by_node.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class OrderByNode final : public UnaryNode +{ + public: + explicit OrderByNode(std::vector, bool>> &&predicates) + : UnaryNode("Order by"), _predicates(std::move(predicates)) + { + } + ~OrderByNode() override = default; + + [[nodiscard]] const std::vector, bool>> &predicates() const + { + return _predicates; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &child_schema = child()->check_and_emit_schema(tables); + + for (auto &predicate : _predicates) + { + tables.check_and_replace_table(std::get<0>(predicate)); + } + + std::copy(child_schema.begin(), child_schema.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + // Pair of Operation and is_asc ordered. + std::vector, bool>> _predicates; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/projection_node.h b/include/plan/logical/node/projection_node.h new file mode 100644 index 0000000..838d1c7 --- /dev/null +++ b/include/plan/logical/node/projection_node.h @@ -0,0 +1,99 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" +#include + +namespace beedb::plan::logical +{ +class ProjectionNode final : public UnaryNode +{ + public: + explicit ProjectionNode(Schema &&schema) : UnaryNode("Projection"), _schema(std::move(schema)) + { + } + ~ProjectionNode() override = default; + + const Schema &check_and_emit_schema(TableMap &tables) override + { + const auto &child_schema = child()->check_and_emit_schema(tables); + + // TODO: Check + for (auto &term : _schema) + { + if (term.is_attribute() && term.is_generated() == false) + { + tables.check_and_replace_table(term.get()); + } + } + + // TODO: Rebuild schema or use own schema just for order? + Schema real_schema; + for (auto &term : _schema) + { + if (term.is_attribute()) + { + auto &attribute = term.get(); + const auto asterisk_has_table = attribute.table_name().has_value(); + if (attribute.is_asterisk()) + { + for (const auto &child_term : child_schema) + { + if (asterisk_has_table == false) + { + real_schema.push_back(child_term); + } + else if (child_term.is_attribute() && + child_term.get().table_name() == attribute.table_name()) + { + real_schema.emplace_back(expression::Attribute{child_term.get(), + attribute.is_print_table_name()}); + } + } + } + else + { + real_schema.emplace_back(std::move(term)); + } + } + else + { + real_schema.emplace_back(std::move(term)); + } + } + + _schema = std::move(real_schema); + + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/scan_node.h b/include/plan/logical/node/scan_node.h new file mode 100644 index 0000000..8755b6d --- /dev/null +++ b/include/plan/logical/node/scan_node.h @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" +#include "schema.h" +#include "table.h" +#include + +namespace beedb::plan::logical +{ +class TableScanNode final : public NodeInterface +{ + public: + TableScanNode(Database &database, TableReference &&table_reference) + : NodeInterface("Sequential Scan"), _database(database), _table_reference(std::move(table_reference)) + { + } + ~TableScanNode() override = default; + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + if (_database.table_exists(_table_reference.table_name()) == false) + { + throw exception::TableNotFoundException(_table_reference.table_name()); + } + + auto *table = _database.table(_table_reference.table_name()); + + tables.insert(table, _table_reference.table_alias()); + + _schema.reserve(table->schema().size()); + for (const auto &term : table->schema().terms()) + { + const auto &attribute_name = term.get().column_name(); + _schema.emplace_back( + expression::Term{expression::Attribute{_table_reference.table_alias(), attribute_name}}); + } + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + [[nodiscard]] const TableReference &table() const + { + return _table_reference; + } + + [[nodiscard]] nlohmann::json to_json() const override + { + auto json = NodeInterface::to_json(); + json["data"] = _table_reference.table_name(); + return json; + } + + private: + Database &_database; + TableReference _table_reference; + Schema _schema; +}; + +class IndexScanNode final : public NodeInterface +{ + public: + IndexScanNode(Database &database, TableReference &&table_reference, expression::Attribute &&attribute, + std::unique_ptr &&predicate) + : NodeInterface("Index Scan"), _database(database), _table_reference(std::move(table_reference)), + _attribute(std::move(attribute)), _predicate(std::move(predicate)) + { + } + ~IndexScanNode() override = default; + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + if (_database.table_exists(_table_reference.table_name()) == false) + { + throw exception::TableNotFoundException(_table_reference.table_name()); + } + + auto *table = _database.table(_table_reference.table_name()); + tables.insert(table, _table_reference.table_name()); + + tables.check_and_replace_table(_predicate); + tables.check_and_replace_table(_attribute); + + _schema.reserve(table->schema().size()); + for (const auto &term : table->schema().terms()) + { + const auto &attribute_name = term.get().column_name(); + _schema.emplace_back( + expression::Term{expression::Attribute{_table_reference.table_alias(), attribute_name}}); + } + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + [[nodiscard]] const TableReference &table() const + { + return _table_reference; + } + [[nodiscard]] const std::unique_ptr &predicate() const + { + return _predicate; + } + [[nodiscard]] const expression::Attribute &attribute() const + { + return _attribute; + } + + void schema(const Schema &schema) + { + _schema.clear(); + std::copy(schema.begin(), schema.end(), std::back_inserter(_schema)); + } + + private: + Database &_database; + TableReference _table_reference; + expression::Attribute _attribute; + std::unique_ptr _predicate; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/schema.h b/include/plan/logical/node/schema.h new file mode 100644 index 0000000..57adf8c --- /dev/null +++ b/include/plan/logical/node/schema.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include + +namespace beedb::plan::logical +{ +using Schema = std::vector; +} \ No newline at end of file diff --git a/include/plan/logical/node/selection_node.h b/include/plan/logical/node/selection_node.h new file mode 100644 index 0000000..5a01260 --- /dev/null +++ b/include/plan/logical/node/selection_node.h @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class SelectionNode final : public UnaryNode +{ + public: + explicit SelectionNode(std::unique_ptr &&predicate) + : UnaryNode("Selection"), _predicate(std::move(predicate)) + { + } + + SelectionNode(const SelectionNode &other, std::unique_ptr &&predicate) + : UnaryNode("Selection"), _predicate(std::move(predicate)) + { + std::copy(other._schema.begin(), other._schema.end(), std::back_inserter(_schema)); + } + ~SelectionNode() override = default; + + [[nodiscard]] const std::unique_ptr &predicate() const + { + return _predicate; + } + + std::unique_ptr &predicate() + { + return _predicate; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &child_schema = child()->check_and_emit_schema(tables); + + tables.check_and_replace_table(_predicate); + + std::copy(child_schema.begin(), child_schema.end(), std::back_inserter(_schema)); + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + std::unique_ptr _predicate{}; + Schema _schema; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/table.h b/include/plan/logical/node/table.h new file mode 100644 index 0000000..516b8dd --- /dev/null +++ b/include/plan/logical/node/table.h @@ -0,0 +1,137 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include + +namespace beedb::plan::logical +{ +class TableReference +{ + public: + TableReference(const std::string &name, const std::string &alias) : _table_name(name), _table_alias(alias) + { + } + TableReference(TableReference &&) noexcept = default; + TableReference(const TableReference &) = default; + ~TableReference() = default; + + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + [[nodiscard]] const std::string &table_alias() const + { + return _table_alias; + } + + bool operator==(const std::string &name) const + { + return _table_name == name || _table_alias == name; + } + + private: + std::string _table_name; + std::string _table_alias; +}; + +class TableMap +{ + public: + TableMap() = default; + ~TableMap() = default; + + void insert(table::Table *table, const std::string &table_alias) + { + _table_alias_to_name.insert(std::make_pair(table_alias, table->name())); + + for (const auto &term : table->schema().terms()) + { + const auto &column_name = term.get().column_name(); + if (_attribute_name_to_table_alias.find(column_name) == _attribute_name_to_table_alias.end()) + { + _attribute_name_to_table_alias.insert( + std::make_pair(column_name, std::vector{table_alias})); + } + else + { + _attribute_name_to_table_alias.at(column_name).push_back(table_alias); + } + } + } + + void ensure_table_exists(const std::string &name) + { + if (_table_alias_to_name.find(name) == _table_alias_to_name.end()) + { + throw exception::TableNotFoundException(name); + } + } + + void check_and_replace_table(expression::Attribute &attribute) + { + if (attribute.table_name().has_value()) + { + ensure_table_exists(attribute.table_name().value()); + } + else if (attribute.is_asterisk() == false) + { + if (_attribute_name_to_table_alias.find(attribute.column_name()) == _attribute_name_to_table_alias.end()) + { + throw exception::CanNotResolveColumnException(attribute.column_name()); + } + + const auto &tables = _attribute_name_to_table_alias.at(attribute.column_name()); + if (tables.size() != 1) + { + throw exception::CanNotResolveColumnException(attribute.column_name()); + } + + attribute.table_name(tables.front()); + } + } + + void check_and_replace_table(std::unique_ptr &operation) + { + expression::for_attribute(operation, [this](auto &attribute) { this->check_and_replace_table(attribute); }); + } + + std::vector tables() const + { + std::vector table_aliases; + table_aliases.reserve(_table_alias_to_name.size()); + for (const auto &[_, name] : _table_alias_to_name) + { + table_aliases.push_back(name); + } + + return table_aliases; + } + + private: + std::unordered_map _table_alias_to_name; + std::unordered_map> _attribute_name_to_table_alias; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/transaction_node.h b/include/plan/logical/node/transaction_node.h new file mode 100644 index 0000000..bf22e58 --- /dev/null +++ b/include/plan/logical/node/transaction_node.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class BeginTransactionNode final : public NotSchematizedNode +{ + public: + BeginTransactionNode() : NotSchematizedNode("Begin Transaction") + { + } + ~BeginTransactionNode() override = default; +}; + +class CommitTransactionNode final : public NotSchematizedNode +{ + public: + CommitTransactionNode() : NotSchematizedNode("Commit Transaction") + { + } + ~CommitTransactionNode() override = default; +}; + +class AbortTransactionNode final : public NotSchematizedNode +{ + public: + AbortTransactionNode() : NotSchematizedNode("Abort Transaction") + { + } + ~AbortTransactionNode() override = default; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/node/update_node.h b/include/plan/logical/node/update_node.h new file mode 100644 index 0000000..e11e428 --- /dev/null +++ b/include/plan/logical/node/update_node.h @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node_interface.h" + +namespace beedb::plan::logical +{ +class UpdateNode final : public UnaryNode +{ + public: + explicit UpdateNode(std::vector>> &&updates) + : UnaryNode("Update"), _updates(std::move(updates)) + { + } + ~UpdateNode() override = default; + + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + + [[nodiscard]] const std::vector>> &updates() + const + { + return _updates; + } + + const Schema &check_and_emit_schema(TableMap &tables) override + { + _schema.clear(); + + const auto &child = UnaryNode::child()->check_and_emit_schema(tables); + + for (auto &update : _updates) + { + tables.check_and_replace_table(std::get<0>(update)); + tables.check_and_replace_table(std::get<1>(update)); + } + + const auto all_tables = tables.tables(); + _table_name = all_tables.front(); + + return _schema; + } + + [[nodiscard]] const Schema &schema() const override + { + return _schema; + } + + private: + Schema _schema; + std::string _table_name; + std::vector>> _updates; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/logical/plan_view.h b/include/plan/logical/plan_view.h new file mode 100644 index 0000000..8a42591 --- /dev/null +++ b/include/plan/logical/plan_view.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "node/node_interface.h" +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::plan::logical +{ +class PlanView +{ + public: + using node_t = NodeInterface *; + using edge_t = std::tuple; + + PlanView(const std::unique_ptr &root) + { + extract_nodes(nullptr, root); + } + + ~PlanView() = default; + + void replace(node_t original_node, node_t new_node); + [[nodiscard]] bool move_between(node_t node, node_t child_node, node_t node_to_move); + void erase(node_t node); + + [[nodiscard]] bool has_child(node_t node) const + { + return _node_children.find(node) != _node_children.end() && _node_children.at(node)[0] != nullptr; + } + [[nodiscard]] const std::array &children(node_t node) const + { + return _node_children.at(node); + } + [[nodiscard]] const std::unordered_map> &nodes_and_children() const + { + return _node_children; + } + [[nodiscard]] const std::unordered_map &nodes_and_parent() const + { + return _node_parent; + } + + [[nodiscard]] node_t parent(const node_t node) const + { + if (_node_parent.find(node) == _node_parent.end()) + { + return nullptr; + } + + return _node_parent.at(node); + } + + node_t root() const; + + private: + std::unordered_map> _node_children; + std::unordered_map _node_parent; + + void extract_nodes(node_t parent, const std::unique_ptr &node); + void insert_between(node_t first, node_t second, node_t node_to_insert); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/optimizer.h b/include/plan/optimizer/optimizer.h new file mode 100644 index 0000000..f351ec4 --- /dev/null +++ b/include/plan/optimizer/optimizer.h @@ -0,0 +1,76 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace beedb::plan::logical +{ +class Optimizer +{ + public: + Optimizer() = default; + virtual ~Optimizer() = default; + + std::unique_ptr optimize(std::unique_ptr &&logical_plan); + + std::unique_ptr operator()(std::unique_ptr &&logical_plan) + { + return optimize(std::move(logical_plan)); + } + + void add(std::unique_ptr &&rule) + { + _rules.emplace_back(std::move(rule)); + } + + private: + std::vector> _rules; + + static std::unique_ptr commit(PlanView &&plan_view, std::unique_ptr &&plan); + static std::unique_ptr commit( + PlanView::node_t node, const PlanView &plan_view, + std::unordered_map> &original_nodes); + static void steal_nodes(std::unique_ptr &&node, + std::unordered_map> &node_container); +}; + +class RequiredOptimizer final : public Optimizer +{ + public: + explicit RequiredOptimizer(Database &database); + ~RequiredOptimizer() override = default; +}; + +class CompleteOptimizer final : public Optimizer +{ + public: + explicit CompleteOptimizer(Database &database); + ~CompleteOptimizer() override = default; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/cross_product_optimization_rule.h b/include/plan/optimizer/rule/cross_product_optimization_rule.h new file mode 100644 index 0000000..8f95c5d --- /dev/null +++ b/include/plan/optimizer/rule/cross_product_optimization_rule.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#include "optimizer_rule_interface.h" +#include +#include + +namespace beedb::plan::logical +{ +class CrossProductOptimizationRule final : public OptimizerRuleInterface +{ + public: + CrossProductOptimizationRule() = default; + ~CrossProductOptimizationRule() = default; + + bool optimize(PlanView &plan) override; + + private: + [[nodiscard]] static bool is_attribute_in_schema(const expression::Attribute &attribute, const Schema &schema); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/hash_join_optimization_rule.h b/include/plan/optimizer/rule/hash_join_optimization_rule.h new file mode 100644 index 0000000..db76c69 --- /dev/null +++ b/include/plan/optimizer/rule/hash_join_optimization_rule.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "optimizer_rule_interface.h" +#include +#include + +namespace beedb::plan::logical +{ +class HashJoinOptimizationRule : public OptimizerRuleInterface +{ + public: + HashJoinOptimizationRule() = default; + ~HashJoinOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; + + private: + static bool contains_only_equals(const std::unique_ptr &predicate); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/index_scan_optimization_rule.h b/include/plan/optimizer/rule/index_scan_optimization_rule.h new file mode 100644 index 0000000..60cb892 --- /dev/null +++ b/include/plan/optimizer/rule/index_scan_optimization_rule.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "optimizer_rule_interface.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::plan::logical +{ +class IndexScanOptimizationRule : public OptimizerRuleInterface +{ + public: + explicit IndexScanOptimizationRule(Database &database) : _database(database) + { + } + ~IndexScanOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; + + private: + Database &_database; + std::pair, std::unique_ptr> find_index_predicate( + const std::string &table_name, const std::unique_ptr &predicate); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/merge_selection_optimization_rule.h b/include/plan/optimizer/rule/merge_selection_optimization_rule.h new file mode 100644 index 0000000..77bd1cf --- /dev/null +++ b/include/plan/optimizer/rule/merge_selection_optimization_rule.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "optimizer_rule_interface.h" + +namespace beedb::plan::logical +{ +class MergeSelectionOptimizationRule final : public OptimizerRuleInterface +{ + public: + MergeSelectionOptimizationRule() = default; + ~MergeSelectionOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/optimizer_rule_interface.h b/include/plan/optimizer/rule/optimizer_rule_interface.h new file mode 100644 index 0000000..32e27f1 --- /dev/null +++ b/include/plan/optimizer/rule/optimizer_rule_interface.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include + +namespace beedb::plan::logical +{ +class OptimizerRuleInterface +{ + public: + OptimizerRuleInterface() = default; + virtual ~OptimizerRuleInterface() = default; + + virtual bool optimize(PlanView &plan) = 0; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/predicate_push_down_optimization_rule.h b/include/plan/optimizer/rule/predicate_push_down_optimization_rule.h new file mode 100644 index 0000000..455e0be --- /dev/null +++ b/include/plan/optimizer/rule/predicate_push_down_optimization_rule.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "optimizer_rule_interface.h" +#include +#include +#include +#include + +namespace beedb::plan::logical +{ +class PredicatePushDownOptimizationRule final : public OptimizerRuleInterface +{ + public: + PredicatePushDownOptimizationRule() = default; + ~PredicatePushDownOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; + + private: + /** + * Checks if the schema of a given node contains all + * attributes needed by the given predicate. + * + * @param node Node to check. + * @param predicate Predicate with attributes that are required. + * @return True, if the schema of the node contains all needed attributes. False otherwise. + */ + [[nodiscard]] static bool provides_needed_attributes(PlanView::node_t node, + const std::unique_ptr &predicate); + + /** + * Determines the lowest node in the plan that could + * work as the child of a given selection. To do so, + * the node that is returned by this function has to + * provide all attributes needed by the predicate. + * The further down the logical plan, the better. + * + * @param plan Logical plan. + * @param current_node Node to start the search. + * @param predicate Predicate to push down. + * @return The node that could work as "the lowest" child. + */ + [[nodiscard]] static PlanView::node_t determine_lowest_position( + const PlanView &plan, PlanView::node_t current_node, const std::unique_ptr &predicate); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/remove_projection_optimization_rule.h b/include/plan/optimizer/rule/remove_projection_optimization_rule.h new file mode 100644 index 0000000..a6ba6ff --- /dev/null +++ b/include/plan/optimizer/rule/remove_projection_optimization_rule.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "optimizer_rule_interface.h" + +namespace beedb::plan::logical +{ +class RemoveProjectionOptimizationRule final : public OptimizerRuleInterface +{ + public: + RemoveProjectionOptimizationRule() = default; + ~RemoveProjectionOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/optimizer/rule/swap_operands_optimization_rule.h b/include/plan/optimizer/rule/swap_operands_optimization_rule.h new file mode 100644 index 0000000..121ddb1 --- /dev/null +++ b/include/plan/optimizer/rule/swap_operands_optimization_rule.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "optimizer_rule_interface.h" +#include +#include + +namespace beedb::plan::logical +{ +class SwapOperandsOptimizationRule : public OptimizerRuleInterface +{ + public: + SwapOperandsOptimizationRule() = default; + ~SwapOperandsOptimizationRule() override = default; + + bool optimize(PlanView &plan) override; + + private: + [[nodiscard]] static bool can_swap(const std::unique_ptr &predicate); + static void swap_if_needed(std::unique_ptr &predicate); + [[nodiscard]] static bool should_swap(const std::unique_ptr &left, + const std::unique_ptr &right); + [[nodiscard]] static expression::Operation::Type swap(expression::Operation::Type type); +}; +} // namespace beedb::plan::logical \ No newline at end of file diff --git a/include/plan/physical/builder.h b/include/plan/physical/builder.h new file mode 100644 index 0000000..bf1c032 --- /dev/null +++ b/include/plan/physical/builder.h @@ -0,0 +1,161 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "plan.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::plan::physical +{ +/** + * The Builder builds the physical operator plan based on the logical plan. + */ +class Builder +{ + public: + /** + * Creates a plan containing physical operators + * based on the logical plan. + * + * @param database Database for the execution. + * @param transaction Transaction for the execution. + * @param logical_plan Logical plan as base for physical operators. + * @return Plan with physical operators. + */ + static Plan build(Database &database, concurrency::Transaction *transaction, + concurrency::TransactionCallback &transaction_callback, bool add_to_scan_set, + const std::unique_ptr &logical_plan); + + /** + * Creates a plan for filling index data structures with data. + * + * @param database Database for execution. + * @param transaction Transaction for the execution. + * @param table_name Name of the indexed table. + * @param column_name Name of the indexed column. + * @param index_name Name of the index. + * @return Plan for filling the index. + */ + static Plan build_index_plan(Database &database, concurrency::Transaction *transaction, + const std::string &table_name, const std::string &column_name, + const std::string &index_name); + + private: + /** + * Builds a physical execution operator based on a logical node. + * + * @param database Database for execution. + * @param transaction Transaction for execution. + * @param logical_plan Full logical plan. + * @param logical_node_name Name of the logical node. + * @return Pointer to the built physical operator. + */ + static std::unique_ptr build_operator( + Database &database, concurrency::Transaction *transaction, + concurrency::TransactionCallback &transaction_callback, bool add_to_scan_set, + concurrency::ScanSetItem *scan_set, const std::unique_ptr &logical_node); + + /** + * Turns a logical predicate into a physical predicate matcher. + * + * @param predicate Logical predicate. + * @param schema Schema for the table, the predicate will be evaluated on. + * @return Pointer to the predicate matcher. + */ + static std::unique_ptr build_predicate( + const std::unique_ptr &, const table::Schema &schema); + + /** + * Turns a logical join predicate into a physical predicate matcher. + * + * @param predicate Logical predicate. + * @param left_schema Schema of the left join table. + * @param right_schema Schema of the right join table. + * @return Pointer to the predicate matcher. + */ + static std::unique_ptr build_predicate( + const std::unique_ptr &predicate, const table::Schema &left_schema, + const table::Schema &right_schema); + + /** + * Builds a physical value from a logical operand for the given type. + * + * @param operand Logical operand. + * @param type Type for the new value. + * @return Physical value. + */ + static table::Value build_value(const expression::Term &term, table::Type::Id type); + + /** + * Builds a physical value from a logical operand for the given type. + * + * @param operand Logical operand. + * @param type Type for the new value. + * @return Physical value. + */ + static table::Value build_value(table::Value &&value, table::Type::Id type); + + static table::Value build_value(const expression::Term &term); + + /** + * Builds a physical tuple based on the required schema and logical operands. + * + * @param schema Schema of the target table. + * @param attributes Logical schema. + * @param values Logical operands that represent the values. + * @return Physical tuple. + */ + static table::Tuple build_tuple(const table::Schema &schema, + const std::vector &column_indices, + const std::vector &default_column_indices, + std::vector &values); + + /** + * Extracts key ranges for index scans from a logical predicate. + * + * @param predicate Logical predicate. + * @return Set of key ranges the index scan has to lookup. + */ + static std::unordered_set extract_key_ranges( + const std::unique_ptr &predicate); + + /** + * Extracts a key from a logical atom. + * @param atom Logical atom. + * @return Key from the atom or std::nullopt, when no key was represented by the atom. + */ + static std::optional extract_key(const expression::Term &term); + + static std::pair> + build_arithmetic_calculator(const std::unique_ptr &expression, + const table::Schema &child_schema); +}; +} // namespace beedb::plan::physical diff --git a/include/plan/physical/plan.h b/include/plan/physical/plan.h new file mode 100644 index 0000000..5cdaa81 --- /dev/null +++ b/include/plan/physical/plan.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include
+#include
+#include + +namespace beedb::plan::physical +{ +/** + * The plan grants access to the physical operator chain. + */ +class Plan +{ + public: + Plan(Database &database, std::unique_ptr &&root) + : _database(database), _root(std::move(root)) + { + } + + ~Plan() = default; + + /** + * Executes the physical operators. + * + * @param schema_callback Will be called once, when the output schema is not empty. + * @param row_callback Will be called for every output tuple. + */ + std::uint64_t execute(io::ExecutionCallback &execution_callback); + + protected: + Database &_database; + std::unique_ptr _root; +}; +} // namespace beedb::plan::physical diff --git a/include/statistic/system_statistics.h b/include/statistic/system_statistics.h new file mode 100644 index 0000000..dcef263 --- /dev/null +++ b/include/statistic/system_statistics.h @@ -0,0 +1,99 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include
+#include + +namespace beedb::statistic +{ + +/** + * Container for managing statistics regarding tables. + */ +class TableStatistic +{ + public: + TableStatistic() = default; + ~TableStatistic() = default; + + void cardinality(table::Table &table, const std::uint64_t cardinality) + { + this->cardinality(table.id(), cardinality); + } + + void cardinality(const std::int32_t table_id, const std::uint64_t cardinality) + { + _cardinality[table_id] = cardinality; + } + + [[nodiscard]] std::uint64_t cardinality(table::Table &table) const + { + if (_cardinality.find(table.id()) == _cardinality.end()) + { + return 0u; + } + + return _cardinality.at(table.id()); + } + + void add_cardinality(table::Table &table, const std::uint64_t cardinality = 1u) + { + if (_cardinality.find(table.id()) != _cardinality.end()) + { + _cardinality[table.id()] += cardinality; + } + else + { + _cardinality[table.id()] = cardinality; + } + } + + [[maybe_unused]] void sub_cardinality(table::Table &table, const std::uint64_t cardinality = 1u) + { + if (_cardinality.find(table.id()) != _cardinality.end()) + { + _cardinality[table.id()] -= cardinality; + } + } + + private: + std::unordered_map _cardinality; +}; + +class SystemStatistics +{ + public: + SystemStatistics() = default; + ~SystemStatistics() = default; + + [[nodiscard]] TableStatistic &table_statistics() + { + return _table_statistics; + } + + private: + TableStatistic _table_statistics; +}; + +} // namespace beedb::statistic diff --git a/include/storage/manager.h b/include/storage/manager.h new file mode 100644 index 0000000..02435a0 --- /dev/null +++ b/include/storage/manager.h @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "page.h" +#include +#include +#include +#include +#include + +namespace beedb::storage +{ +/** + * The StorageManager grants access to the data written to the disk. + */ +class Manager +{ + public: + explicit Manager(const std::string &file_name); + ~Manager(); + + /** + * Copy a page from disk to memory. + * + * @param page_id Id of the page. + * @param buffer Location where to store the data in memory. + */ + void read(Page::id_t page_id, std::byte *buffer); + + /** + * Write the page from memory to disk. + * + * @param page_id Page to be written. + * @param data Location in memory of the data to write to disk. + */ + void write(Page::id_t page_id, const std::byte *data); + + /** + * Allocates a new page in the disk file and extends the + * file by the new allocated page. + * + * @return Id of the new allocated page. + */ + template Page::id_t allocate() + { + const auto page_id = this->_count_pages.fetch_add(1); + P page; + + this->write(page_id, page.data()); + return page_id; + } + + /** + * @return Number of pages stored in the disk file. + */ + [[nodiscard]] std::size_t count_pages() const + { + return _count_pages; + } + + private: + std::atomic_size_t _count_pages = 0u; + std::fstream _storage_file; +}; +} // namespace beedb::storage \ No newline at end of file diff --git a/include/storage/metadata_page.h b/include/storage/metadata_page.h new file mode 100644 index 0000000..0bdf53b --- /dev/null +++ b/include/storage/metadata_page.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "page.h" +#include + +namespace beedb::storage +{ +class MetadataPage final : public Page +{ + public: + MetadataPage() = default; + + ~MetadataPage() override = default; + + [[nodiscard]] concurrency::timestamp::timestamp_t next_transaction_timestamp() const + { + return *reinterpret_cast(Page::data() + sizeof(Page::id_t)); + } + + void next_transaction_timestamp(concurrency::timestamp::timestamp_t timestamp) + { + *reinterpret_cast(Page::data() + sizeof(Page::id_t)) = timestamp; + } +}; +} // namespace beedb::storage diff --git a/include/storage/page.h b/include/storage/page.h new file mode 100644 index 0000000..2fff722 --- /dev/null +++ b/include/storage/page.h @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::storage +{ +/** + * Represents a page on disk, holding a header for meta information + * and raw memory for storing data. + * Pages can be linked logically. All linked pages contain data for the + * same table; like a linked list of storage. + */ +class Page +{ + public: + using id_t = std::uint32_t; + using offset_t = std::uint16_t; + static constexpr id_t INVALID_PAGE_ID = std::numeric_limits::max(); + static constexpr id_t MEMORY_TABLE_PAGE_ID = std::numeric_limits::max() - 1; + + public: + Page() + { + _data.fill(std::byte{'\0'}); + this->next_page_id(INVALID_PAGE_ID); + } + + virtual ~Page() = default; + + /** + * @return Id of this page. + */ + [[nodiscard]] id_t id() const + { + return _id; + } + + void id(id_t id) + { + _id = id; + } + + // [[nodiscard]] std::shared_mutex& latch() + // { + // return _rw_latch; + // } + + [[nodiscard]] std::uint64_t pin_count() const + { + return _pin_count; + } + + void pin_count(std::uint64_t pin_count) + { + _pin_count = pin_count; + } + + [[nodiscard]] bool is_pinned() const + { + return _pin_count > 0u; + } + + [[nodiscard]] bool is_dirty() const + { + return _is_dirty; + } + + void is_dirty(bool is_dirty) + { + _is_dirty = is_dirty; + } + + /** + * @return Id of the page which is logical connected to this page. + */ + [[nodiscard]] id_t next_page_id() const + { + return *reinterpret_cast(_data.data()); + } + + /** + * Updates the next page id. + * @param next_page_id Id of the next page. + */ + void next_page_id(const id_t next_page_id) + { + *reinterpret_cast(_data.data()) = next_page_id; + } + + /** + * @return True, when this page has a next page. + */ + [[nodiscard]] bool has_next_page() const + { + return next_page_id() != INVALID_PAGE_ID; + } + + /** + * @return Pointer to the data stored on this page. + */ + [[nodiscard]] std::byte *data() + { + return _data.data(); + } + + [[nodiscard]] const std::byte *data() const + { + return _data.data(); + } + + /** + * @return True, when this is a persisted page. + */ + explicit operator bool() const + { + return _id < INVALID_PAGE_ID; + } + + /** + * @param index Index of data. + * @return Pointer to the data. + */ + std::byte *operator[](const offset_t index) + { + return &_data[index]; + } + + private: + // Metadata + storage::Page::id_t _id = storage::Page::INVALID_PAGE_ID; + // std::shared_mutex _rw_latch; + std::uint64_t _pin_count = 0u; + bool _is_dirty = false; + + // Page data + std::array _data{std::byte{'\0'}}; +}; +} // namespace beedb::storage \ No newline at end of file diff --git a/include/storage/record_identifier.h b/include/storage/record_identifier.h new file mode 100644 index 0000000..5b54f36 --- /dev/null +++ b/include/storage/record_identifier.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "page.h" + +namespace beedb::storage +{ +class RecordIdentifier +{ + public: + static_assert(sizeof(Page::id_t) + sizeof(std::uint16_t) <= sizeof(std::uint64_t)); + RecordIdentifier() = default; + RecordIdentifier(const RecordIdentifier &) = default; + RecordIdentifier(Page::id_t page_id, std::uint16_t slot) + { + this->page_id(page_id); + this->slot(slot); + } + ~RecordIdentifier() = default; + + [[nodiscard]] Page::id_t page_id() const + { + return _identifier >> (sizeof(std::uint16_t) * 8); + } + + [[nodiscard]] std::uint16_t slot() const + { + return _identifier & std::numeric_limits::max(); + } + + void page_id(Page::id_t page_id) + { + _identifier = (std::uint64_t(page_id) << (sizeof(Page::offset_t) * 8) | std::uint64_t(this->slot())); + } + + void slot(const Page::offset_t offset) + { + _identifier = (std::uint64_t(this->page_id()) << (sizeof(std::uint16_t) * 8)) | offset; + } + + explicit operator bool() const + { + return _identifier < std::numeric_limits::max(); + } + explicit operator std::uint64_t() const + { + return _identifier; + } + + bool operator==(const RecordIdentifier other) const + { + return _identifier == other._identifier; + } + + private: + std::uint64_t _identifier = std::numeric_limits::max(); +}; +} // namespace beedb::storage + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::storage::RecordIdentifier &rid) const + { + return std::hash()(static_cast(rid)); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/storage/record_page.h b/include/storage/record_page.h new file mode 100644 index 0000000..7812e2a --- /dev/null +++ b/include/storage/record_page.h @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "page.h" +#include + +namespace beedb::storage +{ +/** + * Page: + * Next Page Id (16bit) | Number of Slots (16bit) | Free Pointer (16bit) | Slot_0 | Slot_1 | Slot_2 | ... free space ... + * | Record_2 | Record_1 | Record _0 + * |-------------------------------------------------------------^ + */ +class RecordPage final : public Page +{ + private: + class Slot + { + public: + Slot(Page::offset_t start, Page::offset_t size) : _start(start), _size(size << 1) + { + } + ~Slot() = default; + + [[nodiscard]] Page::offset_t start() const + { + return _start; + } + [[nodiscard]] Page::offset_t size() const + { + return _size >> 1; + } + [[nodiscard]] bool is_free() const + { + return (_size & 1u) == 1u; + } + void is_free(bool is_free) + { + _size = ((_size >> 1u) << 1u) | is_free; + } + + private: + Page::offset_t _start = 0; + Page::offset_t _size = 0; + }; + + public: + RecordPage() + { + *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t)) = + Config::page_size; + } + + ~RecordPage() override = default; + + [[nodiscard]] std::uint16_t slots() const + { + return *reinterpret_cast(Page::data() + sizeof(Page::id_t)); + } + void slots(std::uint16_t slots) + { + *reinterpret_cast(Page::data() + sizeof(Page::id_t)) = slots; + } + + [[nodiscard]] const Slot &slot(std::uint16_t index) const + { + return *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t) + + sizeof(std::uint16_t) + (index * sizeof(Slot))); + } + [[nodiscard]] Slot &slot(std::uint16_t index) + { + return *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t) + + sizeof(std::uint16_t) + (index * sizeof(Slot))); + } + + [[nodiscard]] std::byte *record(std::uint16_t index) + { + const auto &slot = this->slot(index); + return Page::data() + slot.start(); + } + + [[nodiscard]] bool is_free(std::uint16_t index) const + { + return this->slot(index).is_free(); + } + + void erase(std::uint16_t index) + { + this->slot(index).is_free(true); + } + + [[nodiscard]] std::uint16_t free_space() const + { + const auto free_space_pointer = + *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t)); + return free_space_pointer - (this->slots() * sizeof(Slot)) - sizeof(std::uint16_t) - sizeof(std::uint16_t) - + sizeof(Page::id_t); + } + + std::uint16_t allocate_slot(std::uint16_t size) + { + const auto slot_size = size + sizeof(concurrency::Metadata); + + const auto slots = this->slots(); + const auto slot_id = slots; + this->slots(slots + 1); + + const auto free_space_pointer_before = + *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t)); + *reinterpret_cast(Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t)) = + free_space_pointer_before - slot_size; + + new (Page::data() + sizeof(Page::id_t) + sizeof(std::uint16_t) + sizeof(std::uint16_t) + (slots * sizeof(Slot))) + Slot(free_space_pointer_before - slot_size, slot_size); + + return slot_id; + } + + void write(const std::uint16_t slot_id, const concurrency::Metadata *concurrency_metadata, const std::byte *payload, + const std::uint16_t size) + { + auto *record = this->record(slot_id); + std::memcpy(record, concurrency_metadata, sizeof(concurrency::Metadata)); + std::memcpy(record + sizeof(concurrency::Metadata), payload, size); + } +}; +} // namespace beedb::storage diff --git a/include/table/column.h b/include/table/column.h new file mode 100644 index 0000000..9ffb348 --- /dev/null +++ b/include/table/column.h @@ -0,0 +1,214 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "type.h" +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Represents one column in a table schema. + */ +class Column +{ + friend std::ostream &operator<<(std::ostream &stream, const Column &column); + + public: + Column() = default; + + Column(const std::int32_t id, const Type type, const bool is_nullable = false, + std::vector> &&indices = {}) + : _id(id), _type(type), _is_nullable(is_nullable), _indices(indices) + { + } + + explicit Column(const Type type, const bool is_nullable = false, + std::vector> &&indices = {}) + : _type(type), _is_nullable(is_nullable), _indices(indices) + { + } + + Column(const Column &other) = default; + + Column(Column &&) = default; + + Column &operator=(Column &&) = default; + + ~Column() = default; + + /** + * @return Id of the column or -1 if the column was not persisted to the metadata table. + */ + [[nodiscard]] std::int32_t id() const + { + return _id; + } + + /** + * @return Type of the column. + */ + [[nodiscard]] const Type &type() const + { + return _type; + } + + /** + * @return True, if data can be null for the column. + */ + [[nodiscard]] bool is_nullable() const + { + return _is_nullable; + } + + /** + * @return True, when at least one index exists for this column. + */ + [[nodiscard]] bool is_indexed() const + { + return _indices.empty() == false; + } + + /** + * Allows to ask for an index with specific requirements. + * + * @param require_range_index Specifies if we need an index supporting range queries. + * @return True, when an index with given requirements exists. + */ + [[nodiscard]] bool is_indexed(const bool require_range_index) const + { + if (require_range_index == false) + { + return is_indexed(); + } + + for (auto &index : _indices) + { + if (index->supports_range()) + { + return true; + } + } + + return false; + } + + /** + * Adds an index to the column. + * + * @param index Index to add. + */ + inline void add_index(std::shared_ptr index) + { + _indices.push_back(std::move(index)); + } + + /** + * @return All indices for this column. + */ + [[nodiscard]] const std::vector> &indices() const + { + return _indices; + } + + /** + * Checks whether an index with a specific name exists. + * + * @param name Name of the index. + * @return True, when the index exists. + */ + [[nodiscard]] bool has_index(const std::string &name) const + { + return index(name) != nullptr; + } + + /** + * Lookup for a specific index. + * + * @param name Name of the index. + * @return Pointer to the specific index. + */ + [[nodiscard]] std::shared_ptr index(const std::string &name) const + { + for (auto &index : _indices) + { + if (index->name() == name) + { + return index; + } + } + + return {}; + } + + /** + * Lookup for a specific index. + * + * @param need_range Specifies whether the wanted index has to provied range queries. + * @return An index that supports the requirements. + */ + [[nodiscard]] std::shared_ptr index(const bool need_range) const + { + if (_indices.empty() == false && need_range == false) + { + return _indices[0]; + } + + for (auto &index : _indices) + { + if (index->supports_range()) + { + return index; + } + } + + return {}; + } + + bool operator==(const Column &other) const + { + return _id == other.id(); + } + bool operator!=(const Column &other) const + { + return _id != other.id(); + } + bool operator==(const Type type) const + { + return _type == type; + } + bool operator!=(const Type type) const + { + return _type != type; + } + + private: + std::int32_t _id{-1}; + Type _type{}; + bool _is_nullable{false}; + std::vector> _indices; +}; +} // namespace beedb::table \ No newline at end of file diff --git a/include/table/date.h b/include/table/date.h new file mode 100644 index 0000000..45babd3 --- /dev/null +++ b/include/table/date.h @@ -0,0 +1,138 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Implements date type. + */ +class Date +{ + public: + Date() noexcept = default; + Date(const std::uint16_t year, const std::uint8_t month, const std::uint8_t day) noexcept + : _year(year), _month(month), _day(day) + { + } + Date(const Date &) = default; + + ~Date() noexcept = default; + + [[nodiscard]] std::uint16_t year() const noexcept + { + return _year; + } + [[nodiscard]] std::uint8_t month() const noexcept + { + return _month; + } + [[nodiscard]] std::uint8_t day() const noexcept + { + return _day; + } + + Date &operator=(const Date &other) = default; + + bool operator==(const Date other) const noexcept + { + return _year == other._year && _month == other._month && _day == other._day; + } + + bool operator!=(const Date other) const noexcept + { + return _year != other._year || _month != other._month || _day != other._day; + } + + bool operator<(const Date other) const noexcept + { + return _year < other._year || (_year == other._year && _month < other._month) || + (_year == other._year && _month == other._month && _day < other._day); + } + + bool operator<=(const Date other) const noexcept + { + return _year < other._year || (_year == other._year && _month < other._month) || + (_year == other._year && _month == other._month && _day <= other._day); + } + + bool operator>(const Date other) const noexcept + { + return _year > other._year || (_year == other._year && _month > other._month) || + (_year == other._year && _month == other._month && _day > other._day); + } + + bool operator>=(const Date other) const noexcept + { + return _year > other._year || (_year == other._year && _month > other._month) || + (_year == other._year && _month == other._month && _day >= other._day); + } + + bool operator==(std::nullptr_t) const noexcept + { + return _year == 0 && _month == 0 && _day == 0; + } + + [[nodiscard]] std::string to_string() const + { + return std::to_string(static_cast(_year)) + "-" + + std::to_string(static_cast(_month)) + "-" + + std::to_string(static_cast(_day)); + } + + static Date from_string(const std::string &date_string) + { + std::smatch match; + if (std::regex_match(date_string, match, _date_regex)) + { + return Date{static_cast(std::stoul(match[1].str())), + static_cast(std::stoul(match[2].str())), + static_cast(std::stoul(match[3].str()))}; + } + + return Date{}; + } + + private: + inline static std::regex _date_regex{"(\\d{4})-(\\d{2})-(\\d{2})"}; + std::uint16_t _year = 0; + std::uint8_t _month = 0; + std::uint8_t _day = 0; +}; +} // namespace beedb::table + +namespace std +{ +template <> struct hash +{ + std::size_t operator()(const beedb::table::Date &date) + { + const std::uint32_t h = ((((std::uint32_t{0} | date.year()) << 8) | date.month()) << 8) || date.day(); + return hash()(h); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/table/memory_table.h b/include/table/memory_table.h new file mode 100644 index 0000000..c4f32bf --- /dev/null +++ b/include/table/memory_table.h @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "schema.h" +#include "tuple.h" +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Stores data in memory. + * Some times, the data can not stay in the buffer from the BufferManager + * and has to be copied to "safe" memory that will not be replaced. + */ +class MemoryTable +{ + public: + explicit MemoryTable(const Schema &schema) + : _schema(schema), _row_size(schema.row_size() + sizeof(concurrency::Metadata)) + { + allocate(512u); + } + + MemoryTable(MemoryTable &&other) noexcept + : _schema(std::move(other._schema)), _row_size(other._row_size), _capacity(other._capacity), _data(other._data) + { + other._data = nullptr; + + _tuples.reserve(other._tuples.size()); + for (auto &&tuple : other._tuples) + { + _tuples.emplace_back(Tuple{_schema, std::move(tuple)}); + } + } + + MemoryTable(const MemoryTable &) = delete; + + ~MemoryTable() + { + std::free(_data); + } + + /** + * Copy data from disk (or page) to the memory. + * + * @param tuple Tuple to be copied to memory. + * @return Index within the memory table. + */ + std::size_t add(const Tuple &tuple) + { + // Reallocate if needed. + if (_tuples.size() == _capacity) + { + allocate(_capacity << 1); + } + + // At this location, we will copy the tuples data. + auto *data = _data + _tuples.size() * _row_size; + std::memcpy(data, tuple.metadata(), sizeof(concurrency::Metadata)); + std::memcpy(data + sizeof(concurrency::Metadata), tuple.data(), _schema.row_size()); + + _tuples.emplace_back(Tuple{_schema, + {storage::Page::MEMORY_TABLE_PAGE_ID, 0u}, + reinterpret_cast(data), + data + sizeof(concurrency::Metadata)}); + return _tuples.size() - 1; + } + + /** + * @return Constant set of all tuples stored in the memory table. + */ + [[nodiscard]] const std::vector &tuples() const + { + return _tuples; + } + + /** + * @return Set of all tuples stored in the memory table. + */ + std::vector &tuples() + { + return _tuples; + } + + /** + * @return Schema of this table. + */ + [[nodiscard]] const Schema &schema() const + { + return _schema; + } + + /** + * @return True, when the table has no tuples stored. + */ + [[nodiscard]] bool empty() const + { + return _tuples.empty(); + } + + /** + * @return Number of stored tuples. + */ + [[nodiscard]] std::size_t size() const + { + return _tuples.size(); + } + + /** + * Grants access to a specific tuple. + * @param index Index of the tuple. + * @return Tuple. + */ + [[nodiscard]] const Tuple &at(const std::size_t index) const + { + return _tuples[index]; + } + + const Tuple &operator[](const std::size_t index) const + { + return _tuples[index]; + } + + [[nodiscard]] auto begin() const + { + return _tuples.begin(); + } + [[nodiscard]] auto end() const + { + return _tuples.end(); + } + + private: + Schema _schema; + const std::uint32_t _row_size; + std::uint64_t _capacity = 0u; + std::byte *_data = nullptr; + std::vector _tuples; + + void allocate(const std::uint64_t capacity) + { + // Resize capacity. + const auto old_capacity = _capacity; + _capacity = capacity; + + // Resize "real" data. + auto *old_data = _data; + _data = static_cast(std::aligned_alloc(64u, capacity * _row_size)); + + // Copy old data to new location. + if (old_data != nullptr) + { + std::memmove(_data, old_data, old_capacity * _row_size); + std::free(old_data); + } + + // Tell tuples their new location. + for (auto i = 0u; i < _tuples.size(); ++i) + { + _tuples[i].metadata(reinterpret_cast(_data + i * _row_size)); + _tuples[i].data(_data + i * _row_size + sizeof(concurrency::Metadata)); + } + } +}; +} // namespace beedb::table diff --git a/include/table/schema.h b/include/table/schema.h new file mode 100644 index 0000000..52556c2 --- /dev/null +++ b/include/table/schema.h @@ -0,0 +1,339 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "column.h" +#include +#include +#include +#include + +namespace beedb::table +{ + +class Schema +{ + public: + using ColumnIndexType = std::size_t; + + Schema() = default; + Schema(const Schema &other) = default; + Schema(Schema &&other) = default; + + /** + * Schema creates an empty schema + * + * @param table_name Name of the table. + */ + explicit Schema(const std::string &table_name) : _table_name(table_name) + { + } + + /** + * Schema creates an empty schema + * + * @param table_name Name of the table. + */ + explicit Schema(std::string &&table_name) : _table_name(std::move(table_name)) + { + } + + Schema(std::string &&table_name, Schema &&other) noexcept + : _table_name(std::move(table_name)), _columns(std::move(other._columns)), _terms(std::move(other._terms)), + _offset(std::move(other._offset)), _column_order(std::move(other._column_order)), _row_size(other._row_size) + { + } + + /** + * Combines to schemas to a new one. + * + * @param first First schema. + * @param second Second schema. + * @param table_name New name for the new schema (will be moved). + */ + Schema(const Schema &first, const Schema &second, std::string &&table_name) : _table_name(std::move(table_name)) + { + const auto size = first.size() + second.size(); + _columns.reserve(size); + _terms.reserve(size); + _column_order.reserve(size); + _offset.reserve(size); + + _offset.insert(_offset.begin(), first._offset.begin(), first._offset.end()); + for (const auto offset : second._offset) + { + _offset.push_back(first._row_size + offset); + } + + for (auto i = 0u; i < first.size(); ++i) + { + _columns.push_back(first._columns[i]); + _terms.push_back(first._terms[i]); + _column_order.push_back(i); + } + + for (auto i = 0u; i < second.size(); ++i) + { + _columns.push_back(second._columns[i]); + _terms.push_back(second._terms[i]); + _column_order.push_back(_columns.size() - 1); + } + + _row_size = first._row_size + second._row_size; + } + + Schema(const Schema &other, const std::vector &terms, const std::string &new_table_name) + : _table_name(new_table_name), _columns(other._columns), _terms(terms), _offset(other._offset), + _column_order(other._column_order), _row_size(other._row_size) + { + } + + Schema(const Schema &other, const std::vector &terms) + : _table_name(other._table_name), _columns(other._columns), _terms(terms), _offset(other._offset), + _row_size(other._row_size) + { + // build _column_order based on "attributes", since it's order does not necessarily coincide with the physical + // column order + for (const auto &term : _terms) + { + + // TODO: At this point, "term" may have the right column name but another table name (since other + // comes from the physical table and "term" from the logical layer (i.e. "movie.id" vs "m.id"). + if (term.is_attribute()) + { + const auto old_index = other.column_index( + term.get().column_name()); // the "other" schema has the correct mapping! + if (old_index.has_value()) + { + _column_order.push_back(old_index.value()); + } + } + } + } + + /** + * Creates a schema, used for deserialization. + * + * @param columns Columns of the schema. + * @param attributes Visible attributes. + * @param offsets Offsets for columns. + * @param column_orders Orders of attributes. + * @param row_size Size of the row in bytes. + */ + Schema(std::vector &&columns, std::vector &&terms, std::vector &&offsets, + std::vector &&column_orders, const std::uint16_t row_size) + : _table_name(""), _columns(std::move(columns)), _terms(std::move(terms)), _offset(std::move(offsets)), + _column_order(std::move(column_orders)), _row_size(row_size) + { + } + + ~Schema() = default; + + /** + * Adds a new column and its attribute to the schema. + * + * @param column Column to be added. + * @param attribute Logical attribute for the column. + * @param visible True, when the column is visible for output. + */ + void add(Column &&column, expression::Term &&term) + { + _terms.emplace_back(std::move(term)); + _columns.push_back(column); + _column_order.push_back(_columns.size() - 1); + if (_offset.empty()) + { + _offset.push_back(0u); + } + else + { + const auto last_index = _offset.size() - 1; + _offset.push_back(_offset[last_index] + _columns[last_index].type().size()); + } + _row_size += column.type().size(); + } + + Schema &operator=(const Schema &) = default; + Schema &operator=(Schema &&) = default; + + /** + * @return Number of columns in the schema. + */ + [[nodiscard]] std::size_t size() const + { + return _columns.size(); + } + + /** + * Calculates the byte-offset for a specific column. + * + * @param column_index Index of the column. + * @return Offset in number of bytes for the raw data access. + */ + [[nodiscard]] std::uint16_t offset(const std::size_t column_index) const + { + return _offset[column_index]; + } + + /** + * @return Number of bytes of the raw data. + */ + [[nodiscard]] std::uint16_t row_size() const + { + return _row_size; + } + + [[nodiscard]] std::optional column_index(const expression::Term &term) const + { + for (auto i = 0u; i < _terms.size(); i++) + { + if (_terms[i] == term) + { // uses combined_name/expression::Attribute equality semantics + return _column_order[i]; + } + } + + return std::nullopt; + } + + /** + * Calculates the index of a column in the schema. Search is solely based on the attributes NAME, + * neither on its table name or alias!! + * + * @param attribute_name Name of the column. + * @return Index in the schema. + */ + [[nodiscard]] std::optional column_index(const std::string &attribute_name) const + { + // this version is solely based on the attribute name + for (auto i = 0u; i < _terms.size(); i++) + { + const auto &term = _terms[i]; + if (term.alias().has_value() && term.alias() == attribute_name) + { + return _column_order[i]; + } + else if (term.is_attribute()) + { + if (term.get().column_name() == attribute_name) + { + return _column_order[i]; + } + } + } + return std::nullopt; + } + + /** + * Checks whether the schema holds a specific attribute. + * + * @param attribute Logical attribute. + * @return True, when the schema contains the attribute. + */ + [[nodiscard]] bool contains(const expression::Term &term) const + { + return column_index(term).has_value(); + } + + /** + * Checks whether the schema holds a specific column. + * + * @param column_name Name of the column. + * @return True, when the schema contains a column with the give name. + */ + [[nodiscard]] bool contains(const std::string &column_name) const + { + return column_index(column_name).has_value(); + } + + /** + * @return Name of the table represented by this schema. + */ + [[nodiscard]] const std::string &table_name() const + { + return _table_name; + } + + /** + * @return Constant set of all columns. + */ + [[nodiscard]] const std::vector &columns() const + { + return _columns; + } + + /** + * Access to a specific column. + * + * @param index Index of the column. + * @return Constant access to the column. + */ + [[nodiscard]] const Column &column(const std::size_t index) const + { + return _columns[index]; + } + + [[nodiscard]] Column &column(const std::size_t index) + { + return _columns[index]; + } + + [[nodiscard]] const std::vector &terms() const + { + return _terms; + } + + /** + * @return True, when no column was added to the schema. + */ + [[nodiscard]] bool empty() const + { + return _columns.empty(); + } + + /** + * @return Order of the column indices. + */ + [[nodiscard]] const std::vector &column_order() const + { + return _column_order; + } + + Column &operator[](const ColumnIndexType index) + { + return _columns[index]; + } + const Column &operator[](const ColumnIndexType index) const + { + return _columns[index]; + } + + private: + std::string _table_name; + std::vector _columns; + std::vector _terms; + std::vector _offset; + std::vector _column_order; + std::uint16_t _row_size = 0u; +}; +} // namespace beedb::table diff --git a/include/table/table.h b/include/table/table.h new file mode 100644 index 0000000..3c1feab --- /dev/null +++ b/include/table/table.h @@ -0,0 +1,182 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "schema.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Represents a table in the database. + */ +class Table +{ + friend std::ostream &operator<<(std::ostream &stream, const Table &table); + + public: + using id_t = std::int32_t; + + Table(const id_t id, const storage::Page::id_t page_id, const storage::Page::id_t time_travel_page_id, + const Schema &schema) + : _id(id), _page_id(page_id), _time_travel_page_id(time_travel_page_id), _schema(schema) + { + } + + Table(const id_t id, const storage::Page::id_t page_id, const storage::Page::id_t time_travel_page_id, + Schema &&schema) + : _id(id), _page_id(page_id), _time_travel_page_id(time_travel_page_id), _schema(std::move(schema)) + { + } + + ~Table() = default; + + /** + * @return Name of the table. + */ + [[nodiscard]] const std::string &name() const + { + return _schema.table_name(); + } + + /** + * @return Id of the table. + */ + [[nodiscard]] id_t id() const + { + return _id; + } + + /** + * @return Id of the first data page. + */ + [[nodiscard]] storage::Page::id_t page_id() const + { + return _page_id; + } + + /** + * @return Id of the first data page of the time travel storage. + */ + [[nodiscard]] storage::Page::id_t time_travel_page_id() const + { + return _time_travel_page_id; + } + + /** + * @return Id of the last data page (pages are like a linked list). + */ + [[nodiscard]] storage::Page::id_t last_page_id() const + { + return _last_page_id; + } + + /** + * Updates the last page of a table. + * + * @param page_id Id of the last page. + */ + void last_page_id(const storage::Page::id_t page_id) + { + _last_page_id = page_id; + } + + /** + * Updates the first page of the time travel storage. + * + * @param page_id Id of the first page. + */ + void time_travel_page_id(const storage::Page::id_t page_id) + { + _time_travel_page_id = page_id; + } + + void last_time_travel_page_id(const storage::Page::id_t page_id) + { + _last_time_travel_page_id = page_id; + } + + [[nodiscard]] storage::Page::id_t last_time_travel_page_id() const + { + return _last_time_travel_page_id; + } + + /** + * @return Schema of the table. + */ + [[nodiscard]] const Schema &schema() const + { + return _schema; + } + + Column &operator[](const std::size_t index) + { + return _schema[index]; + } + + Column &operator[](const expression::Term &term) + { + const auto index = _schema.column_index(term); + assert(index.has_value()); + return _schema[index.value()]; + } + + const Column &operator[](const std::size_t index) const + { + return _schema[index]; + } + + const Column &operator[](const expression::Term &term) const + { + const auto index = _schema.column_index(term); + assert(index.has_value()); + return _schema[index.value()]; + } + + /** + * @return True, if the table is virtual aka not persisted. + */ + [[nodiscard]] bool is_virtual() const + { + return _id == -1; + } + + [[nodiscard]] std::mutex &latch() + { + return _latch; + } + + private: + const id_t _id; + const storage::Page::id_t _page_id; + storage::Page::id_t _time_travel_page_id; + storage::Page::id_t _last_page_id = storage::Page::INVALID_PAGE_ID; // Will not be persisted + storage::Page::id_t _last_time_travel_page_id = storage::Page::INVALID_PAGE_ID; + Schema _schema; + std::mutex _latch; +}; +} // namespace beedb::table diff --git a/include/table/table_disk_manager.h b/include/table/table_disk_manager.h new file mode 100644 index 0000000..b6f5ca0 --- /dev/null +++ b/include/table/table_disk_manager.h @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "schema.h" +#include "table.h" +#include "tuple.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * The TableDiskManager specifies the interface between tables and the disk. + */ +class TableDiskManager +{ + public: + explicit TableDiskManager(buffer::Manager &buffer_manager); + ~TableDiskManager() = default; + + /** + * Reads the content of a page and interprets it as tuples + * for the given schema. + * + * @param page Page with raw content. + * @param transaction Transaction to read rows for. + * @param schema Schema for the tuples. + * @return List of tuples stored at the given page and list + * of additional pinned pages, needed for time traveling. + */ + [[nodiscard]] std::pair, std::unordered_set> read_rows( + storage::RecordPage *page, concurrency::Transaction *transaction, const Schema &schema); + + /** + * Writes the tuple as raw content to a free page associated with the table. + * The written page will be unpinned. + * + * @param transaction Transaction to insert the tuple for. + * @param table Table to insert the tuple in. + * @param tuple Tuple to insert. The tuple will be moved. + * @return Record identifier. + */ + storage::RecordIdentifier add_row(concurrency::Transaction *transaction, Table &table, Tuple &&tuple); + + /** + * Writes the tuple as raw content to a free page associated with the table. + * The written page will not be unpinned since the tuple is returned to the caller! + * + * @param transaction Transaction to insert the tuple for. + * @param table Table to insert the tuple in. + * @param tuple Tuple to insert. The tuple will be moved. + * @return Access to the tuple. + */ + Tuple add_row_and_get(concurrency::Transaction *transaction, Table &table, Tuple &&tuple); + + /** + * Copies a tuple, originally living in the table space, to the time travel space + * for tuple versioning. + * + * @param transaction Transaction the tuple will be copied in. + * @param table Table of the tuple. + * @param tuple Tuple itself. + * @return Pointer to the newly inserted tuple in the time travel space. + */ + storage::RecordIdentifier copy_row_to_time_travel(concurrency::Transaction *transaction, Table &table, + const Tuple &tuple); + + /** + * Removes the data stored at the page. This is not a database remove operation + * but a hard remove-the-data operation. + * + * @param table Table the tuple is stored in. + * @param record_identifier Record to remove. + */ + void remove_row(Table &table, const storage::RecordIdentifier record_identifier); + + private: + buffer::Manager &_buffer_manager; + + /** + * Scans for a page with enough free space for a new tuple. + * + * @param table Target table. + * @param time_travel When true, we will allocate space in time travel space. + * + * @return Id of the page with free space. + */ + std::pair find_page_for_row(Table &table, const bool time_travel = false); + + /** + * Adds a tuple to a free page. + * + * @param table Target table. + * @param tuple Tuple to be written. + * @return Page and slot the row is written to. + */ + std::pair add_row(concurrency::Transaction *transaction, Table &table, + Tuple &tuple); +}; +} // namespace beedb::table \ No newline at end of file diff --git a/include/table/tuple.h b/include/table/tuple.h new file mode 100644 index 0000000..bd9e203 --- /dev/null +++ b/include/table/tuple.h @@ -0,0 +1,420 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "column.h" +#include "schema.h" +#include "type.h" +#include "value.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Represents a tuple stored on the database. + */ +class Tuple +{ + public: + /** + * Creates a tuple with a given schema, stored on a page on the disk. + * + * @param schema Schema for the tuple. + * @param record_identifier Identifier of the row on the disk. + * @param metadata Metadata for concurrency. + * @param data Raw content of the tuple. + */ + Tuple(const Schema &schema, const storage::RecordIdentifier record_identifier, concurrency::Metadata *metadata, + std::byte *data) + : _schema(schema), _record_identifier(record_identifier), _metadata(metadata), _data(data) + { + } + + /** + * Creates a tuple living in the memory. The tuple is not + * persisted on the disk. + * + * @param schema Schema of the tuple. + * @param row_size Size of the tuple in bytes. + */ + Tuple(const Schema &schema, const std::size_t row_size) + : _schema(schema), _metadata(new concurrency::Metadata(concurrency::timestamp::make_infinity())), + _data(new std::byte[row_size]) + { + std::memset(_data, '\0', row_size); + } + + /** + * Moves the data from the other tuple into this. The + * other tuple will contain no data after moving. + * + * @param schema Schema for the tuple. + * @param move_from Source tuple. + */ + Tuple(const Schema &schema, Tuple &&move_from) + : _schema(schema), _record_identifier(move_from._record_identifier), _metadata(move_from._metadata), + _data(move_from._data) + { + move_from._metadata = nullptr; + move_from._data = nullptr; + } + + /** + * Moves the data from the other tuple into this. The + * other tuple will contain no data after moving. + * + * @param move_from Source tuple. + */ + Tuple(Tuple &&move_from) noexcept + : _schema(move_from.schema()), _record_identifier(move_from._record_identifier), _metadata(move_from._metadata), + _data(move_from._data) + { + move_from._metadata = nullptr; + move_from._data = nullptr; + } + + /** + * Copies a tuple into memory. + * + * @param copy_from Source tuple. + */ + Tuple(const Tuple ©_from) + : _schema(copy_from.schema()), _metadata(new concurrency::Metadata(*copy_from._metadata)), + _data(new std::byte[copy_from._schema.row_size()]) + { + std::memcpy(_data, copy_from._data, copy_from.schema().row_size()); + } + + /** + * Frees the memory, if the tuple is not persisted on the disk. + */ + ~Tuple() + { + if (static_cast(_record_identifier) == false && _data != nullptr) + { + delete _metadata; + delete[] _data; + } + } + + /** + * @return Schema of the tuple. + */ + [[nodiscard]] const Schema &schema() const + { + return _schema; + } + + /** + * @return Id of the page the tuple is persisted on. + */ + [[nodiscard]] storage::Page::id_t page_id() const + { + return _record_identifier.page_id(); + } + + /** + * @return Offset in page or -1 if tuple is in memory. + */ + [[nodiscard]] std::uint16_t slot_id() const + { + return _record_identifier.slot(); + } + + /** + * @return Record identifier. + */ + [[nodiscard]] storage::RecordIdentifier record_identifier() const + { + return _record_identifier; + } + + /** + * @return Access to the raw data. + */ + [[nodiscard]] std::byte *data() const + { + return _data; + } + + /** + * @return True, when the tuple has data. + */ + [[nodiscard]] bool has_data() const + { + return _data != nullptr; + } + + /** + * Updates the raw data. + * + * @param data New raw data. + */ + void data(std::byte *data) + { + _data = data; + } + + /** + * @return Metadata for concurrency of this tuple. + */ + [[nodiscard]] concurrency::Metadata *metadata() const + { + return _metadata; + } + + /** + * Updates the metadata pointer. + * @param metadata New metadata. + */ + void metadata(concurrency::Metadata *metadata) + { + _metadata = metadata; + } + + /** + * Extracts a value from the tuple for a specific column. + * + * @param index Index of the column. + * @return Value of the tuple. + */ + [[nodiscard]] Value get(const std::size_t index) const + { + const auto offset = _schema.offset(index); + const Type &type = _schema[index].type(); + if (type == Type::INT) + { + return {type, *reinterpret_cast(&(_data[offset]))}; + } + else if (type == Type::LONG) + { + return {type, *reinterpret_cast(&(_data[offset]))}; + } + else if (type == Type::DECIMAL) + { + return {type, *reinterpret_cast(&(_data[offset]))}; + } + else if (type == Type::CHAR) + { + return {type, std::string_view(reinterpret_cast(&_data[offset]))}; + } + else if (type == Type::DATE) + { + return {type, *reinterpret_cast(&(_data[offset]))}; + } + + return {type, 0}; + } + + /** + * Set the value of the tuple at the given index to the given long. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const std::int64_t value) + { + const auto offset = _schema.offset(index); + *reinterpret_cast(&(_data[offset])) = value; + } + + /** + * Set the value of the tuple at the given index to the given int. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const std::int32_t value) + { + const auto offset = _schema.offset(index); + *reinterpret_cast(&(_data[offset])) = value; + } + + /** + * Set the value of the tuple at the given index to the given decimal. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const double value) + { + const auto offset = _schema.offset(index); + *reinterpret_cast(&(_data[offset])) = value; + } + + /** + * Set the value of the tuple at the given index to the given date. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const Date value) + { + const auto offset = _schema.offset(index); + *reinterpret_cast(&(_data[offset])) = value; + } + + /** + * Set the value of the tuple at the given index to the given string. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const std::string &value) + { + const auto offset = _schema.offset(index); + const auto column_size = _schema[index].type().size(); + const auto length = std::min(std::size_t(column_size), value.length()); + std::memcpy(&(_data[offset]), value.c_str(), length); + if (value.length() < column_size) + { + std::memset(&(_data[offset + value.length()]), '\0', column_size - value.length()); + } + } + + /** + * Set the value of the tuple at the given index to the given string. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, std::string &&value) + { + const auto offset = _schema.offset(index); + const auto column_size = _schema[index].type().size(); + const auto length = std::min(std::size_t(column_size), value.length()); + std::memmove(&(_data[offset]), value.c_str(), length); + if (value.length() < column_size) + { + std::memset(&(_data[offset + value.length()]), '\0', column_size - value.length()); + } + } + + /** + * Set the value of the tuple at the given index to the given string. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const std::string_view &value) + { + const auto offset = _schema.offset(index); + const auto column_size = _schema[index].type().size(); + const auto length = std::min(std::size_t(column_size), value.length()); + std::memcpy(&(_data[offset]), value.data(), length); + if (value.length() < column_size) + { + std::memset(&(_data[offset + value.length()]), '\0', column_size - value.length()); + } + } + + /** + * Set the value of the tuple at the given index to the given string. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, std::string_view &&value) + { + const auto offset = _schema.offset(index); + const auto column_size = _schema[index].type().size(); + const auto length = std::min(std::size_t(column_size), value.length()); + std::memmove(&(_data[offset]), value.data(), length); + if (value.length() < column_size) + { + std::memset(&(_data[offset + value.length()]), '\0', column_size - value.length()); + } + } + + /** + * Set the value of the tuple at the given index to null. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, std::nullptr_t) + { + const auto type = _schema.column(index).type(); + + if (type == Type::Id::LONG || type == Type::Id::INT || type == Type::Id::DECIMAL || type == Type::Id::DATE) + { + auto value = Value::make_null(type); + set(index, std::move(value.value())); + } + else if (type == Type::Id::CHAR) + { + // TODO: What is the NULL-Value of a string? + const auto offset = _schema.offset(index); + *reinterpret_cast(&_data[offset]) = '\0'; + } + } + + /** + * Set the value of the tuple at the given index to the given value. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const Value &value) + { + set(index, value.value()); + } + + /** + * Set the value of the tuple at the given index to the given value. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, const Value::value_type &value) + { + std::visit([this, index](const auto &v) { this->set(index, v); }, value); + } + + /** + * Set the value of the tuple at the given index to the given value. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, Value &&value) + { + set(index, std::move(value.value())); + } + + /** + * Set the value of the tuple at the given index to the given value. + * @param index Index for the value. + * @param value New value. + */ + void set(const std::size_t index, Value::value_type &&value) + { + std::visit([this, index](auto &&v) { this->set(index, std::move(v)); }, value); + } + + private: + const Schema &_schema; + const storage::RecordIdentifier _record_identifier; + concurrency::Metadata *_metadata; + std::byte *_data; +}; +} // namespace beedb::table \ No newline at end of file diff --git a/include/table/type.h b/include/table/type.h new file mode 100644 index 0000000..e19b24f --- /dev/null +++ b/include/table/type.h @@ -0,0 +1,159 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include "date.h" +#include +#include + +namespace beedb::table +{ +/** + * Represents a database type. + */ +class Type +{ + public: + /** + * Ids for types. + */ + enum Id : std::uint16_t + { + INT, + LONG, + DECIMAL, + CHAR, + DATE, + UNDEFINED + }; + + [[nodiscard]] static Type make_int() noexcept + { + return Type{Id::INT}; + } + [[nodiscard]] static Type make_long() noexcept + { + return Type{Id::LONG}; + } + [[nodiscard]] static Type make_decimal() noexcept + { + return Type{Id::DECIMAL}; + } + [[nodiscard]] static Type make_char(const std::uint16_t length) noexcept + { + return Type{Id::CHAR, length}; + } + [[nodiscard]] static Type make_date() noexcept + { + return Type{Id::DATE}; + } + + constexpr Type() : _id(Id::UNDEFINED) + { + } + + constexpr Type(const Id id, const std::uint16_t length = 0u) : _id(id), _length(length) + { + } + + constexpr Type(const Type &other) = default; + + ~Type() = default; + + /** + * @return Length of the type if the length is dynamic (like char). + */ + [[nodiscard]] std::uint16_t dynamic_length() const + { + return _length; + } + + operator Id() const + { + return _id; + } + bool operator==(Id id) const + { + return _id == id; + } + bool operator!=(Id id) const + { + return _id != id; + } + + Type &operator=(const Id id) + { + _id = id; + return *this; + } + + Type &operator=(const Type &other) = default; + + /** + * @return Real size in bytes of the type. + */ + [[nodiscard]] std::uint16_t size() const + { + switch (_id) + { + case INT: + return sizeof(std::int32_t); + case LONG: + return sizeof(std::int64_t); + case CHAR: + return sizeof(std::int8_t) * dynamic_length(); + case DECIMAL: + return sizeof(double); + case DATE: + return sizeof(Date); + case UNDEFINED: + return 0; + } + } + + /** + * @return Name of the type. + */ + [[nodiscard]] std::string name() const + { + switch (_id) + { + case INT: + return "INT"; + case LONG: + return "LONG"; + case DECIMAL: + return "DECIMAL"; + case DATE: + return "DATE"; + case CHAR: + return "CHAR(" + std::to_string(std::int32_t(dynamic_length())) + ")"; + case UNDEFINED: + return "UNDEFINED"; + } + } + + private: + Id _id; + std::uint16_t _length{0U}; +} __attribute__((packed)); +} // namespace beedb::table diff --git a/include/table/value.h b/include/table/value.h new file mode 100644 index 0000000..b2c8516 --- /dev/null +++ b/include/table/value.h @@ -0,0 +1,595 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include "type.h" +#include +#include +#include +#include +#include +#include + +namespace beedb::table +{ +/** + * Represents a value within a tuple. + * A value contains the raw value and a type. + * The raw value will be interpreted as the given type. + */ +class Value +{ + friend std::ostream &operator<<(std::ostream &stream, const Value &value); + + public: + using value_type = std::variant; + + Value() = default; + + Value(const Type type, const value_type &value) : _type(type), _value(value) + { + } + + Value(const Type type, value_type &&value) : _type(type), _value(std::move(value)) + { + } + + Value(const Value &) = default; + Value(Value &&) = default; + + Value &operator=(const Value &other) + { + _value = other._value; + return *this; + } + + Value &operator=(Value &&other) noexcept + { + _value = std::move(other._value); + return *this; + } + + ~Value() = default; + + /** + * @return Raw value. + */ + [[nodiscard]] const value_type &value() const + { + return _value; + } + + [[nodiscard]] value_type &value() + { + return _value; + } + + /** + * @return Value interpreted as given data type. + */ + template [[nodiscard]] T get() const + { + return std::get(_value); + } + + /** + * Updates the value. + * + * @param value New value. + */ + void value(const value_type &value) + { + _value = value; + } + + /** + * @return Type of the value. + */ + [[nodiscard]] const Type &type() const + { + return _type; + } + + bool operator==(const Type &type) const + { + return _type == type; + } + + bool operator==(const Type::Id type_id) const + { + return _type == type_id; + } + + bool operator==(const Value &other) const + { + if (_type != other._type) + { + return false; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) == std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) == std::get(other._value); + } + } + + return _value == other._value; + } + + bool operator!=(const Value &other) const + { + if (_type != other._type) + { + return true; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) != std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) != std::get(other._value); + } + } + + return _value != other._value; + } + + bool operator<=(const Value &other) const + { + if (_type != other._type) + { + return false; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) <= std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) <= std::get(other._value); + } + } + + return _value <= other._value; + } + + bool operator<(const Value &other) const + { + if (_type != other._type) + { + return false; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) < std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) < std::get(other._value); + } + } + + return _value < other._value; + } + + bool operator>=(const Value &other) const + { + if (_type != other._type) + { + return false; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) >= std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) >= std::get(other._value); + } + } + + return _value >= other._value; + } + + bool operator>(const Value &other) const + { + if (_type != other._type) + { + return false; + } + + if (_type == Type::Id::CHAR) + { + if (std::holds_alternative(_value) && std::holds_alternative(other._value)) + { + return std::get(_value) > std::get(other._value); + } + else if (std::holds_alternative(other._value) && + std::holds_alternative(_value)) + { + return std::get(_value) > std::get(other._value); + } + } + + return _value > other._value; + } + + bool operator==(const std::nullptr_t) const + { + if (_type == Type::INT) + { + return std::get(_value) == std::numeric_limits::min(); + } + else if (_type == Type::LONG) + { + return std::get(_value) == std::numeric_limits::min(); + } + else if (_type == Type::DECIMAL) + { + return std::get(_value) == std::numeric_limits::min(); + } + else if (_type == Type::CHAR) + { + if (std::holds_alternative(_value)) + { + return std::get(_value).empty(); + } + else if (std::holds_alternative(_value)) + { + return std::get(_value).empty(); + } + } + else if (_type == Type::DATE) + { + return std::get(_value) == nullptr; + } + + return true; + } + + /** + * @return Maximal value for the stored type. + */ + [[nodiscard]] Value max() const + { + if (_type == Type::INT) + { + return {_type, value_type{std::numeric_limits::max()}}; + } + else if (_type == Type::LONG) + { + return {_type, std::numeric_limits::max()}; + } + else if (_type == Type::DECIMAL) + { + return {_type, std::numeric_limits::max()}; + } + else if (_type == Type::CHAR) + { + return {_type, std::string{""}}; + } + else + { + return {_type, 0}; + } + } + + /** + * @return Minimal value for the stored type. + */ + [[nodiscard]] [[maybe_unused]] Value min() const + { + if (_type == Type::INT) + { + return {_type, std::numeric_limits::min() + 1}; + } + else if (_type == Type::LONG) + { + return {_type, std::numeric_limits::min() + 1}; + } + else if (_type == Type::DECIMAL) + { + return {_type, std::numeric_limits::min() + 1}; + } + else if (_type == Type::CHAR) + { + return {_type, std::string{""}}; + } + else if (_type == Type::DATE) + { + return {_type, Date{1, 1, 1}}; + } + else + { + return {_type, 0}; + } + } + + Value &operator+=(const Value &other) + { + if (_type == other._type) + { + if (_type == Type::INT) + { + _value = std::get(_value) + std::get(other._value); + } + else if (_type == Type::LONG) + { + _value = std::get(_value) + std::get(other._value); + } + else if (_type == Type::DECIMAL) + { + _value = std::get(_value) + std::get(other._value); + } + else if (_type == Type::DATE) + { + // TODO: Implement Date + Date + } + } + + return *this; + } + + static table::Value make_zero(const Type type) + { + if (type == table::Type::LONG) + { + return {type, std::int64_t{0}}; + } + else if (type == table::Type::INT) + { + return {type, std::int32_t{0}}; + } + else if (type == table::Type::DECIMAL) + { + return {type, double{0.0}}; + } + else if (type == Type::DATE) + { + return {type, Date{}}; + } + else + { + return {type, std::string{""}}; + } + } + + static table::Value make_null(const Type type) + { + if (type == table::Type::LONG) + { + return {type, std::numeric_limits::min()}; + } + else if (type == table::Type::INT) + { + return {type, std::numeric_limits::min()}; + } + else if (type == table::Type::DECIMAL) + { + return {type, std::numeric_limits::min()}; + } + else if (type == Type::DATE) + { + return {type, Date{}}; + } + else + { + return {type, std::string{'\0'}}; + } + } + + explicit operator std::string() const + { + if (*this == nullptr) + { + return "NULL"; + } + + if (_type == Type::INT) + { + return std::to_string(std::get(_value)); + } + else if (_type == Type::LONG) + { + return std::to_string(std::get(_value)); + } + else if (_type == Type::DECIMAL) + { + return std::to_string(std::get(_value)); + } + else if (_type == Type::CHAR) + { + if (std::holds_alternative(_value)) + { + return std::get(_value).substr(0, _type.dynamic_length()); + } + else if (std::holds_alternative(_value)) + { + return std::string{std::get(_value).data()}.substr(0, _type.dynamic_length()); + } + } + else if (_type == Type::DATE) + { + return std::get(_value).to_string(); + } + + return ""; + } + + friend table::Value operator+(const table::Value &left, const table::Value &right) + { + if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) + { + return left; + } + + if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) + { + return {Type::Id::DECIMAL, left.try_get_as() + right.try_get_as()}; + } + else if (left == Type::Id::LONG || right == Type::Id::LONG) + { + return {Type::Id::LONG, left.try_get_as() + right.try_get_as()}; + } + else + { + return {Type::Id::INT, left.try_get_as() + right.try_get_as()}; + } + } + + friend table::Value operator-(const table::Value &left, const table::Value &right) + { + if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) + { + return left; + } + + if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) + { + return {Type::Id::DECIMAL, left.try_get_as() - right.try_get_as()}; + } + else if (left == Type::Id::LONG || right == Type::Id::LONG) + { + return {Type::Id::LONG, left.try_get_as() - right.try_get_as()}; + } + else + { + return {Type::Id::INT, left.try_get_as() - right.try_get_as()}; + } + } + + friend table::Value operator/(const table::Value &left, const table::Value &right) + { + if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) + { + return left; + } + + if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) + { + return {Type::Id::DECIMAL, left.try_get_as() / right.try_get_as()}; + } + else if (left == Type::Id::LONG || right == Type::Id::LONG) + { + return {Type::Id::LONG, left.try_get_as() / right.try_get_as()}; + } + else + { + return {Type::Id::INT, left.try_get_as() / right.try_get_as()}; + } + } + + friend table::Value operator*(const table::Value &left, const table::Value &right) + { + if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) + { + return left; + } + + if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) + { + return {Type::Id::DECIMAL, left.try_get_as() * right.try_get_as()}; + } + else if (left == Type::Id::LONG || right == Type::Id::LONG) + { + return {Type::Id::LONG, left.try_get_as() * right.try_get_as()}; + } + else + { + return {Type::Id::INT, left.try_get_as() * right.try_get_as()}; + } + } + + template [[nodiscard]] T try_get_as() const + { + T value; + std::visit( + [&value](const auto &v) { + using VT = std::decay_t; + if constexpr (std::is_same::value) + { + value = v; + } + else if constexpr ((std::is_same::value && + (std::is_same::value || std::is_same::value)) || + (std::is_same::value && + (std::is_same::value || std::is_same::value)) || + (std::is_same::value && + (std::is_same::value || std::is_same::value))) + { + value = static_cast(v); + } + }, + _value); + + return value; + } + + private: + const Type _type; + value_type _value{std::int32_t(0U)}; +}; +} // namespace beedb::table + +namespace std +{ +template <> struct hash +{ + public: + std::size_t operator()(const beedb::table::Value &value) const + { + std::size_t h; + std::visit( + [&h](const auto &v) { + using T = std::decay_t; + h = std::hash()(v); + }, + value.value()); + return h; + } +}; +} // namespace std diff --git a/include/util/clock.h b/include/util/clock.h new file mode 100644 index 0000000..f6ddd64 --- /dev/null +++ b/include/util/clock.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include + +namespace beedb::util +{ +/** + * Clock for time measurements while query execution. + * The clock is started directly on creation. + */ +class Clock +{ + public: + Clock() : _start(std::chrono::steady_clock::now()) + { + } + + ~Clock() = default; + + /** + * Stops the measurement. + * + * @return Measured time in milliseconds. + */ + [[nodiscard]] std::chrono::milliseconds end() const + { + const auto end = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(end - _start); + } + + private: + std::chrono::time_point _start; +}; +} // namespace beedb::util \ No newline at end of file diff --git a/include/util/command_line_interface.h b/include/util/command_line_interface.h new file mode 100644 index 0000000..c2b59e6 --- /dev/null +++ b/include/util/command_line_interface.h @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include + +namespace beedb::util +{ +class CommandLineInterface +{ + public: + CommandLineInterface(std::string &&history_file_name, std::string &&prompt_message) + : _history_file_name(std::move(history_file_name)), _prompt_message(std::move(prompt_message)) + { + linenoiseHistoryLoad(_history_file_name.c_str()); + } + + std::optional next() + { + char *line; + if ((line = linenoise(_prompt_message.c_str())) != nullptr) + { + auto line_as_string = std::string{line}; + std::free(line); + + linenoiseHistoryAdd(line_as_string.c_str()); + linenoiseHistorySave(_history_file_name.c_str()); + + return std::make_optional(std::move(line_as_string)); + } + + return std::nullopt; + } + + private: + const std::string _history_file_name; + const std::string _prompt_message; +}; +} // namespace beedb::util \ No newline at end of file diff --git a/include/util/ini_parser.h b/include/util/ini_parser.h new file mode 100644 index 0000000..5d986f7 --- /dev/null +++ b/include/util/ini_parser.h @@ -0,0 +1,171 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include + +namespace beedb::util +{ +/** + * Parses an ini file and stores the parsed key,value tuples + * including sections. + */ +class IniParser +{ + public: + using key_t = std::pair; + using value_t = std::string; + + /** + * Initializes the parser and parses the given + * .ini file. + * + * @param file_name + */ + explicit IniParser(const std::string &file_name); + + ~IniParser() = default; + + /** + * @return True, if the parsed config is empty + * (maybe because the file was not found). + */ + [[nodiscard]] bool empty() const + { + return _configurations.empty(); + } + + /** + * Returns the value for a given + * key in a given section or the + * default value of the key does + * not exists. + * + * @param default_value + * @return Value of the key or default value. + */ + template T get(const std::string &, const std::string &, const T &default_value) const + { + return default_value; + } + + private: + struct key_t_hash + { + std::size_t operator()(const key_t &pair) const + { + return std::hash()(pair.first) ^ std::hash()(pair.second); + } + }; + + std::unordered_map _configurations; + + /** + * Parses the ini file with the given name. + * + * @param file_name Name of the ini file. + */ + void parse(const std::string &file_name); +}; +} // namespace beedb::util + +template <> +inline std::string beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const std::string &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == this->_configurations.end()) + { + return default_value; + } + + return this->_configurations.at(config_key); +} + +template <> +inline bool beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const bool &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == _configurations.end()) + { + return default_value; + } + + return bool(std::stoi(this->_configurations.at(config_key))); +} + +template <> +inline std::int32_t beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const std::int32_t &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == this->_configurations.end()) + { + return default_value; + } + + return std::int32_t(std::stol(this->_configurations.at(config_key))); +} + +template <> +inline std::uint32_t beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const std::uint32_t &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == this->_configurations.end()) + { + return default_value; + } + + return std::uint32_t(std::stoul(this->_configurations.at(config_key))); +} + +template <> +inline std::int64_t beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const std::int64_t &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == this->_configurations.end()) + { + return default_value; + } + + return std::int64_t(std::stoll(this->_configurations.at(config_key))); +} + +template <> +inline std::uint64_t beedb::util::IniParser::get(const std::string §ion, const std::string &key, + const std::uint64_t &default_value) const +{ + const auto config_key = key_t{std::make_pair(section, key)}; + if (this->_configurations.find(config_key) == this->_configurations.end()) + { + return default_value; + } + + return std::uint64_t(std::stoull(this->_configurations.at(config_key))); +} \ No newline at end of file diff --git a/include/util/mpmc_queue.h b/include/util/mpmc_queue.h new file mode 100644 index 0000000..c56c396 --- /dev/null +++ b/include/util/mpmc_queue.h @@ -0,0 +1,148 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace beedb::util +{ +/** + * Author: Jan Mühlig + * Inspired by http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue + */ +template class BoundMPMCQueue +{ + public: + BoundMPMCQueue(const std::uint16_t capacity) noexcept : _capacity(capacity) + { + _storage = new (std::aligned_alloc(64, sizeof(std::pair) * capacity)) + std::pair[capacity]; + std::memset(static_cast(_storage), 0, sizeof(std::pair) * capacity); + for (auto i = 0u; i < capacity; ++i) + { + std::get<0>(_storage[i]).store(i, std::memory_order_relaxed); + } + } + ~BoundMPMCQueue() noexcept + { + delete[] _storage; + } + BoundMPMCQueue(const BoundMPMCQueue &) = delete; + BoundMPMCQueue(BoundMPMCQueue &&) = delete; + BoundMPMCQueue &operator=(const BoundMPMCQueue &) = delete; + BoundMPMCQueue &operator=(BoundMPMCQueue &&) = delete; + void push_back(const T &item) noexcept + { + while (try_push_back(item) == false) + ; + } + T pop_front() noexcept + { + T item; + while (try_pop_front(item) == false) + ; + return item; + } + T pop_front_or(const T &default_value) noexcept + { + T item; + if (try_pop_front(item)) + { + return item; + } + else + { + return default_value; + } + } + bool try_push_back(const T &item) noexcept + { + auto pos = _head.load(std::memory_order_relaxed); + std::uint64_t slot; + for (;;) + { + slot = pos % _capacity; + const auto sequence = std::get<0>(_storage[slot]).load(std::memory_order_acquire); + const auto difference = std::int64_t(sequence) - std::int64_t(pos); + if (difference == 0) + { + if (_head.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) + { + break; + } + } + else if (difference < 0) + { + return false; + } + else + { + pos = _head.load(std::memory_order_relaxed); + } + } + std::get<1>(_storage[slot]) = item; + std::get<0>(_storage[slot]).store(pos + 1, std::memory_order_release); + return true; + } + bool try_pop_front(T &return_item) noexcept + { + auto pos = _tail.load(std::memory_order_relaxed); + std::uint64_t slot; + for (;;) + { + slot = pos % _capacity; + const auto sequence = std::get<0>(_storage[slot]).load(std::memory_order_acquire); + const auto difference = std::int64_t(sequence) - std::int64_t(pos + 1); + if (difference == 0) + { + if (_tail.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) + { + break; + } + } + else if (difference < 0) + { + return false; + } + else + { + pos = _tail.load(std::memory_order_relaxed); + } + } + return_item = std::get<1>(_storage[slot]); + std::get<0>(_storage[slot]).store(pos + _capacity, std::memory_order_release); + return true; + } + + private: + const std::uint32_t _capacity; + std::pair *_storage; + alignas(64) std::atomic_uint64_t _head{0u}; + alignas(64) std::atomic_uint64_t _tail{0u}; +}; +} // namespace beedb::util diff --git a/include/util/optional.h b/include/util/optional.h new file mode 100644 index 0000000..58e440f --- /dev/null +++ b/include/util/optional.h @@ -0,0 +1,228 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include + +namespace beedb::util +{ +/** + * Implementation of an optional data container. + * Inspired by std::optional (https://en.cppreference.com/w/cpp/utility/optional). + * In contrast to std::optional, the value is moved on assignment. + */ +template class optional +{ + public: + optional() = default; + + ~optional() noexcept + { + if (_is_empty == false) + { + pointer()->~T(); + } + } + + explicit optional(const T &t) : _is_empty(false) + { + new (_storage.data()) T(t); + } + + explicit optional(const T &&) = delete; + + explicit optional(T &t) : _is_empty(false) + { + new (_storage.data()) T(t); + } + + explicit optional(T &&t) : _is_empty(false) + { + new (_storage.data()) T(std::move(t)); + } + + optional(const optional &o) : _is_empty(o._is_empty) + { + if (o._is_empty == false) + { + new (_storage.data()) T(o.value()); + } + } + + optional(const optional &&) = delete; + + optional(optional &o) : _is_empty(o._is_empty) + { + if (o._is_empty == false) + { + new (_storage.data()) T(o.value()); + } + } + + optional(optional &&o) noexcept : _is_empty(o._is_empty) + { + if (o._is_empty == false) + { + new (_storage.data()) T(std::move(o.value())); + } + o._is_empty = true; + } + + optional &operator=(const T &t) + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = false; + new (_storage.data()) T(t); + return *this; + } + + optional &operator=(T &t) + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = false; + new (_storage.data()) T(t); + return *this; + } + + optional &operator=(T &&t) + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = false; + new (_storage.data()) T(std::move(t)); + return *this; + } + + optional &operator=(const optional &o) + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = o._is_empty; + if (o._is_empty == false) + { + new (_storage.data()) T(o.value()); + } + return *this; + } + + optional &operator=(optional &&o) noexcept + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = o._is_empty; + if (o._is_empty == false) + { + new (_storage.data()) T(std::move(o.value())); + o._is_empty = true; + } + return *this; + } + + [[nodiscard]] bool has_value() const noexcept + { + return _is_empty == false; + } + T &value() noexcept + { + return *pointer(); + } + const T &value() const noexcept + { + return *pointer(); + } + + void clear() noexcept + { + if (_is_empty == false) + { + pointer()->~T(); + } + _is_empty = true; + } + + T *operator->() noexcept + { + return pointer(); + } + operator T *() noexcept + { + return pointer(); + } + operator T &() noexcept + { + return value(); + } + operator const T &() const noexcept + { + return value(); + } + bool operator==(const bool b) const noexcept + { + return has_value() == b; + } + bool operator!=(const bool b) const noexcept + { + return has_value() != b; + } + bool operator==(const std::nullptr_t) const noexcept + { + return has_value() == false; + } + bool operator!=(const std::nullptr_t) const noexcept + { + return has_value() == true; + } + operator bool() const noexcept + { + return has_value(); + } + + private: + std::array _storage; + bool _is_empty = true; + + T *pointer() noexcept + { + return reinterpret_cast(_storage.data()); + } +}; + +template optional make_optional(T &&t) +{ + return optional{std::forward(t)}; +} +} // namespace beedb::util \ No newline at end of file diff --git a/include/util/perf.h b/include/util/perf.h new file mode 100644 index 0000000..7c0730b --- /dev/null +++ b/include/util/perf.h @@ -0,0 +1,192 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * For more Performance Counter take a look into the Manual from Intel: + * https://software.intel.com/sites/default/files/managed/8b/6e/335279_performance_monitoring_events_guide.pdf + * + * To get event ids from manual specification see libpfm4: + * http://www.bnikolic.co.uk/blog/hpc-prof-events.html + * Clone, Make, use examples/check_events to generate event id code from event: + * ./check_events :[:c=] + * Example: + * ./cycle_activity:0x14:c=20 + */ + +namespace beedb::util +{ + +/** + * Represents a Linux Performance Counter. + */ +class PerfCounter +{ + public: + PerfCounter(std::string &&name, const std::uint64_t type, const std::uint64_t event_id) : _name(std::move(name)) + { + std::memset(&_perf_event_attribute, 0, sizeof(perf_event_attr)); + _perf_event_attribute.type = type; + _perf_event_attribute.size = sizeof(perf_event_attr); + _perf_event_attribute.config = event_id; + _perf_event_attribute.disabled = true; + _perf_event_attribute.inherit = 1; + _perf_event_attribute.exclude_kernel = false; + _perf_event_attribute.exclude_hv = false; + _perf_event_attribute.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; + } + + ~PerfCounter() = default; + + bool open() + { + _file_descriptor = syscall(__NR_perf_event_open, &_perf_event_attribute, 0, -1, -1, 0); + return _file_descriptor >= 0; + } + + bool start() + { + ioctl(_file_descriptor, PERF_EVENT_IOC_RESET, 0); + ioctl(_file_descriptor, PERF_EVENT_IOC_ENABLE, 0); + return ::read(_file_descriptor, &_prev, sizeof(read_format)) == sizeof(read_format); + } + + bool stop() + { + const auto is_read = ::read(_file_descriptor, &_data, sizeof(read_format)) == sizeof(read_format); + ioctl(_file_descriptor, PERF_EVENT_IOC_DISABLE, 0); + return is_read; + } + + [[nodiscard]] double read() const + { + const auto multiplexing_correction = static_cast(_data.time_enabled - _prev.time_enabled) / + static_cast(_data.time_running - _prev.time_running); + return static_cast(_data.value - _prev.value) * multiplexing_correction; + } + + [[nodiscard]] const std::string &name() const + { + return _name; + } + explicit operator const std::string &() const + { + return name(); + } + + bool operator==(const std::string &name) const + { + return _name == name; + } + + private: + struct read_format + { + std::uint64_t value = 0; + std::uint64_t time_enabled = 0; + std::uint64_t time_running = 0; + }; + + const std::string _name; + std::int32_t _file_descriptor = -1; + perf_event_attr _perf_event_attribute{}; + read_format _prev{}; + read_format _data{}; +}; + +/** + * Holds a set of performance counter and starts/stops them together. + */ +class Perf +{ + public: + [[maybe_unused]] static PerfCounter INSTRUCTIONS; + [[maybe_unused]] static PerfCounter CYCLES; + [[maybe_unused]] static PerfCounter L1_MISSES; + [[maybe_unused]] [[maybe_unused]] static PerfCounter LLC_MISSES; + [[maybe_unused]] static PerfCounter LLC_REFERENCES; + [[maybe_unused]] static PerfCounter STALLED_CYCLES_BACKEND; + [[maybe_unused]] static PerfCounter STALLS_MEM_ANY; + [[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_NTA; + [[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_T0; + [[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_T1_T2; + [[maybe_unused]] static PerfCounter SW_PREFETCH_ACCESS_WRITE; + + Perf() noexcept = default; + ~Perf() noexcept = default; + + bool add(PerfCounter &counter_) + { + if (counter_.open()) + { + _counter.push_back(counter_); + return true; + } + + return false; + } + + void start() + { + for (auto &counter_ : _counter) + { + counter_.start(); + } + } + + void stop() + { + for (auto &counter_ : _counter) + { + counter_.stop(); + } + } + + double operator[](const std::string &name) const + { + auto counter_iterator = std::find(_counter.begin(), _counter.end(), name); + if (counter_iterator != _counter.end()) + { + return counter_iterator->read(); + } + + return 0.0; + } + + std::vector &counter() + { + return _counter; + } + + private: + std::vector _counter; +}; +} // namespace beedb::util diff --git a/include/util/quicksort.h b/include/util/quicksort.h new file mode 100644 index 0000000..b9e4cf0 --- /dev/null +++ b/include/util/quicksort.h @@ -0,0 +1,91 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include + +namespace beedb::util +{ +/** + * Sort algorithm based on quicksort. + * Values are moved instead of copied while sorting. + */ +class Quicksort +{ + public: + /** + * Sorts the data within the given container. + * @param data Container with data. + * @param comparator Comparator for sorting. + */ + template static void sort(std::vector &data, const C &comparator) + { + sort(data, 0, data.size() - 1, comparator); + } + + private: + template + static void sort(std::vector &data, const std::int64_t low_index, const std::int64_t high_index, + const C &comparator) + { + if (low_index < high_index) + { + const auto pivot = partition(data, low_index, high_index, comparator); + + sort(data, low_index, pivot - 1, comparator); + sort(data, pivot + 1, high_index, comparator); + } + } + + template + static std::size_t partition(std::vector &data, const std::int64_t low_index, const std::int64_t high_index, + const C &comparator) + { + const auto &pivot_element = data[high_index]; + auto i = low_index; + + for (auto j = low_index; j < high_index; ++j) + { + if (comparator(data[j], pivot_element)) + { + swap(data, std::size_t(i), std::size_t(j)); + i++; + } + } + swap(data, std::size_t(i), std::size_t(high_index)); + return std::size_t(i); + } + + template static void swap(std::vector &data, const std::size_t i, const std::size_t j) + { + if (i == j) + { + return; + } + auto first{std::move(data[i])}; + new ((data.data() + i)) T(std::move(data[j])); + new ((data.data() + j)) T(std::move(first)); + } +}; +} // namespace beedb::util \ No newline at end of file diff --git a/include/util/random_generator.h b/include/util/random_generator.h new file mode 100644 index 0000000..a5128e2 --- /dev/null +++ b/include/util/random_generator.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once + +#include +#include + +namespace beedb::util +{ +/** + * Random generator for cheap pseudo random numbers. + */ +class RandomGenerator +{ + public: + RandomGenerator() noexcept; + explicit RandomGenerator(std::uint32_t seed) noexcept; + + /** + * @return Next pseudo random number. + */ + std::int32_t next() noexcept; + + /** + * @param max_value Max value. + * @return Next pseudo random number in range (0,max_value]. + */ + std::uint32_t next(const std::uint64_t max_value) noexcept + { + return next() % max_value; + } + + private: + std::array _register; + std::uint32_t _multiplier = 0; + std::uint32_t _ic_state = 0; + const std::uint32_t _addend = 0; +}; +} // namespace beedb::util diff --git a/include/util/text_table.h b/include/util/text_table.h new file mode 100644 index 0000000..7618811 --- /dev/null +++ b/include/util/text_table.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------------* + * Architecture & Implementation of DBMS * + *------------------------------------------------------------------------------* + * Copyright 2022 Databases and Information Systems Group TU Dortmund * + * Visit us at * + * http://dbis.cs.tu-dortmund.de/cms/en/home/ * + * * + * 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. * + * * + * Authors: * + * Maximilian Berens * + * Roland Kühn * + * Jan Mühlig * + *------------------------------------------------------------------------------* + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace beedb::util +{ +/** + * Formats the given data and prints it as table. + */ +class TextTable +{ + friend std::ostream &operator<<(std::ostream &stream, const TextTable &text_table); + + public: + TextTable() = default; + ~TextTable() = default; + + /** + * Set header for the table. + * + * @param row_values Header values. + */ + void header(std::vector &&row_values); + + /** + * Adds a row to the table. + * + * @param row_values Row. + */ + void push_back(std::vector &&row_values) + { + _table_rows.emplace_back(std::move(row_values)); + } + + /** + * Clears the table. + */ + void clear() + { + _table_rows.clear(); + } + + /** + * @return True, when no row or header was added. + */ + [[nodiscard]] bool empty() const + { + return _table_rows.empty(); + } + + private: + std::vector> _table_rows; + + /** + * Calculates the printed length of a given string. + * @param input String to calculate length. + * @return Number of printed characters. + */ + [[nodiscard]] static std::size_t printed_length(const std::string &input); + + /** + * Calculates the maximal length for each column. + * + * @return List of length per column. + */ + [[nodiscard]] std::vector length_per_column() const; + + /** + * Prints a separator line to the given output stream. + * + * @param stream Output stream the line should be printed to. + * @param column_lengths List of maximal length per column. + * @return The given output stream. + */ + static std::ostream &print_separator_line(std::ostream &stream, const std::vector &column_lengths, + std::string &&left, std::string &&right, std::string &&separator); + + /** + * Prints a row to the given output stream. + * + * @param stream Output stream the row should be printed to. + * @param column_lengths List of maximal length per column. + * @param row The row that should be printed. + * @return The given output stream. + */ + static std::ostream &print_row(std::ostream &stream, const std::vector &column_lengths, + const std::vector &row); +}; +} // namespace beedb::util \ No newline at end of file diff --git a/lib/argparse/argparse.hpp b/lib/argparse/argparse.hpp new file mode 100644 index 0000000..4328492 --- /dev/null +++ b/lib/argparse/argparse.hpp @@ -0,0 +1,539 @@ +/* + __ _ _ __ __ _ _ __ __ _ _ __ ___ ___ + / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++ +| (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse + \__,_|_| \__, | .__/ \__,_|_| |___/\___| + |___/|_| + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019 Pranav Srinivas Kumar . + +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. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace argparse { + + namespace details { // namespace for helper methods + + template struct is_container_helper {}; + + template + struct is_container : std::false_type {}; + + template <> struct is_container : std::false_type {}; + + template + struct is_container< + T, + std::conditional_t().begin()), + decltype(std::declval().end()), + decltype(std::declval().size())>, + void>> : public std::true_type {}; + + template + static constexpr bool is_container_v = is_container::value; + + template + using enable_if_container = std::enable_if_t, T>; + + template + using enable_if_not_container = std::enable_if_t, T>; + } // namespace + + class Argument { + friend class ArgumentParser; + + public: + Argument() = default; + + template + explicit Argument(Args... args) + : mNames({std::move(args)...}), mIsOptional((is_optional(args) || ...)) { + std::sort( + mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) { + return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); + }); + } + + Argument &help(std::string aHelp) { + mHelp = std::move(aHelp); + return *this; + } + + Argument &default_value(std::any aDefaultValue) { + mDefaultValue = std::move(aDefaultValue); + return *this; + } + + Argument &required() { + mIsRequired = true; + return *this; + } + + Argument &implicit_value(std::any aImplicitValue) { + mImplicitValue = std::move(aImplicitValue); + mNumArgs = 0; + return *this; + } + + Argument &action(std::function aAction) { + mAction = std::move(aAction); + return *this; + } + + Argument &nargs(size_t aNumArgs) { + mNumArgs = aNumArgs; + return *this; + } + + template + Iterator consume(Iterator start, Iterator end, std::string usedName = {}) { + if (mIsUsed) { + throw std::runtime_error("Duplicate argument"); + } + mIsUsed = true; + mUsedName = std::move(usedName); + if (mNumArgs == 0) { + mValues.emplace_back(mImplicitValue); + return start; + } else if (mNumArgs <= static_cast(std::distance(start, end))) { + end = std::next(start, mNumArgs); + if (std::any_of(start, end, Argument::is_optional)) { + throw std::runtime_error("optional argument in parameter sequence"); + } + std::transform(start, end, std::back_inserter(mValues), mAction); + return end; + } else if (mDefaultValue.has_value()) { + return start; + } else { + throw std::runtime_error("Too few arguments"); + } + } + + /* + * @throws std::runtime_error if argument values are not valid + */ + void validate() const { + if (mIsOptional) { + if (mIsUsed && mValues.size() != mNumArgs && !mDefaultValue.has_value()) { + std::stringstream stream; + stream << mUsedName << ": expected " << mNumArgs + << " argument(s). " << mValues.size() << " provided."; + throw std::runtime_error(stream.str()); + } else { + // TODO: check if an implicit value was programmed for this argument + if (!mIsUsed && !mDefaultValue.has_value() && mIsRequired) { + std::stringstream stream; + stream << mNames[0] << ": required."; + throw std::runtime_error(stream.str()); + } + if (mIsUsed && mIsRequired && mValues.size() == 0) { + std::stringstream stream; + stream << mUsedName << ": no value provided."; + throw std::runtime_error(stream.str()); + } + } + } else { + if (mValues.size() != mNumArgs && !mDefaultValue.has_value()) { + std::stringstream stream; + stream << mUsedName << ": expected " << mNumArgs + << " argument(s). " << mValues.size() << " provided."; + throw std::runtime_error(stream.str()); + } + } + } + + size_t get_arguments_length() const { + return std::accumulate(std::begin(mNames), std::end(mNames), size_t(0), + [](const auto &sum, const auto &s) { + return sum + s.size() + + 1; // +1 for space between names + }); + } + + friend std::ostream &operator<<(std::ostream &stream, + const Argument &argument) { + std::stringstream nameStream; + std::copy(std::begin(argument.mNames), std::end(argument.mNames), + std::ostream_iterator(nameStream, " ")); + stream << nameStream.str() << "\t" << argument.mHelp; + if (argument.mIsRequired) + stream << "[Required]"; + stream << "\n"; + return stream; + } + + template bool operator!=(const T &aRhs) const { + return !(*this == aRhs); + } + + /* + * Entry point for template non-container types + * @throws std::logic_error in case of incompatible types + */ + template + std::enable_if_t, bool> operator==(const T &aRhs) const { + return get() == aRhs; + } + + /* + * Template specialization for containers + * @throws std::logic_error in case of incompatible types + */ + template + std::enable_if_t, bool> operator==(const T &aRhs) const { + using ValueType = typename T::value_type; + auto tLhs = get(); + if (tLhs.size() != aRhs.size()) + return false; + else { + return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs), + [](const auto &lhs, const auto &rhs) { + return std::any_cast(lhs) == rhs; + }); + } + } + + private: + static bool is_integer(const std::string &aValue) { + if (aValue.empty() || + ((!isdigit(aValue[0])) && (aValue[0] != '-') && (aValue[0] != '+'))) + return false; + char *tPtr; + strtol(aValue.c_str(), &tPtr, 10); + return (*tPtr == 0); + } + + static bool is_float(const std::string &aValue) { + std::istringstream tStream(aValue); + float tFloat; + // noskipws considers leading whitespace invalid + tStream >> std::noskipws >> tFloat; + // Check the entire string was consumed + // and if either failbit or badbit is set + return tStream.eof() && !tStream.fail(); + } + + // If an argument starts with "-" or "--", then it's optional + static bool is_optional(const std::string &aName) { + return (!aName.empty() && aName[0] == '-' && !is_integer(aName) && + !is_float(aName)); + } + + static bool is_positional(const std::string &aName) { + return !is_optional(aName); + } + + /* + * Getter for template non-container types + * @throws std::logic_error in case of incompatible types + */ + template details::enable_if_not_container get() const { + if (!mValues.empty()) { + return std::any_cast(mValues.front()); + } + if (mDefaultValue.has_value()) { + return std::any_cast(mDefaultValue); + } + throw std::logic_error("No value provided"); + } + + /* + * Getter for container types + * @throws std::logic_error in case of incompatible types + */ + template details::enable_if_container get() const { + using ValueType = typename CONTAINER::value_type; + CONTAINER tResult; + if (!mValues.empty()) { + std::transform( + std::begin(mValues), std::end(mValues), std::back_inserter(tResult), + [](const auto &value) { return std::any_cast(value); }); + return tResult; + } + if (mDefaultValue.has_value()) { + const auto &tDefaultValues = + std::any_cast(mDefaultValue); + std::transform(std::begin(tDefaultValues), std::end(tDefaultValues), + std::back_inserter(tResult), [](const auto &value) { + return std::any_cast(value); + }); + return tResult; + } + throw std::logic_error("No value provided"); + } + + std::vector mNames; + std::string mUsedName; + std::string mHelp; + std::any mDefaultValue; + std::any mImplicitValue; + std::function mAction = + [](const std::string &aValue) { return aValue; }; + std::vector mValues; + std::vector mRawValues; + size_t mNumArgs = 1; + bool mIsOptional = false; + bool mIsRequired = false; + bool mIsUsed = false; // relevant for optional arguments. True if used by user + + public: + static constexpr auto mHelpOption = "-h"; + static constexpr auto mHelpOptionLong = "--help"; + }; + + class ArgumentParser { + public: + explicit ArgumentParser(std::string aProgramName = {}) + : mProgramName(std::move(aProgramName)) { + add_argument(Argument::mHelpOption, Argument::mHelpOptionLong) + .help("show this help message and exit") + .nargs(0) + .default_value(false) + .implicit_value(true); + } + + // Parameter packing + // Call add_argument with variadic number of string arguments + template Argument &add_argument(Targs... Fargs) { + std::shared_ptr tArgument = + std::make_shared(std::move(Fargs)...); + + if (tArgument->mIsOptional) + mOptionalArguments.emplace_back(tArgument); + else + mPositionalArguments.emplace_back(tArgument); + + for (const auto &mName : tArgument->mNames) { + mArgumentMap.insert_or_assign(mName, tArgument); + } + return *tArgument; + } + + // Parameter packed add_parents method + // Accepts a variadic number of ArgumentParser objects + template void add_parents(Targs... Fargs) { + const auto tNewParentParsers = {Fargs...}; + for (const auto &tParentParser : tNewParentParsers) { + const auto &tPositionalArguments = tParentParser.mPositionalArguments; + std::copy(std::begin(tPositionalArguments), + std::end(tPositionalArguments), + std::back_inserter(mPositionalArguments)); + + const auto &tOptionalArguments = tParentParser.mOptionalArguments; + std::copy(std::begin(tOptionalArguments), std::end(tOptionalArguments), + std::back_inserter(mOptionalArguments)); + + const auto &tArgumentMap = tParentParser.mArgumentMap; + for (const auto &[tKey, tValue] : tArgumentMap) { + mArgumentMap.insert_or_assign(tKey, tValue); + } + } + std::move(std::begin(tNewParentParsers), std::end(tNewParentParsers), + std::back_inserter(mParentParsers)); + } + + /* Call parse_args_internal - which does all the work + * Then, validate the parsed arguments + * This variant is used mainly for testing + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args(const std::vector &aArguments) { + parse_args_internal(aArguments); + parse_args_validate(); + } + + /* Main entry point for parsing command-line arguments using this + * ArgumentParser + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args(int argc, const char *const argv[]) { + std::vector arguments; + std::copy(argv, argv + argc, std::back_inserter(arguments)); + parse_args(arguments); + } + + /* Getter enabled for all template types other than std::vector and std::list + * @throws std::logic_error in case of an invalid argument name + * @throws std::logic_error in case of incompatible types + */ + template T get(const std::string &aArgumentName) { + auto tIterator = mArgumentMap.find(aArgumentName); + if (tIterator != mArgumentMap.end()) { + return tIterator->second->get(); + } + throw std::logic_error("No such argument"); + } + + /* Indexing operator. Return a reference to an Argument object + * Used in conjuction with Argument.operator== e.g., parser["foo"] == true + * @throws std::logic_error in case of an invalid argument name + */ + Argument &operator[](const std::string &aArgumentName) { + auto tIterator = mArgumentMap.find(aArgumentName); + if (tIterator != mArgumentMap.end()) { + return *(tIterator->second); + } + throw std::logic_error("No such argument"); + } + + // Printing the one and only help message + // I've stuck with a simple message format, nothing fancy. + // TODO: support user-defined help and usage messages for the ArgumentParser + std::string print_help() { + std::stringstream stream; + stream << std::left; + stream << "Usage: " << mProgramName << " [options] "; + size_t tLongestArgumentLength = get_length_of_longest_argument(); + + for (const auto &argument : mPositionalArguments) { + stream << argument->mNames.front() << " "; + } + stream << "\n\n"; + + if (!mPositionalArguments.empty()) + stream << "Positional arguments:\n"; + + for (const auto &mPositionalArgument : mPositionalArguments) { + stream.width(tLongestArgumentLength); + stream << *mPositionalArgument; + } + + if (!mOptionalArguments.empty()) + stream << (mPositionalArguments.empty() ? "" : "\n") + << "Optional arguments:\n"; + + for (const auto &mOptionalArgument : mOptionalArguments) { + stream.width(tLongestArgumentLength); + stream << *mOptionalArgument; + } + + std::cout << stream.str(); + return stream.str(); + } + + private: + /* + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args_internal(const std::vector &aArguments) { + if (mProgramName.empty() && !aArguments.empty()) { + mProgramName = aArguments.front(); + } + auto end = std::end(aArguments); + auto positionalArgumentIt = std::begin(mPositionalArguments); + for (auto it = std::next(std::begin(aArguments)); it != end;) { + const auto &tCurrentArgument = *it; + if (tCurrentArgument == Argument::mHelpOption || + tCurrentArgument == Argument::mHelpOptionLong) { + throw std::runtime_error("help called"); + } + if (Argument::is_positional(tCurrentArgument)) { + if (positionalArgumentIt == std::end(mPositionalArguments)) { + throw std::runtime_error( + "Maximum number of positional arguments exceeded"); + } + auto tArgument = *(positionalArgumentIt++); + it = tArgument->consume(it, end); + } else if (auto tIterator = mArgumentMap.find(tCurrentArgument); + tIterator != mArgumentMap.end()) { + auto tArgument = tIterator->second; + it = tArgument->consume(std::next(it), end, tCurrentArgument); + } else if (const auto &tCompoundArgument = tCurrentArgument; + tCompoundArgument.size() > 1 && tCompoundArgument[0] == '-' && + tCompoundArgument[1] != '-') { + ++it; + for (size_t j = 1; j < tCompoundArgument.size(); j++) { + auto iCurrentArgument = std::string{'-', tCompoundArgument[j]}; + if (auto iIterator = mArgumentMap.find(iCurrentArgument); + iIterator != mArgumentMap.end()) { + auto tArgument = iIterator->second; + it = tArgument->consume(it, end, iCurrentArgument); + } else { + throw std::runtime_error("Unknown argument"); + } + } + } else { + throw std::runtime_error("Unknown argument"); + } + } + } + + /* + * @throws std::runtime_error in case of any invalid argument + */ + void parse_args_validate() { + // Check if all arguments are parsed + std::for_each(std::begin(mArgumentMap), std::end(mArgumentMap), + [](const auto &argPair) { + const auto &tArgument = argPair.second; + tArgument->validate(); + }); + } + + // Used by print_help. + size_t get_length_of_longest_argument() { + if (mArgumentMap.empty()) + return 0; + std::vector argumentLengths(mArgumentMap.size()); + std::transform(std::begin(mArgumentMap), std::end(mArgumentMap), + std::begin(argumentLengths), [](const auto &argPair) { + const auto &tArgument = argPair.second; + return tArgument->get_arguments_length(); + }); + return *std::max_element(std::begin(argumentLengths), + std::end(argumentLengths)); + } + + std::string mProgramName; + std::vector mParentParsers; + std::vector> mPositionalArguments; + std::vector> mOptionalArguments; + std::map> mArgumentMap; + }; + +#define PARSE_ARGS(parser, argc, argv) \ + try { \ + parser.parse_args(argc, argv); \ + } catch (const std::runtime_error &err) { \ + std::cout << err.what() << std::endl; \ + parser.print_help(); \ + exit(0); \ + } + +} // namespace argparse diff --git a/lib/linenoise/LICENSE b/lib/linenoise/LICENSE new file mode 100644 index 0000000..18e8148 --- /dev/null +++ b/lib/linenoise/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2010-2014, Salvatore Sanfilippo +Copyright (c) 2010-2013, Pieter Noordhuis + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/linenoise/README.markdown b/lib/linenoise/README.markdown new file mode 100644 index 0000000..1afea2a --- /dev/null +++ b/lib/linenoise/README.markdown @@ -0,0 +1,247 @@ +# Linenoise + +A minimal, zero-config, BSD licensed, readline replacement used in Redis, +MongoDB, and Android. + +* Single and multi line editing mode with the usual key bindings implemented. +* History handling. +* Completion. +* Hints (suggestions at the right of the prompt as you type). +* About 1,100 lines of BSD license source code. +* Only uses a subset of VT100 escapes (ANSI.SYS compatible). + +## Can a line editing library be 20k lines of code? + +Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing? + +So what usually happens is either: + + * Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Real world example of this problem: Tclsh). + * Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance). + +The result is a pollution of binaries without line editing support. + +So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not. + +## Terminals, in 2010. + +Apparently almost every terminal you can happen to use today has some kind of support for basic VT100 escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it, and now can work even on ANSI.SYS compatible terminals, since no +VT220 specific sequences are used anymore. + +The library is currently about 1100 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software. + +## Tested with... + + * Linux text only console ($TERM = linux) + * Linux KDE terminal application ($TERM = xterm) + * Linux xterm ($TERM = xterm) + * Linux Buildroot ($TERM = vt100) + * Mac OS X iTerm ($TERM = xterm) + * Mac OS X default Terminal.app ($TERM = xterm) + * OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen) + * IBM AIX 6.1 + * FreeBSD xterm ($TERM = xterm) + * ANSI.SYS + * Emacs comint mode ($TERM = dumb) + +Please test it everywhere you can and report back! + +## Let's push this forward! + +Patches should be provided in the respect of Linenoise sensibility for small +easy to understand code. + +Send feedbacks to antirez at gmail + +# The API + +Linenoise is very easy to use, and reading the example shipped with the +library should get you up to speed ASAP. Here is a list of API calls +and how to use them. + + char *linenoise(const char *prompt); + +This is the main Linenoise call: it shows the user a prompt with line editing +and history capabilities. The prompt you specify is used as a prompt, that is, +it will be printed to the left of the cursor. The library returns a buffer +with the line composed by the user, or NULL on end of file or when there +is an out of memory condition. + +When a tty is detected (the user is actually typing into a terminal session) +the maximum editable line length is `LINENOISE_MAX_LINE`. When instead the +standard input is not a tty, which happens every time you redirect a file +to a program, or use it in an Unix pipeline, there are no limits to the +length of the line that can be returned. + +The returned line should be freed with the `free()` standard system call. +However sometimes it could happen that your program uses a different dynamic +allocation library, so you may also used `linenoiseFree` to make sure the +line is freed with the same allocator it was created. + +The canonical loop used by a program using Linenoise will be something like +this: + + while((line = linenoise("hello> ")) != NULL) { + printf("You wrote: %s\n", line); + linenoiseFree(line); /* Or just free(line) if you use libc malloc. */ + } + +## Single line VS multi line editing + +By default, Linenoise uses single line editing, that is, a single row on the +screen will be used, and as the user types more, the text will scroll towards +left to make room. This works if your program is one where the user is +unlikely to write a lot of text, otherwise multi line editing, where multiple +screens rows are used, can be a lot more comfortable. + +In order to enable multi line editing use the following API call: + + linenoiseSetMultiLine(1); + +You can disable it using `0` as argument. + +## History + +Linenoise supporst history, so that the user does not have to retype +again and again the same things, but can use the down and up arrows in order +to search and re-edit already inserted lines of text. + +The followings are the history API calls: + + int linenoiseHistoryAdd(const char *line); + int linenoiseHistorySetMaxLen(int len); + int linenoiseHistorySave(const char *filename); + int linenoiseHistoryLoad(const char *filename); + +Use `linenoiseHistoryAdd` every time you want to add a new element +to the top of the history (it will be the first the user will see when +using the up arrow). + +Note that for history to work, you have to set a length for the history +(which is zero by default, so history will be disabled if you don't set +a proper one). This is accomplished using the `linenoiseHistorySetMaxLen` +function. + +Linenoise has direct support for persisting the history into an history +file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do +just that. Both functions return -1 on error and 0 on success. + +## Mask mode + +Sometimes it is useful to allow the user to type passwords or other +secrets that should not be displayed. For such situations linenoise supports +a "mask mode" that will just replace the characters the user is typing +with `*` characters, like in the following example: + + $ ./linenoise_example + hello> get mykey + echo: 'get mykey' + hello> /mask + hello> ********* + +You can enable and disable mask mode using the following two functions: + + void linenoiseMaskModeEnable(void); + void linenoiseMaskModeDisable(void); + +## Completion + +Linenoise supports completion, which is the ability to complete the user +input when she or he presses the `` key. + +In order to use completion, you need to register a completion callback, which +is called every time the user presses ``. Your callback will return a +list of items that are completions for the current string. + +The following is an example of registering a completion callback: + + linenoiseSetCompletionCallback(completion); + +The completion must be a function returning `void` and getting as input +a `const char` pointer, which is the line the user has typed so far, and +a `linenoiseCompletions` object pointer, which is used as argument of +`linenoiseAddCompletion` in order to add completions inside the callback. +An example will make it more clear: + + void completion(const char *buf, linenoiseCompletions *lc) { + if (buf[0] == 'h') { + linenoiseAddCompletion(lc,"hello"); + linenoiseAddCompletion(lc,"hello there"); + } + } + +Basically in your completion callback, you inspect the input, and return +a list of items that are good completions by using `linenoiseAddCompletion`. + +If you want to test the completion feature, compile the example program +with `make`, run it, type `h` and press ``. + +## Hints + +Linenoise has a feature called *hints* which is very useful when you +use Linenoise in order to implement a REPL (Read Eval Print Loop) for +a program that accepts commands and arguments, but may also be useful in +other conditions. + +The feature shows, on the right of the cursor, as the user types, hints that +may be useful. The hints can be displayed using a different color compared +to the color the user is typing, and can also be bold. + +For example as the user starts to type `"git remote add"`, with hints it's +possible to show on the right of the prompt a string ` `. + +The feature works similarly to the history feature, using a callback. +To register the callback we use: + + linenoiseSetHintsCallback(hints); + +The callback itself is implemented like this: + + char *hints(const char *buf, int *color, int *bold) { + if (!strcasecmp(buf,"git remote add")) { + *color = 35; + *bold = 0; + return " "; + } + return NULL; + } + +The callback function returns the string that should be displayed or NULL +if no hint is available for the text the user currently typed. The returned +string will be trimmed as needed depending on the number of columns available +on the screen. + +It is possible to return a string allocated in dynamic way, by also registering +a function to deallocate the hint string once used: + + void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); + +The free hint callback will just receive the pointer and free the string +as needed (depending on how the hits callback allocated it). + +As you can see in the example above, a `color` (in xterm color terminal codes) +can be provided together with a `bold` attribute. If no color is set, the +current terminal foreground color is used. If no bold attribute is set, +non-bold text is printed. + +Color codes are: + + red = 31 + green = 32 + yellow = 33 + blue = 34 + magenta = 35 + cyan = 36 + white = 37; + +## Screen handling + +Sometimes you may want to clear the screen as a result of something the +user typed. You can do this by calling the following function: + + void linenoiseClearScreen(void); + +## Related projects + +* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language. +* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift. diff --git a/lib/linenoise/linenoise.c b/lib/linenoise/linenoise.c new file mode 100644 index 0000000..cfe51e7 --- /dev/null +++ b/lib/linenoise/linenoise.c @@ -0,0 +1,1225 @@ +/* linenoise.c -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * You can find the latest source code at: + * + * http://github.com/antirez/linenoise + * + * Does a number of crazy assumptions that happen to be true in 99.9999% of + * the 2010 UNIX computers around. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2016, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ------------------------------------------------------------------------ + * + * References: + * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html + * + * Todo list: + * - Filter bogus Ctrl+ combinations. + * - Win32 support + * + * Bloat: + * - History search like Ctrl+r in readline? + * + * List of escape sequences used by this program, we do everything just + * with three sequences. In order to be so cheap we may have some + * flickering effect with some slow terminal, but the lesser sequences + * the more compatible. + * + * EL (Erase Line) + * Sequence: ESC [ n K + * Effect: if n is 0 or missing, clear from cursor to end of line + * Effect: if n is 1, clear from beginning of line to cursor + * Effect: if n is 2, clear entire line + * + * CUF (CUrsor Forward) + * Sequence: ESC [ n C + * Effect: moves cursor forward n chars + * + * CUB (CUrsor Backward) + * Sequence: ESC [ n D + * Effect: moves cursor backward n chars + * + * The following is used to get the terminal width if getting + * the width with the TIOCGWINSZ ioctl fails + * + * DSR (Device Status Report) + * Sequence: ESC [ 6 n + * Effect: reports the current cusor position as ESC [ n ; m R + * where n is the row and m is the column + * + * When multi line mode is enabled, we also use an additional escape + * sequence. However multi line editing is disabled by default. + * + * CUU (Cursor Up) + * Sequence: ESC [ n A + * Effect: moves cursor up of n chars. + * + * CUD (Cursor Down) + * Sequence: ESC [ n B + * Effect: moves cursor down of n chars. + * + * When linenoiseClearScreen() is called, two additional escape sequences + * are used in order to clear the screen and position the cursor at home + * position. + * + * CUP (Cursor position) + * Sequence: ESC [ H + * Effect: moves the cursor to upper left corner + * + * ED (Erase display) + * Sequence: ESC [ 2 J + * Effect: clear the whole screen + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linenoise.h" + +#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 +#define LINENOISE_MAX_LINE 4096 +static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; +static linenoiseCompletionCallback *completionCallback = NULL; +static linenoiseHintsCallback *hintsCallback = NULL; +static linenoiseFreeHintsCallback *freeHintsCallback = NULL; + +static struct termios orig_termios; /* In order to restore at exit.*/ +static int maskmode = 0; /* Show "***" instead of input. For passwords. */ +static int rawmode = 0; /* For atexit() function to check if restore is needed*/ +static int mlmode = 0; /* Multi line mode. Default is single line. */ +static int atexit_registered = 0; /* Register atexit just 1 time. */ +static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; +static int history_len = 0; +static char **history = NULL; + +/* The linenoiseState structure represents the state during line editing. + * We pass this state to functions implementing specific editing + * functionalities. */ +struct linenoiseState { + int ifd; /* Terminal stdin file descriptor. */ + int ofd; /* Terminal stdout file descriptor. */ + char *buf; /* Edited line buffer. */ + size_t buflen; /* Edited line buffer size. */ + const char *prompt; /* Prompt to display. */ + size_t plen; /* Prompt length. */ + size_t pos; /* Current cursor position. */ + size_t oldpos; /* Previous refresh cursor position. */ + size_t len; /* Current edited line length. */ + size_t cols; /* Number of columns in terminal. */ + size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ + int history_index; /* The history index we are currently editing. */ +}; + +enum KEY_ACTION{ + KEY_NULL = 0, /* NULL */ + CTRL_A = 1, /* Ctrl+a */ + CTRL_B = 2, /* Ctrl-b */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_E = 5, /* Ctrl-e */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_K = 11, /* Ctrl+k */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_N = 14, /* Ctrl-n */ + CTRL_P = 16, /* Ctrl-p */ + CTRL_T = 20, /* Ctrl-t */ + CTRL_U = 21, /* Ctrl+u */ + CTRL_W = 23, /* Ctrl+w */ + ESC = 27, /* Escape */ + BACKSPACE = 127 /* Backspace */ +}; + +static void linenoiseAtExit(void); +int linenoiseHistoryAdd(const char *line); +static void refreshLine(struct linenoiseState *l); + +/* Debugging macro. */ +#if 0 +FILE *lndebug_fp = NULL; +#define lndebug(...) \ + do { \ + if (lndebug_fp == NULL) { \ + lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ + fprintf(lndebug_fp, \ + "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ + (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ + (int)l->maxrows,old_rows); \ + } \ + fprintf(lndebug_fp, ", " __VA_ARGS__); \ + fflush(lndebug_fp); \ + } while (0) +#else +#define lndebug(fmt, ...) +#endif + +/* ======================= Low level terminal handling ====================== */ + +/* Enable "mask mode". When it is enabled, instead of the input that + * the user is typing, the terminal will just display a corresponding + * number of asterisks, like "****". This is useful for passwords and other + * secrets that should not be displayed. */ +void linenoiseMaskModeEnable(void) { + maskmode = 1; +} + +/* Disable mask mode. */ +void linenoiseMaskModeDisable(void) { + maskmode = 0; +} + +/* Set if to use or not the multi line mode. */ +void linenoiseSetMultiLine(int ml) { + mlmode = ml; +} + +/* Return true if the terminal name is in the list of terminals we know are + * not able to understand basic escape sequences. */ +static int isUnsupportedTerm(void) { + char *term = getenv("TERM"); + int j; + + if (term == NULL) return 0; + for (j = 0; unsupported_term[j]; j++) + if (!strcasecmp(term,unsupported_term[j])) return 1; + return 0; +} + +/* Raw mode: 1960 magic shit. */ +static int enableRawMode(int fd) { + struct termios raw; + + if (!isatty(STDIN_FILENO)) goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + rawmode = 1; + return 0; + +fatal: + errno = ENOTTY; + return -1; +} + +static void disableRawMode(int fd) { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) + rawmode = 0; +} + +/* Use the ESC [6n escape sequence to query the horizontal cursor position + * and return it. On error -1 is returned, on success the position of the + * cursor. */ +static int getCursorPosition(int ifd, int ofd) { + char buf[32]; + int cols, rows; + unsigned int i = 0; + + /* Report cursor location */ + if (write(ofd, "\x1b[6n", 4) != 4) return -1; + + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (read(ifd,buf+i,1) != 1) break; + if (buf[i] == 'R') break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') return -1; + if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; + return cols; +} + +/* Try to get the number of columns in the current terminal, or assume 80 + * if it fails. */ +static int getColumns(int ifd, int ofd) { + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + /* ioctl() failed. Try to query the terminal itself. */ + int start, cols; + + /* Get the initial position so we can restore it later. */ + start = getCursorPosition(ifd,ofd); + if (start == -1) goto failed; + + /* Go to right margin and get position. */ + if (write(ofd,"\x1b[999C",6) != 6) goto failed; + cols = getCursorPosition(ifd,ofd); + if (cols == -1) goto failed; + + /* Restore position. */ + if (cols > start) { + char seq[32]; + snprintf(seq,32,"\x1b[%dD",cols-start); + if (write(ofd,seq,strlen(seq)) == -1) { + /* Can't recover... */ + } + } + return cols; + } else { + return ws.ws_col; + } + +failed: + return 80; +} + +/* Clear the screen. Used to handle ctrl+l */ +void linenoiseClearScreen(void) { + if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { + /* nothing to do, just to avoid warning. */ + } +} + +/* Beep, used for completion when there is nothing to complete or when all + * the choices were already shown. */ +static void linenoiseBeep(void) { + fprintf(stderr, "\x7"); + fflush(stderr); +} + +/* ============================== Completion ================================ */ + +/* Free a list of completion option populated by linenoiseAddCompletion(). */ +static void freeCompletions(linenoiseCompletions *lc) { + size_t i; + for (i = 0; i < lc->len; i++) + free(lc->cvec[i]); + if (lc->cvec != NULL) + free(lc->cvec); +} + +/* This is an helper function for linenoiseEdit() and is called when the + * user types the key in order to complete the string currently in the + * input. + * + * The state of the editing is encapsulated into the pointed linenoiseState + * structure as described in the structure definition. */ +static int completeLine(struct linenoiseState *ls) { + linenoiseCompletions lc = { 0, NULL }; + int nread, nwritten; + char c = 0; + + completionCallback(ls->buf,&lc); + if (lc.len == 0) { + linenoiseBeep(); + } else { + size_t stop = 0, i = 0; + + while(!stop) { + /* Show completion or original buffer */ + if (i < lc.len) { + struct linenoiseState saved = *ls; + + ls->len = ls->pos = strlen(lc.cvec[i]); + ls->buf = lc.cvec[i]; + refreshLine(ls); + ls->len = saved.len; + ls->pos = saved.pos; + ls->buf = saved.buf; + } else { + refreshLine(ls); + } + + nread = read(ls->ifd,&c,1); + if (nread <= 0) { + freeCompletions(&lc); + return -1; + } + + switch(c) { + case 9: /* tab */ + i = (i+1) % (lc.len+1); + if (i == lc.len) linenoiseBeep(); + break; + case 27: /* escape */ + /* Re-show original buffer */ + if (i < lc.len) refreshLine(ls); + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < lc.len) { + nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); + ls->len = ls->pos = nwritten; + } + stop = 1; + break; + } + } + } + + freeCompletions(&lc); + return c; /* Return last read character */ +} + +/* Register a callback function to be called for tab-completion. */ +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { + completionCallback = fn; +} + +/* Register a hits function to be called to show hits to the user at the + * right of the prompt. */ +void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { + hintsCallback = fn; +} + +/* Register a function to free the hints returned by the hints callback + * registered with linenoiseSetHintsCallback(). */ +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { + freeHintsCallback = fn; +} + +/* This function is used by the callback function registered by the user + * in order to add completion options given the input string when the + * user typed . See the example.c source code for a very easy to + * understand example. */ +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { + size_t len = strlen(str); + char *copy, **cvec; + + copy = malloc(len+1); + if (copy == NULL) return; + memcpy(copy,str,len+1); + cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + if (cvec == NULL) { + free(copy); + return; + } + lc->cvec = cvec; + lc->cvec[lc->len++] = copy; +} + +/* =========================== Line editing ================================= */ + +/* We define a very simple "append buffer" structure, that is an heap + * allocated string where we can append to. This is useful in order to + * write all the escape sequences in a buffer and flush them to the standard + * output in a single call, to avoid flickering effects. */ +struct abuf { + char *b; + int len; +}; + +static void abInit(struct abuf *ab) { + ab->b = NULL; + ab->len = 0; +} + +static void abAppend(struct abuf *ab, const char *s, int len) { + char *new = realloc(ab->b,ab->len+len); + + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; +} + +static void abFree(struct abuf *ab) { + free(ab->b); +} + +/* Helper of refreshSingleLine() and refreshMultiLine() to show hints + * to the right of the prompt. */ +void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { + char seq[64]; + if (hintsCallback && plen+l->len < l->cols) { + int color = -1, bold = 0; + char *hint = hintsCallback(l->buf,&color,&bold); + if (hint) { + int hintlen = strlen(hint); + int hintmaxlen = l->cols-(plen+l->len); + if (hintlen > hintmaxlen) hintlen = hintmaxlen; + if (bold == 1 && color == -1) color = 37; + if (color != -1 || bold != 0) + snprintf(seq,64,"\033[%d;%d;49m",bold,color); + else + seq[0] = '\0'; + abAppend(ab,seq,strlen(seq)); + abAppend(ab,hint,hintlen); + if (color != -1 || bold != 0) + abAppend(ab,"\033[0m",4); + /* Call the function to free the hint returned. */ + if (freeHintsCallback) freeHintsCallback(hint); + } + } +} + +/* Single line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static void refreshSingleLine(struct linenoiseState *l) { + char seq[64]; + size_t plen = strlen(l->prompt); + int fd = l->ofd; + char *buf = l->buf; + size_t len = l->len; + size_t pos = l->pos; + struct abuf ab; + + while((plen+pos) >= l->cols) { + buf++; + len--; + pos--; + } + while (plen+len > l->cols) { + len--; + } + + abInit(&ab); + /* Cursor to left edge */ + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + if (maskmode == 1) { + while (len--) abAppend(&ab,"*",1); + } else { + abAppend(&ab,buf,len); + } + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); + /* Erase to right */ + snprintf(seq,64,"\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); + /* Move cursor to original position. */ + snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); + abAppend(&ab,seq,strlen(seq)); + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); +} + +/* Multi line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static void refreshMultiLine(struct linenoiseState *l) { + char seq[64]; + int plen = strlen(l->prompt); + int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ + int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ + int rpos2; /* rpos after refresh. */ + int col; /* colum position, zero-based. */ + int old_rows = l->maxrows; + int fd = l->ofd, j; + struct abuf ab; + + /* Update maxrows if needed. */ + if (rows > (int)l->maxrows) l->maxrows = rows; + + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + abInit(&ab); + if (old_rows-rpos > 0) { + lndebug("go down %d", old_rows-rpos); + snprintf(seq,64,"\x1b[%dB", old_rows-rpos); + abAppend(&ab,seq,strlen(seq)); + } + + /* Now for every row clear it, go up. */ + for (j = 0; j < old_rows-1; j++) { + lndebug("clear+up"); + snprintf(seq,64,"\r\x1b[0K\x1b[1A"); + abAppend(&ab,seq,strlen(seq)); + } + + /* Clean the top line. */ + lndebug("clear"); + snprintf(seq,64,"\r\x1b[0K"); + abAppend(&ab,seq,strlen(seq)); + + /* Write the prompt and the current buffer content */ + abAppend(&ab,l->prompt,strlen(l->prompt)); + if (maskmode == 1) { + unsigned int i; + for (i = 0; i < l->len; i++) abAppend(&ab,"*",1); + } else { + abAppend(&ab,l->buf,l->len); + } + + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); + + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + if (l->pos && + l->pos == l->len && + (l->pos+plen) % l->cols == 0) + { + lndebug(""); + abAppend(&ab,"\n",1); + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + rows++; + if (rows > (int)l->maxrows) l->maxrows = rows; + } + + /* Move cursor to right position. */ + rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ + lndebug("rpos2 %d", rpos2); + + /* Go up till we reach the expected positon. */ + if (rows-rpos2 > 0) { + lndebug("go-up %d", rows-rpos2); + snprintf(seq,64,"\x1b[%dA", rows-rpos2); + abAppend(&ab,seq,strlen(seq)); + } + + /* Set column. */ + col = (plen+(int)l->pos) % (int)l->cols; + lndebug("set col %d", 1+col); + if (col) + snprintf(seq,64,"\r\x1b[%dC", col); + else + snprintf(seq,64,"\r"); + abAppend(&ab,seq,strlen(seq)); + + lndebug("\n"); + l->oldpos = l->pos; + + if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ + abFree(&ab); +} + +/* Calls the two low level functions refreshSingleLine() or + * refreshMultiLine() according to the selected mode. */ +static void refreshLine(struct linenoiseState *l) { + if (mlmode) + refreshMultiLine(l); + else + refreshSingleLine(l); +} + +/* Insert the character 'c' at cursor current position. + * + * On error writing to the terminal -1 is returned, otherwise 0. */ +int linenoiseEditInsert(struct linenoiseState *l, char c) { + if (l->len < l->buflen) { + if (l->len == l->pos) { + l->buf[l->pos] = c; + l->pos++; + l->len++; + l->buf[l->len] = '\0'; + if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { + /* Avoid a full update of the line in the + * trivial case. */ + char d = (maskmode==1) ? '*' : c; + if (write(l->ofd,&d,1) == -1) return -1; + } else { + refreshLine(l); + } + } else { + memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); + l->buf[l->pos] = c; + l->len++; + l->pos++; + l->buf[l->len] = '\0'; + refreshLine(l); + } + } + return 0; +} + +/* Move cursor on the left. */ +void linenoiseEditMoveLeft(struct linenoiseState *l) { + if (l->pos > 0) { + l->pos--; + refreshLine(l); + } +} + +/* Move cursor on the right. */ +void linenoiseEditMoveRight(struct linenoiseState *l) { + if (l->pos != l->len) { + l->pos++; + refreshLine(l); + } +} + +/* Move cursor to the start of the line. */ +void linenoiseEditMoveHome(struct linenoiseState *l) { + if (l->pos != 0) { + l->pos = 0; + refreshLine(l); + } +} + +/* Move cursor to the end of the line. */ +void linenoiseEditMoveEnd(struct linenoiseState *l) { + if (l->pos != l->len) { + l->pos = l->len; + refreshLine(l); + } +} + +/* Substitute the currently edited line with the next or previous history + * entry as specified by 'dir'. */ +#define LINENOISE_HISTORY_NEXT 0 +#define LINENOISE_HISTORY_PREV 1 +void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { + if (history_len > 1) { + /* Update the current history entry before to + * overwrite it with the next one. */ + free(history[history_len - 1 - l->history_index]); + history[history_len - 1 - l->history_index] = strdup(l->buf); + /* Show the new entry */ + l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; + if (l->history_index < 0) { + l->history_index = 0; + return; + } else if (l->history_index >= history_len) { + l->history_index = history_len-1; + return; + } + strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); + l->buf[l->buflen-1] = '\0'; + l->len = l->pos = strlen(l->buf); + refreshLine(l); + } +} + +/* Delete the character at the right of the cursor without altering the cursor + * position. Basically this is what happens with the "Delete" keyboard key. */ +void linenoiseEditDelete(struct linenoiseState *l) { + if (l->len > 0 && l->pos < l->len) { + memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); + l->len--; + l->buf[l->len] = '\0'; + refreshLine(l); + } +} + +/* Backspace implementation. */ +void linenoiseEditBackspace(struct linenoiseState *l) { + if (l->pos > 0 && l->len > 0) { + memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); + l->pos--; + l->len--; + l->buf[l->len] = '\0'; + refreshLine(l); + } +} + +/* Delete the previosu word, maintaining the cursor at the start of the + * current word. */ +void linenoiseEditDeletePrevWord(struct linenoiseState *l) { + size_t old_pos = l->pos; + size_t diff; + + while (l->pos > 0 && l->buf[l->pos-1] == ' ') + l->pos--; + while (l->pos > 0 && l->buf[l->pos-1] != ' ') + l->pos--; + diff = old_pos - l->pos; + memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); + l->len -= diff; + refreshLine(l); +} + +/* This function is the core of the line editing capability of linenoise. + * It expects 'fd' to be already in "raw mode" so that every key pressed + * will be returned ASAP to read(). + * + * The resulting string is put into 'buf' when the user type enter, or + * when ctrl+d is typed. + * + * The function returns the length of the current buffer. */ +static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) +{ + struct linenoiseState l; + + /* Populate the linenoise state that we pass to functions implementing + * specific editing functionalities. */ + l.ifd = stdin_fd; + l.ofd = stdout_fd; + l.buf = buf; + l.buflen = buflen; + l.prompt = prompt; + l.plen = strlen(prompt); + l.oldpos = l.pos = 0; + l.len = 0; + l.cols = getColumns(stdin_fd, stdout_fd); + l.maxrows = 0; + l.history_index = 0; + + /* Buffer starts empty. */ + l.buf[0] = '\0'; + l.buflen--; /* Make sure there is always space for the nulterm */ + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); + + if (write(l.ofd,prompt,l.plen) == -1) return -1; + while(1) { + char c; + int nread; + char seq[3]; + + nread = read(l.ifd,&c,1); + if (nread <= 0) return l.len; + + /* Only autocomplete when the callback is set. It returns < 0 when + * there was an error reading from fd. Otherwise it will return the + * character that should be handled next. */ + if (c == 9 && completionCallback != NULL) { + c = completeLine(&l); + /* Return on errors */ + if (c < 0) return l.len; + /* Read next character when 0 */ + if (c == 0) continue; + } + + switch(c) { + case ENTER: /* enter */ + history_len--; + free(history[history_len]); + if (mlmode) linenoiseEditMoveEnd(&l); + if (hintsCallback) { + /* Force a refresh without hints to leave the previous + * line as the user typed it after a newline. */ + linenoiseHintsCallback *hc = hintsCallback; + hintsCallback = NULL; + refreshLine(&l); + hintsCallback = hc; + } + return (int)l.len; + case CTRL_C: /* ctrl-c */ + errno = EAGAIN; + return -1; + case BACKSPACE: /* backspace */ + case 8: /* ctrl-h */ + linenoiseEditBackspace(&l); + break; + case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the + line is empty, act as end-of-file. */ + if (l.len > 0) { + linenoiseEditDelete(&l); + } else { + history_len--; + free(history[history_len]); + return -1; + } + break; + case CTRL_T: /* ctrl-t, swaps current character with previous. */ + if (l.pos > 0 && l.pos < l.len) { + int aux = buf[l.pos-1]; + buf[l.pos-1] = buf[l.pos]; + buf[l.pos] = aux; + if (l.pos != l.len-1) l.pos++; + refreshLine(&l); + } + break; + case CTRL_B: /* ctrl-b */ + linenoiseEditMoveLeft(&l); + break; + case CTRL_F: /* ctrl-f */ + linenoiseEditMoveRight(&l); + break; + case CTRL_P: /* ctrl-p */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case CTRL_N: /* ctrl-n */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case ESC: /* escape sequence */ + /* Read the next two bytes representing the escape sequence. + * Use two calls to handle slow terminals returning the two + * chars at different times. */ + if (read(l.ifd,seq,1) == -1) break; + if (read(l.ifd,seq+1,1) == -1) break; + + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (read(l.ifd,seq+2,1) == -1) break; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': /* Delete key. */ + linenoiseEditDelete(&l); + break; + } + } + } else { + switch(seq[1]) { + case 'A': /* Up */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); + break; + case 'B': /* Down */ + linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); + break; + case 'C': /* Right */ + linenoiseEditMoveRight(&l); + break; + case 'D': /* Left */ + linenoiseEditMoveLeft(&l); + break; + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + } + + /* ESC O sequences. */ + else if (seq[0] == 'O') { + switch(seq[1]) { + case 'H': /* Home */ + linenoiseEditMoveHome(&l); + break; + case 'F': /* End*/ + linenoiseEditMoveEnd(&l); + break; + } + } + break; + default: + if (linenoiseEditInsert(&l,c)) return -1; + break; + case CTRL_U: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + l.pos = l.len = 0; + refreshLine(&l); + break; + case CTRL_K: /* Ctrl+k, delete from current to end of line. */ + buf[l.pos] = '\0'; + l.len = l.pos; + refreshLine(&l); + break; + case CTRL_A: /* Ctrl+a, go to the start of the line */ + linenoiseEditMoveHome(&l); + break; + case CTRL_E: /* ctrl+e, go to the end of the line */ + linenoiseEditMoveEnd(&l); + break; + case CTRL_L: /* ctrl+l, clear screen */ + linenoiseClearScreen(); + refreshLine(&l); + break; + case CTRL_W: /* ctrl+w, delete previous word */ + linenoiseEditDeletePrevWord(&l); + break; + } + } + return l.len; +} + +/* This special mode is used by linenoise in order to print scan codes + * on screen for debugging / development purposes. It is implemented + * by the linenoise_example program using the --keycodes option. */ +void linenoisePrintKeyCodes(void) { + char quit[4]; + + printf("Linenoise key codes debugging mode.\n" + "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); + if (enableRawMode(STDIN_FILENO) == -1) return; + memset(quit,' ',4); + while(1) { + char c; + int nread; + + nread = read(STDIN_FILENO,&c,1); + if (nread <= 0) continue; + memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ + quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ + if (memcmp(quit,"quit",sizeof(quit)) == 0) break; + + printf("'%c' %02x (%d) (type quit to exit)\n", + isprint(c) ? c : '?', (int)c, (int)c); + printf("\r"); /* Go left edge manually, we are in raw mode. */ + fflush(stdout); + } + disableRawMode(STDIN_FILENO); +} + +/* This function calls the line editing function linenoiseEdit() using + * the STDIN file descriptor set in raw mode. */ +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { + int count; + + if (buflen == 0) { + errno = EINVAL; + return -1; + } + + if (enableRawMode(STDIN_FILENO) == -1) return -1; + count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); + disableRawMode(STDIN_FILENO); + printf("\n"); + return count; +} + +/* This function is called when linenoise() is called with the standard + * input file descriptor not attached to a TTY. So for example when the + * program using linenoise is called in pipe or with a file redirected + * to its standard input. In this case, we want to be able to return the + * line regardless of its length (by default we are limited to 4k). */ +static char *linenoiseNoTTY(void) { + char *line = NULL; + size_t len = 0, maxlen = 0; + + while(1) { + if (len == maxlen) { + if (maxlen == 0) maxlen = 16; + maxlen *= 2; + char *oldval = line; + line = realloc(line,maxlen); + if (line == NULL) { + if (oldval) free(oldval); + return NULL; + } + } + int c = fgetc(stdin); + if (c == EOF || c == '\n') { + if (c == EOF && len == 0) { + free(line); + return NULL; + } else { + line[len] = '\0'; + return line; + } + } else { + line[len] = c; + len++; + } + } +} + +/* The high level function that is the main API of the linenoise library. + * This function checks if the terminal has basic capabilities, just checking + * for a blacklist of stupid terminals, and later either calls the line + * editing function or uses dummy fgets() so that you will be able to type + * something even in the most desperate of the conditions. */ +char *linenoise(const char *prompt) { + char buf[LINENOISE_MAX_LINE]; + int count; + + if (!isatty(STDIN_FILENO)) { + /* Not a tty: read from file / pipe. In this mode we don't want any + * limit to the line size, so we call a function to handle that. */ + return linenoiseNoTTY(); + } else if (isUnsupportedTerm()) { + size_t len; + + printf("%s",prompt); + fflush(stdout); + if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; + len = strlen(buf); + while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + len--; + buf[len] = '\0'; + } + return strdup(buf); + } else { + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); + if (count == -1) return NULL; + return strdup(buf); + } +} + +/* This is just a wrapper the user may want to call in order to make sure + * the linenoise returned buffer is freed with the same allocator it was + * created with. Useful when the main program is using an alternative + * allocator. */ +void linenoiseFree(void *ptr) { + free(ptr); +} + +/* ================================ History ================================= */ + +/* Free the history, but does not reset it. Only used when we have to + * exit() to avoid memory leaks are reported by valgrind & co. */ +static void freeHistory(void) { + if (history) { + int j; + + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + } +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static void linenoiseAtExit(void) { + disableRawMode(STDIN_FILENO); + freeHistory(); +} + +/* This is the API call to add a new entry in the linenoise history. + * It uses a fixed array of char pointers that are shifted (memmoved) + * when the history max length is reached in order to remove the older + * entry and make room for the new one, so it is not exactly suitable for huge + * histories, but will work well for a few hundred of entries. + * + * Using a circular buffer is smarter, but a bit more complex to handle. */ +int linenoiseHistoryAdd(const char *line) { + char *linecopy; + + if (history_max_len == 0) return 0; + + /* Initialization on first call. */ + if (history == NULL) { + history = malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } + + /* Don't add duplicated lines. */ + if (history_len && !strcmp(history[history_len-1], line)) return 0; + + /* Add an heap allocated copy of the line in the history. + * If we reached the max length, remove the older line. */ + linecopy = strdup(line); + if (!linecopy) return 0; + if (history_len == history_max_len) { + free(history[0]); + memmove(history,history+1,sizeof(char*)*(history_max_len-1)); + history_len--; + } + history[history_len] = linecopy; + history_len++; + return 1; +} + +/* Set the maximum length for the history. This function can be called even + * if there is already some history, the function will make sure to retain + * just the latest 'len' elements if the new history length value is smaller + * than the amount of items already inside the history. */ +int linenoiseHistorySetMaxLen(int len) { + char **new; + + if (len < 1) return 0; + if (history) { + int tocopy = history_len; + + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; + + /* If we can't copy everything, free the elements we'll not use. */ + if (len < tocopy) { + int j; + + for (j = 0; j < tocopy-len; j++) free(history[j]); + tocopy = len; + } + memset(new,0,sizeof(char*)*len); + memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = new; + } + history_max_len = len; + if (history_len > history_max_len) + history_len = history_max_len; + return 1; +} + +/* Save the history in the specified file. On success 0 is returned + * otherwise -1 is returned. */ +int linenoiseHistorySave(const char *filename) { + mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + FILE *fp; + int j; + + fp = fopen(filename,"w"); + umask(old_umask); + if (fp == NULL) return -1; + chmod(filename,S_IRUSR|S_IWUSR); + for (j = 0; j < history_len; j++) + fprintf(fp,"%s\n",history[j]); + fclose(fp); + return 0; +} + +/* Load the history from the specified file. If the file does not exist + * zero is returned and no operation is performed. + * + * If the file exists and the operation succeeded 0 is returned, otherwise + * on error -1 is returned. */ +int linenoiseHistoryLoad(const char *filename) { + FILE *fp = fopen(filename,"r"); + char buf[LINENOISE_MAX_LINE]; + + if (fp == NULL) return -1; + + while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { + char *p; + + p = strchr(buf,'\r'); + if (!p) p = strchr(buf,'\n'); + if (p) *p = '\0'; + linenoiseHistoryAdd(buf); + } + fclose(fp); + return 0; +} diff --git a/lib/linenoise/linenoise.h b/lib/linenoise/linenoise.h new file mode 100644 index 0000000..dab0c20 --- /dev/null +++ b/lib/linenoise/linenoise.h @@ -0,0 +1,77 @@ +/* linenoise.h -- VERSION 1.0 + * + * Guerrilla line editing library against the idea that a line editing lib + * needs to be 20,000 lines of C code. + * + * See linenoise.c for more information. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010-2014, Salvatore Sanfilippo + * Copyright (c) 2010-2013, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LINENOISE_H +#define __LINENOISE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct linenoiseCompletions + { + size_t len; + char **cvec; + } linenoiseCompletions; + + typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); + typedef char *(linenoiseHintsCallback)(const char *, int *color, int *bold); + typedef void(linenoiseFreeHintsCallback)(void *); + void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); + void linenoiseSetHintsCallback(linenoiseHintsCallback *); + void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); + void linenoiseAddCompletion(linenoiseCompletions *, const char *); + + char *linenoise(const char *prompt); + void linenoiseFree(void *ptr); + int linenoiseHistoryAdd(const char *line); + int linenoiseHistorySetMaxLen(int len); + int linenoiseHistorySave(const char *filename); + int linenoiseHistoryLoad(const char *filename); + void linenoiseClearScreen(void); + void linenoiseSetMultiLine(int ml); + void linenoisePrintKeyCodes(void); + void linenoiseMaskModeEnable(void); + void linenoiseMaskModeDisable(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LINENOISE_H */ diff --git a/lib/nlohmann/json.hpp b/lib/nlohmann/json.hpp new file mode 100644 index 0000000..3a4e2a8 --- /dev/null +++ b/lib/nlohmann/json.hpp @@ -0,0 +1,24867 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.9.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +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. +*/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 9 +#define NLOHMANN_JSON_VERSION_PATCH 1 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + +#include + +// #include + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + +#include // exception +#include // runtime_error +#include // to_string + +// #include + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + +#include // pair +// #include +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13) +#if defined(JSON_HEDLEY_VERSION) +#undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 13 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) +#undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) +#undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) +#undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a, b) a##b + +#if defined(JSON_HEDLEY_CONCAT) +#undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a, b) JSON_HEDLEY_CONCAT_EX(a, b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) +#undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a, b, c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) +#undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a, b, c) JSON_HEDLEY_CONCAT3_EX(a, b, c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) +#undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major, minor, revision) (((major)*1000000) + ((minor)*1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) +#undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) +#define JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) +#undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +#define JSON_HEDLEY_MSVC_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, \ + (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) +#define JSON_HEDLEY_MSVC_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) +#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) +#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) +#undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +#define JSON_HEDLEY_INTEL_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) +#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) +#define JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) +#undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) +#define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) +#define JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) +#undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) +#define JSON_HEDLEY_SUNPRO_VERSION \ + JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \ + (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) +#define JSON_HEDLEY_SUNPRO_VERSION \ + JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C)&0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) +#define JSON_HEDLEY_SUNPRO_VERSION \ + JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \ + (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \ + (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) +#define JSON_HEDLEY_SUNPRO_VERSION \ + JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC)&0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) +#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) +#define JSON_HEDLEY_EMSCRIPTEN_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) +#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) +#undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) +#define JSON_HEDLEY_ARM_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, \ + (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) +#define JSON_HEDLEY_ARM_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, \ + (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) +#define JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) +#undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) +#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) +#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) +#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) +#define JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) +#undef JSON_HEDLEY_TI_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && \ + (defined(__TMS470__) || defined(__TI_ARM__) || defined(__MSP430__) || defined(__TMS320C2000__)) +#if (__TI_COMPILER_VERSION__ >= 16000000) +#define JSON_HEDLEY_TI_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) +#undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) +#define JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) +#undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) +#define JSON_HEDLEY_TI_CL2000_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) +#define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) +#undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) +#define JSON_HEDLEY_TI_CL430_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) +#define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) +#define JSON_HEDLEY_TI_ARMCL_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) +#define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) +#undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) +#define JSON_HEDLEY_TI_CL6X_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) +#define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) +#undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) +#define JSON_HEDLEY_TI_CL7X_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) +#define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) +#define JSON_HEDLEY_TI_CLPRU_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) +#define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) +#undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) +#if defined(_RELEASE_PATCHLEVEL) +#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) +#else +#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) +#endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) +#define JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) +#undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) +#if __VER__ > 1000 +#define JSON_HEDLEY_IAR_VERSION \ + JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) +#else +#define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) +#endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) +#define JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) +#undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) +#define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) +#define JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) +#undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) +#define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) +#define JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) +#undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) +#define JSON_HEDLEY_COMPCERT_VERSION \ + JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, \ + __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) +#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) +#undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) +#define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) +#define JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) +#undef JSON_HEDLEY_GCC_VERSION +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) && !defined(__clang__) && !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_ARM_VERSION) && !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && !defined(__COMPCERT__) +#define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) +#define JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) \ + (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +#define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) +#else +#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ + JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) +#else +#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0)) +#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else +#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) +#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) (0) +#elif !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19, 20, 0)) +#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else +#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) __has_cpp_attribute(attribute) +#else +#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) __has_cpp_attribute(attribute) +#else +#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) +#undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +#define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) +#else +#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) +#else +#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) +#undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) +#define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else +#define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) +#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) +#else +#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) +#define JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) +#else +#define JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) +#undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else +#define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) +#else +#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) \ + JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) +#else +#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else +#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) __has_declspec_attribute(attribute) +#else +#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) __has_declspec_attribute(attribute) +#else +#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) +#undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) +#define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else +#define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) +#define JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) +#else +#define JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) +#undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) +#define JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) +#else +#define JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +#if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr JSON_HEDLEY_DIAGNOSTIC_POP +#else +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") xpr JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) +#undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +#define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_CONST_CAST(T, expr) \ + (__extension__({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL((T)(expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +#define JSON_HEDLEY_CONST_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) +#undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) +#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else +#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) +#undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) +#define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else +#define JSON_HEDLEY_STATIC_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) +#undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +#if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +#define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"")((T)(expr)) JSON_HEDLEY_DIAGNOSTIC_POP +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 3, 0) +#define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") JSON_HEDLEY_DIAGNOSTIC_POP #else +#define JSON_HEDLEY_CPP_CAST(T, expr) ((T)(expr)) +#endif +#else +#define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 7, 0) || JSON_HEDLEY_TI_CL430_VERSION_CHECK(2, 0, 1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 1, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 0, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) || JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8, 0, 0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) && defined(__C99_PRAGMA_OPERATOR)) +#define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else +#define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) +#define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 4, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 1, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) +#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else +#define JSON_HEDLEY_DIAGNOSTIC_PUSH +#define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable : 4996)) +#elif JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && !defined(__cplusplus) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && defined(__cplusplus) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable : 4068)) +#elif JSON_HEDLEY_TI_VERSION_CHECK(16, 9, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 3, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES \ + _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable : 5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif JSON_HEDLEY_TI_VERSION_CHECK(18, 1, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 3, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#else +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else +#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) +#undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) +#undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " #since)) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) +#define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18, 1, 0) || JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18, 1, 0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 3, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 3, 0) +#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ + __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || JSON_HEDLEY_PELLES_VERSION_CHECK(6, 50, 0) +#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else +#define JSON_HEDLEY_DEPRECATED(since) +#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) +#undef JSON_HEDLEY_UNAVAILABLE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(warning) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else +#define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) +#define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) +#define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ +#define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ +#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else +#define JSON_HEDLEY_WARN_UNUSED_RESULT +#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) +#undef JSON_HEDLEY_SENTINEL +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) +#define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else +#define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) +#undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_NO_RETURN __noreturn +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +#define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 2, 0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) +#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) +#define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) +#define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) +#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else +#define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) +#undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) +#define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else +#define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) +#undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) +#undef JSON_HEDLEY_ASSUME +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) +#define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) +#if defined(__cplusplus) +#define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) +#else +#define JSON_HEDLEY_ASSUME(expr) _nassert(expr) +#endif +#endif +#if (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(18, 10, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 5) +#define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) +#define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) +#if defined(JSON_HEDLEY_UNREACHABLE) +#define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) +#else +#define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) +#endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) +#if JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) +#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) +#else +#define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() +#endif +#else +#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) +#define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") +#pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros", 4, 0, 0) +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wvariadic-macros" +#elif defined(JSON_HEDLEY_GCC_VERSION) +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) +#undef JSON_HEDLEY_NON_NULL +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) +#define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +#define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) +#undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) && !defined(__USE_MINGW_ANSI_STDIO) +#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) && defined(__USE_MINGW_ANSI_STDIO) +#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif JSON_HEDLEY_HAS_ATTRIBUTE(format) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6, 0, 0) +#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) __declspec(vaformat(printf, string_idx, first_to_check)) +#else +#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) +#undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) +#if __cplusplus >= 201103L +#define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) +#endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) +#define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) +#undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) +#undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) +#undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) +#undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) +#define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || JSON_HEDLEY_GCC_VERSION_CHECK(9, 0, 0) +#define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability((expr), (value), (probability)) +#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, (probability)) +#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, (probability)) +#define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +#define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 7, 0) || JSON_HEDLEY_TI_CL430_VERSION_CHECK(3, 1, 0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 1, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 27) || JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) +#define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) \ + : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) \ + : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +#define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +#define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +#define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +#define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +#define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) +#define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) +#undef JSON_HEDLEY_MALLOC +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +#define JSON_HEDLEY_MALLOC __declspec(restrict) +#else +#define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) +#undef JSON_HEDLEY_PURE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(pure) || JSON_HEDLEY_GCC_VERSION_CHECK(2, 96, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(2, 0, 1) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0)) +#define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +#define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) +#undef JSON_HEDLEY_CONST +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(const) || JSON_HEDLEY_GCC_VERSION_CHECK(2, 5, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else +#define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) +#undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) +#define JSON_HEDLEY_RESTRICT restrict +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 1, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus)) || JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || \ + defined(__clang__) +#define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus) +#define JSON_HEDLEY_RESTRICT _Restrict +#else +#define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) +#undef JSON_HEDLEY_INLINE +#endif +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) +#define JSON_HEDLEY_INLINE inline +#elif defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_ARM_VERSION_CHECK(6, 2, 0) +#define JSON_HEDLEY_INLINE __inline__ +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 1, 0) || JSON_HEDLEY_TI_CL430_VERSION_CHECK(3, 1, 0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_INLINE __inline +#else +#define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) +#undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) +#define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0)) +#define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +#define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) +#undef JSON_HEDLEY_NEVER_INLINE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) +#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10, 2, 0) +#define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) +#define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) +#define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) +#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else +#define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) +#undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) +#undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) +#undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +#define JSON_HEDLEY_PRIVATE +#define JSON_HEDLEY_PUBLIC __declspec(dllexport) +#define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +#if JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ + (defined(__TI_EABI__) && ((JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0))) +#define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +#define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +#else +#define JSON_HEDLEY_PRIVATE +#define JSON_HEDLEY_PUBLIC +#endif +#define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) +#undef JSON_HEDLEY_NO_THROW +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 1, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) +#define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else +#define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) +#undef JSON_HEDLEY_FALL_THROUGH +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || JSON_HEDLEY_GCC_VERSION_CHECK(7, 0, 0) +#define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang, fallthrough) +#define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) +#define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ +#define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else +#define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) +#undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) +#define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ +#define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else +#define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) +#undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_TINYC_VERSION) +#define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else +#define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) +#undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) && !defined(__cplusplus)) || JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) +#define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 24) +#if defined(__INTPTR_TYPE__) +#define JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + __builtin_types_compatible_p(__typeof__((1 ? (void *)((__INTPTR_TYPE__)((expr)*0)) : (int *)0)), int *) +#else +#include +#define JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + __builtin_types_compatible_p(__typeof__((1 ? (void *)((intptr_t)((expr)*0)) : (int *)0)), int *) +#endif +#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17, 0, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5, 3, 0) +#if defined(__INTPTR_TYPE__) +#define JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + _Generic((1 ? (void *)((__INTPTR_TYPE__)((expr)*0)) : (int *)0), int * : 1, void * : 0) +#else +#include +#define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void *)((intptr_t)*0) : (int *)0), int * : 1, void * : 0) +#endif +#elif defined(JSON_HEDLEY_GCC_VERSION) || defined(JSON_HEDLEY_INTEL_VERSION) || defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || JSON_HEDLEY_TI_CL430_VERSION_CHECK(18, 12, 0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || defined(JSON_HEDLEY_TI_CLPRU_VERSION) || defined(__clang__) +#define JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + (sizeof(void) != sizeof(*(1 ? ((void *)((expr)*0L)) : ((struct { char v[sizeof(void) * 2]; } *)1)))) +#endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) +#if !defined(JSON_HEDLEY_IS_CONSTANT) +#define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) +#endif +#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else +#if !defined(JSON_HEDLEY_IS_CONSTANT) +#define JSON_HEDLEY_IS_CONSTANT(expr) (0) +#endif +#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) +#undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) +#undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) +#undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) +#define JSON_HEDLEY_BEGIN_C_DECLS \ + extern "C" \ + { +#define JSON_HEDLEY_END_C_DECLS } +#define JSON_HEDLEY_C_DECL extern "C" +#else +#define JSON_HEDLEY_BEGIN_C_DECLS +#define JSON_HEDLEY_END_C_DECLS +#define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) +#undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if !defined(__cplusplus) && \ + ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6, 0, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || defined(_Static_assert)) +#define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif (defined(__cplusplus) && (__cplusplus >= 201103L)) || JSON_HEDLEY_MSVC_VERSION_CHECK(16, 0, 0) +#define JSON_HEDLEY_STATIC_ASSERT(expr, message) \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +#define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) +#undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) +#if __cplusplus >= 201103L +#define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) +#elif defined(NULL) +#define JSON_HEDLEY_NULL NULL +#else +#define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void *, 0) +#endif +#elif defined(NULL) +#define JSON_HEDLEY_NULL NULL +#else +#define JSON_HEDLEY_NULL ((void *)0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) +#undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 4, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) +#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 0, 0) +#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +#define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) +#undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 8, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) +#undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) +#undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +#if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +#define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#else +#define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +#endif +#else +#define JSON_HEDLEY_REQUIRE(expr) +#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) +#undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) +#define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) +#undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19, 0, 0) +#define JSON_HEDLEY_FLAGS_CAST(T, expr) \ + (__extension__({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)")((T)(expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +#define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) +#undef JSON_HEDLEY_EMPTY_BASES +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(19, 0, 23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20, 0, 0) +#define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else +#define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) +#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) (0) +#else +#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) +#if defined(__clang__) +#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 +#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 +#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define JSON_HAS_CPP_20 +#define JSON_HAS_CPP_17 +#define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#define JSON_HAS_CPP_17 +#define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) +#define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) +#define JSON_THROW(exception) throw exception +#define JSON_TRY try +#define JSON_CATCH(exception) catch (exception) +#define JSON_INTERNAL_CATCH(exception) catch (exception) +#else +#include +#define JSON_THROW(exception) std::abort() +#define JSON_TRY if (true) +#define JSON_CATCH(exception) if (false) +#define JSON_INTERNAL_CATCH(exception) if (false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) +#undef JSON_THROW +#define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) +#undef JSON_TRY +#define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) +#undef JSON_CATCH +#define JSON_CATCH JSON_CATCH_USER +#undef JSON_INTERNAL_CATCH +#define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) +#undef JSON_INTERNAL_CATCH +#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow to override assert +#if !defined(JSON_ASSERT) +#include // assert +#define JSON_ASSERT(x) assert(x) +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template inline void to_json(BasicJsonType &j, const ENUM_TYPE &e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = \ + std::find_if(std::begin(m), std::end(m), [e](const std::pair &ej_pair) -> bool { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template inline void from_json(const BasicJsonType &j, ENUM_TYPE &e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if( \ + std::begin(m), std::end(m), \ + [&j](const std::pair &ej_pair) -> bool { return ej_pair.second == j; }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template