From 8362c073f34f25ef2fbe800780f4b8790b3fffa8 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 4 Jun 2024 16:40:48 -0400 Subject: [PATCH] Everywhere: Remove LibSQL, SQLServer, and the sql REPL :^) It is now entirely unused and replaced by sqlite3. --- .github/CODEOWNERS | 3 - AK/Debug.h.in | 8 - Ladybird/CMakeLists.txt | 7 +- Ladybird/HelperProcess.cpp | 12 - Ladybird/HelperProcess.h | 2 - Ladybird/SQLServer/CMakeLists.txt | 14 - Ladybird/SQLServer/main.cpp | 60 - Ladybird/WebContent/CMakeLists.txt | 2 +- Ladybird/WebWorker/CMakeLists.txt | 2 +- Meta/CMake/all_the_debug_macros.cmake | 2 - Meta/Lagom/CMakeLists.txt | 3 - Meta/Lagom/Fuzzers/FuzzSQLParser.cpp | 17 - Meta/Lagom/Fuzzers/fuzzers.cmake | 2 - Meta/ShellCompletions/zsh/_serenity | 2 +- Meta/gn/secondary/AK/BUILD.gn | 2 - Meta/gn/secondary/Ladybird/BUILD.gn | 7 - Meta/gn/secondary/Ladybird/SQLServer/BUILD.gn | 21 - .../gn/secondary/Ladybird/WebContent/BUILD.gn | 1 - Meta/gn/secondary/Ladybird/WebWorker/BUILD.gn | 1 - .../Userland/Libraries/LibSQL/BUILD.gn | 73 - .../Userland/Libraries/LibWebView/BUILD.gn | 1 - README.md | 2 +- Tests/CMakeLists.txt | 1 - Tests/LibSQL/CMakeLists.txt | 13 - Tests/LibSQL/TestSqlBtreeIndex.cpp | 319 ---- Tests/LibSQL/TestSqlDatabase.cpp | 237 --- Tests/LibSQL/TestSqlExpressionParser.cpp | 604 -------- Tests/LibSQL/TestSqlHeap.cpp | 194 --- Tests/LibSQL/TestSqlStatementExecution.cpp | 1063 -------------- Tests/LibSQL/TestSqlStatementParser.cpp | 785 ---------- Tests/LibSQL/TestSqlValueAndTuple.cpp | 1307 ----------------- Tests/LibTest/CMakeLists.txt | 2 +- Userland/Libraries/CMakeLists.txt | 1 - Userland/Libraries/LibSQL/AST/AST.h | 1110 -------------- .../Libraries/LibSQL/AST/CreateSchema.cpp | 25 - Userland/Libraries/LibSQL/AST/CreateTable.cpp | 42 - Userland/Libraries/LibSQL/AST/Delete.cpp | 40 - Userland/Libraries/LibSQL/AST/Describe.cpp | 38 - Userland/Libraries/LibSQL/AST/Expression.cpp | 244 --- Userland/Libraries/LibSQL/AST/Insert.cpp | 61 - Userland/Libraries/LibSQL/AST/Lexer.cpp | 410 ------ Userland/Libraries/LibSQL/AST/Lexer.h | 61 - Userland/Libraries/LibSQL/AST/Parser.cpp | 1175 --------------- Userland/Libraries/LibSQL/AST/Parser.h | 135 -- Userland/Libraries/LibSQL/AST/Select.cpp | 183 --- Userland/Libraries/LibSQL/AST/Statement.cpp | 25 - .../LibSQL/AST/SyntaxHighlighter.cpp | 97 -- .../Libraries/LibSQL/AST/SyntaxHighlighter.h | 32 - Userland/Libraries/LibSQL/AST/Token.cpp | 53 - Userland/Libraries/LibSQL/AST/Token.h | 255 ---- Userland/Libraries/LibSQL/AST/Update.cpp | 63 - Userland/Libraries/LibSQL/BTree.cpp | 113 -- Userland/Libraries/LibSQL/BTree.h | 201 --- Userland/Libraries/LibSQL/BTreeIterator.cpp | 247 ---- Userland/Libraries/LibSQL/CMakeLists.txt | 43 - Userland/Libraries/LibSQL/Database.cpp | 264 ---- Userland/Libraries/LibSQL/Database.h | 64 - Userland/Libraries/LibSQL/Forward.h | 89 -- Userland/Libraries/LibSQL/Heap.cpp | 367 ----- Userland/Libraries/LibSQL/Heap.h | 153 -- Userland/Libraries/LibSQL/Index.cpp | 28 - Userland/Libraries/LibSQL/Index.h | 58 - Userland/Libraries/LibSQL/Key.cpp | 34 - Userland/Libraries/LibSQL/Key.h | 29 - Userland/Libraries/LibSQL/Meta.cpp | 239 --- Userland/Libraries/LibSQL/Meta.h | 155 -- Userland/Libraries/LibSQL/Result.cpp | 47 - Userland/Libraries/LibSQL/Result.h | 132 -- Userland/Libraries/LibSQL/ResultSet.cpp | 53 - Userland/Libraries/LibSQL/ResultSet.h | 47 - Userland/Libraries/LibSQL/Row.cpp | 31 - Userland/Libraries/LibSQL/Row.h | 46 - Userland/Libraries/LibSQL/SQLClient.cpp | 88 -- Userland/Libraries/LibSQL/SQLClient.h | 75 - Userland/Libraries/LibSQL/Serializer.cpp | 28 - Userland/Libraries/LibSQL/Serializer.h | 167 --- Userland/Libraries/LibSQL/TreeNode.cpp | 383 ----- Userland/Libraries/LibSQL/Tuple.cpp | 207 --- Userland/Libraries/LibSQL/Tuple.h | 85 -- Userland/Libraries/LibSQL/TupleDescriptor.h | 104 -- Userland/Libraries/LibSQL/Type.h | 80 - Userland/Libraries/LibSQL/Value.cpp | 942 ------------ Userland/Libraries/LibSQL/Value.h | 202 --- Userland/Libraries/LibSyntax/Language.cpp | 8 - Userland/Libraries/LibSyntax/Language.h | 1 - Userland/Libraries/LibWebView/ProcessInfo.h | 1 - .../Libraries/LibWebView/ProcessManager.cpp | 4 - Userland/Services/SQLServer/CMakeLists.txt | 17 - .../SQLServer/ConnectionFromClient.cpp | 114 -- .../Services/SQLServer/ConnectionFromClient.h | 46 - .../Services/SQLServer/DatabaseConnection.cpp | 75 - .../Services/SQLServer/DatabaseConnection.h | 39 - Userland/Services/SQLServer/Forward.h | 13 - Userland/Services/SQLServer/SQLClient.ipc | 10 - Userland/Services/SQLServer/SQLServer.ipc | 10 - Userland/Services/SQLServer/SQLStatement.cpp | 146 -- Userland/Services/SQLServer/SQLStatement.h | 50 - Userland/Services/SQLServer/main.cpp | 29 - Userland/Utilities/CMakeLists.txt | 2 - Userland/Utilities/sql.cpp | 384 ----- 100 files changed, 8 insertions(+), 14561 deletions(-) delete mode 100644 Ladybird/SQLServer/CMakeLists.txt delete mode 100644 Ladybird/SQLServer/main.cpp delete mode 100644 Meta/Lagom/Fuzzers/FuzzSQLParser.cpp delete mode 100644 Meta/gn/secondary/Ladybird/SQLServer/BUILD.gn delete mode 100644 Meta/gn/secondary/Userland/Libraries/LibSQL/BUILD.gn delete mode 100644 Tests/LibSQL/CMakeLists.txt delete mode 100644 Tests/LibSQL/TestSqlBtreeIndex.cpp delete mode 100644 Tests/LibSQL/TestSqlDatabase.cpp delete mode 100644 Tests/LibSQL/TestSqlExpressionParser.cpp delete mode 100644 Tests/LibSQL/TestSqlHeap.cpp delete mode 100644 Tests/LibSQL/TestSqlStatementExecution.cpp delete mode 100644 Tests/LibSQL/TestSqlStatementParser.cpp delete mode 100644 Tests/LibSQL/TestSqlValueAndTuple.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/AST.h delete mode 100644 Userland/Libraries/LibSQL/AST/CreateSchema.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/CreateTable.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Delete.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Describe.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Expression.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Insert.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Lexer.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Lexer.h delete mode 100644 Userland/Libraries/LibSQL/AST/Parser.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Parser.h delete mode 100644 Userland/Libraries/LibSQL/AST/Select.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Statement.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/SyntaxHighlighter.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/SyntaxHighlighter.h delete mode 100644 Userland/Libraries/LibSQL/AST/Token.cpp delete mode 100644 Userland/Libraries/LibSQL/AST/Token.h delete mode 100644 Userland/Libraries/LibSQL/AST/Update.cpp delete mode 100644 Userland/Libraries/LibSQL/BTree.cpp delete mode 100644 Userland/Libraries/LibSQL/BTree.h delete mode 100644 Userland/Libraries/LibSQL/BTreeIterator.cpp delete mode 100644 Userland/Libraries/LibSQL/CMakeLists.txt delete mode 100644 Userland/Libraries/LibSQL/Database.cpp delete mode 100644 Userland/Libraries/LibSQL/Database.h delete mode 100644 Userland/Libraries/LibSQL/Forward.h delete mode 100644 Userland/Libraries/LibSQL/Heap.cpp delete mode 100644 Userland/Libraries/LibSQL/Heap.h delete mode 100644 Userland/Libraries/LibSQL/Index.cpp delete mode 100644 Userland/Libraries/LibSQL/Index.h delete mode 100644 Userland/Libraries/LibSQL/Key.cpp delete mode 100644 Userland/Libraries/LibSQL/Key.h delete mode 100644 Userland/Libraries/LibSQL/Meta.cpp delete mode 100644 Userland/Libraries/LibSQL/Meta.h delete mode 100644 Userland/Libraries/LibSQL/Result.cpp delete mode 100644 Userland/Libraries/LibSQL/Result.h delete mode 100644 Userland/Libraries/LibSQL/ResultSet.cpp delete mode 100644 Userland/Libraries/LibSQL/ResultSet.h delete mode 100644 Userland/Libraries/LibSQL/Row.cpp delete mode 100644 Userland/Libraries/LibSQL/Row.h delete mode 100644 Userland/Libraries/LibSQL/SQLClient.cpp delete mode 100644 Userland/Libraries/LibSQL/SQLClient.h delete mode 100644 Userland/Libraries/LibSQL/Serializer.cpp delete mode 100644 Userland/Libraries/LibSQL/Serializer.h delete mode 100644 Userland/Libraries/LibSQL/TreeNode.cpp delete mode 100644 Userland/Libraries/LibSQL/Tuple.cpp delete mode 100644 Userland/Libraries/LibSQL/Tuple.h delete mode 100644 Userland/Libraries/LibSQL/TupleDescriptor.h delete mode 100644 Userland/Libraries/LibSQL/Type.h delete mode 100644 Userland/Libraries/LibSQL/Value.cpp delete mode 100644 Userland/Libraries/LibSQL/Value.h delete mode 100644 Userland/Services/SQLServer/CMakeLists.txt delete mode 100644 Userland/Services/SQLServer/ConnectionFromClient.cpp delete mode 100644 Userland/Services/SQLServer/ConnectionFromClient.h delete mode 100644 Userland/Services/SQLServer/DatabaseConnection.cpp delete mode 100644 Userland/Services/SQLServer/DatabaseConnection.h delete mode 100644 Userland/Services/SQLServer/Forward.h delete mode 100644 Userland/Services/SQLServer/SQLClient.ipc delete mode 100644 Userland/Services/SQLServer/SQLServer.ipc delete mode 100644 Userland/Services/SQLServer/SQLStatement.cpp delete mode 100644 Userland/Services/SQLServer/SQLStatement.h delete mode 100644 Userland/Services/SQLServer/main.cpp delete mode 100644 Userland/Utilities/sql.cpp diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fa3269e5190..55f034e7d59 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,7 +12,6 @@ /Userland/Libraries/LibJS/Runtime/Intl @trflynn89 /Userland/Libraries/LibLocale @trflynn89 /Userland/Libraries/LibRegex @alimpfard -/Userland/Libraries/LibSQL @GMTA @trflynn89 /Userland/Libraries/LibTLS @alimpfard /Userland/Libraries/LibTimeZone @trflynn89 /Userland/Libraries/LibUnicode @trflynn89 @@ -22,11 +21,9 @@ /Userland/Libraries/LibWeb/WebDriver @trflynn89 /Userland/Libraries/LibXML @alimpfard /Userland/Services/RequestServer @alimpfard -/Userland/Services/SQLServer @trflynn89 /Userland/Services/WebDriver @trflynn89 /Userland/Utilities/gzip.cpp @timschumi /Userland/Utilities/lzcat.cpp @timschumi -/Userland/Utilities/sql.cpp @trflynn89 /Userland/Utilities/tar.cpp @timschumi /Userland/Utilities/unzip.cpp @timschumi /Userland/Utilities/wasm.cpp @alimpfard diff --git a/AK/Debug.h.in b/AK/Debug.h.in index 7ae80398654..8137b5fcf87 100644 --- a/AK/Debug.h.in +++ b/AK/Debug.h.in @@ -442,14 +442,6 @@ # cmakedefine01 SPICE_AGENT_DEBUG #endif -#ifndef SQL_DEBUG -# cmakedefine01 SQL_DEBUG -#endif - -#ifndef SQLSERVER_DEBUG -# cmakedefine01 SQLSERVER_DEBUG -#endif - #ifndef SYNTAX_HIGHLIGHTING_DEBUG # cmakedefine01 SYNTAX_HIGHLIGHTING_DEBUG #endif diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index 766f4c58cd9..1ebd1040a97 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -71,7 +71,7 @@ target_sources(ladybird PUBLIC FILE_SET ladybird TYPE HEADERS BASE_DIRS ${LADYBIRD_SOURCE_DIR} FILES ${LADYBIRD_HEADERS} ) -target_link_libraries(ladybird PRIVATE AK LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibJS LibMain LibSQL LibWeb LibWebView LibProtocol LibURL) +target_link_libraries(ladybird PRIVATE AK LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibJS LibMain LibWeb LibWebView LibProtocol LibURL) target_include_directories(ladybird PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(ladybird PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) @@ -99,7 +99,7 @@ add_executable(headless-browser target_include_directories(headless-browser PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(headless-browser PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) -target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibSQL LibTLS LibIPC LibDiff LibProtocol LibURL) +target_link_libraries(headless-browser PRIVATE AK LibCore LibWeb LibWebView LibWebSocket LibCrypto LibFileSystem LibHTTP LibImageDecoderClient LibJS LibGfx LibMain LibTLS LibIPC LibDiff LibProtocol LibURL) add_custom_target(run COMMAND "${CMAKE_COMMAND}" -E env "LADYBIRD_SOURCE_DIR=${LADYBIRD_SOURCE_DIR}" "$" $ENV{LAGOM_ARGS} @@ -114,12 +114,11 @@ add_custom_target(debug-ladybird add_subdirectory(ImageDecoder) add_subdirectory(RequestServer) -add_subdirectory(SQLServer) add_subdirectory(WebContent) add_subdirectory(WebDriver) add_subdirectory(WebWorker) -set(ladybird_helper_processes ImageDecoder RequestServer SQLServer WebContent WebWorker) +set(ladybird_helper_processes ImageDecoder RequestServer WebContent WebWorker) add_dependencies(ladybird ${ladybird_helper_processes}) add_dependencies(headless-browser ${ladybird_helper_processes}) diff --git a/Ladybird/HelperProcess.cpp b/Ladybird/HelperProcess.cpp index 3c6da4acd20..fa5393f7ab0 100644 --- a/Ladybird/HelperProcess.cpp +++ b/Ladybird/HelperProcess.cpp @@ -175,18 +175,6 @@ ErrorOr> launch_request_server_process(Re return launch_generic_server_process("RequestServer"sv, candidate_request_server_paths, move(arguments), RegisterWithProcessManager::Yes, Ladybird::EnableCallgrindProfiling::No); } -ErrorOr> launch_sql_server_process(ReadonlySpan candidate_sql_server_paths) -{ - Vector arguments; - - if (auto server = mach_server_name(); server.has_value()) { - arguments.append("--mach-server-name"sv); - arguments.append(server.value()); - } - - return launch_singleton_server_process("SQLServer"sv, candidate_sql_server_paths, arguments, RegisterWithProcessManager::Yes); -} - ErrorOr connect_new_request_server_client(Protocol::RequestClient& client) { auto new_socket = client.send_sync_but_allow_failure(); diff --git a/Ladybird/HelperProcess.h b/Ladybird/HelperProcess.h index 36db98d0ab5..bce225c9b48 100644 --- a/Ladybird/HelperProcess.h +++ b/Ladybird/HelperProcess.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,5 @@ ErrorOr> launch_web_content_process( ErrorOr> launch_image_decoder_process(ReadonlySpan candidate_image_decoder_paths); ErrorOr> launch_web_worker_process(ReadonlySpan candidate_web_worker_paths, NonnullRefPtr); ErrorOr> launch_request_server_process(ReadonlySpan candidate_request_server_paths, StringView serenity_resource_root, Vector const& certificates); -ErrorOr> launch_sql_server_process(ReadonlySpan candidate_sql_server_paths); ErrorOr connect_new_request_server_client(Protocol::RequestClient&); diff --git a/Ladybird/SQLServer/CMakeLists.txt b/Ladybird/SQLServer/CMakeLists.txt deleted file mode 100644 index 909388c2862..00000000000 --- a/Ladybird/SQLServer/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(SQL_SERVER_SOURCE_DIR ${LADYBIRD_SOURCE_DIR}/Userland/Services/SQLServer) - -set(SQL_SERVER_SOURCES - ${SQL_SERVER_SOURCE_DIR}/ConnectionFromClient.cpp - ${SQL_SERVER_SOURCE_DIR}/DatabaseConnection.cpp - ${SQL_SERVER_SOURCE_DIR}/SQLStatement.cpp - main.cpp -) - -add_executable(SQLServer ${SQL_SERVER_SOURCES}) - -target_include_directories(SQLServer PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/Services/) -target_include_directories(SQLServer PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..) -target_link_libraries(SQLServer PRIVATE LibCore LibFileSystem LibIPC LibSQL LibMain) diff --git a/Ladybird/SQLServer/main.cpp b/Ladybird/SQLServer/main.cpp deleted file mode 100644 index e06b6f41d62..00000000000 --- a/Ladybird/SQLServer/main.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022-2024, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(AK_OS_MACOS) -# include -#endif - -ErrorOr serenity_main(Main::Arguments arguments) -{ - AK::set_rich_debug_enabled(true); - - StringView pid_file; - StringView mach_server_name; - - Core::ArgsParser args_parser; - args_parser.add_option(pid_file, "Path to the PID file for the SQLServer singleton process", "pid-file", 'p', "pid_file"); - args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name"); - args_parser.parse(arguments); - - VERIFY(!pid_file.is_empty()); - - auto database_path = ByteString::formatted("{}/Ladybird", Core::StandardPaths::data_directory()); - TRY(Core::Directory::create(database_path, Core::Directory::CreateDirectories::Yes)); - - Core::EventLoop loop; - -#if defined(AK_OS_MACOS) - if (!mach_server_name.is_empty()) - Core::Platform::register_with_mach_server(mach_server_name); -#endif - - auto server = TRY(IPC::MultiServer::try_create()); - u64 connection_count { 0 }; - - server->on_new_client = [&](auto& client) { - client.set_database_path(database_path); - ++connection_count; - - client.on_disconnect = [&]() { - if (--connection_count == 0) { - MUST(Core::System::unlink(pid_file)); - loop.quit(0); - } - }; - }; - - return loop.exec(); -} diff --git a/Ladybird/WebContent/CMakeLists.txt b/Ladybird/WebContent/CMakeLists.txt index 2d956b1a236..bc3019052e9 100644 --- a/Ladybird/WebContent/CMakeLists.txt +++ b/Ladybird/WebContent/CMakeLists.txt @@ -70,7 +70,7 @@ else() add_executable(WebContent main.cpp) endif() -target_link_libraries(WebContent PRIVATE webcontent LibSQL LibURL) +target_link_libraries(WebContent PRIVATE webcontent LibURL) target_sources(webcontent PUBLIC FILE_SET ladybird TYPE HEADERS BASE_DIRS ${LADYBIRD_SOURCE_DIR} diff --git a/Ladybird/WebWorker/CMakeLists.txt b/Ladybird/WebWorker/CMakeLists.txt index 240f6d3a107..4276da17d67 100644 --- a/Ladybird/WebWorker/CMakeLists.txt +++ b/Ladybird/WebWorker/CMakeLists.txt @@ -20,7 +20,7 @@ add_library(webworker STATIC ${WEBWORKER_SOURCES}) target_include_directories(webworker PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/Services/) target_include_directories(webworker PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) target_include_directories(webworker PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/..) -target_link_libraries(webworker PUBLIC LibCore LibFileSystem LibGfx LibIPC LibJS LibProtocol LibWeb LibWebView LibLocale LibImageDecoderClient LibMain LibSQL LibURL) +target_link_libraries(webworker PUBLIC LibCore LibFileSystem LibGfx LibIPC LibJS LibProtocol LibWeb LibWebView LibLocale LibImageDecoderClient LibMain LibURL) add_executable(WebWorker main.cpp) target_include_directories(WebWorker PRIVATE ${LADYBIRD_SOURCE_DIR}/Userland/) diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index 41e75c6600d..a234529e913 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -174,8 +174,6 @@ set(SOCKET_DEBUG ON) set(SOLITAIRE_DEBUG ON) set(SPAM_DEBUG ON) set(SPICE_AGENT_DEBUG ON) -set(SQL_DEBUG ON) -set(SQLSERVER_DEBUG ON) set(STORAGE_DEVICE_DEBUG ON) set(SYNTAX_HIGHLIGHTING_DEBUG ON) set(SYSCALL_1_DEBUG ON) diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 1532b9821e2..4a2002bbb61 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -435,7 +435,6 @@ if (BUILD_LAGOM) Protocol Regex RIFF - SQL Syntax TextCodec Threading @@ -506,7 +505,6 @@ if (BUILD_LAGOM) lagom_utility(lzcat SOURCES ../../Userland/Utilities/lzcat.cpp LIBS LibCompress LibMain) - lagom_utility(sql SOURCES ../../Userland/Utilities/sql.cpp LIBS LibFileSystem LibIPC LibLine LibMain LibSQL) lagom_utility(tar SOURCES ../../Userland/Utilities/tar.cpp LIBS LibArchive LibCompress LibFileSystem LibMain) lagom_utility(test262-runner SOURCES ../../Tests/LibJS/test262-runner.cpp LIBS LibJS LibFileSystem) @@ -556,7 +554,6 @@ if (BUILD_LAGOM) LibCompress LibGfx LibLocale - LibSQL LibTest LibTextCodec LibTTF diff --git a/Meta/Lagom/Fuzzers/FuzzSQLParser.cpp b/Meta/Lagom/Fuzzers/FuzzSQLParser.cpp deleted file mode 100644 index 37cc7140b5c..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzSQLParser.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021, Luke Wilde - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - auto parser = SQL::AST::Parser(SQL::AST::Lexer({ data, size })); - [[maybe_unused]] auto statement = parser.next_statement(); - return 0; -} diff --git a/Meta/Lagom/Fuzzers/fuzzers.cmake b/Meta/Lagom/Fuzzers/fuzzers.cmake index 9b5a78f35d1..550bf19b646 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -42,7 +42,6 @@ set(FUZZER_TARGETS SHA256 SHA384 SHA512 - SQLParser Tar TextDecoder TGALoader @@ -108,7 +107,6 @@ set(FUZZER_DEPENDENCIES_SHA1 LibCrypto) set(FUZZER_DEPENDENCIES_SHA256 LibCrypto) set(FUZZER_DEPENDENCIES_SHA384 LibCrypto) set(FUZZER_DEPENDENCIES_SHA512 LibCrypto) -set(FUZZER_DEPENDENCIES_SQLParser LibSQL) set(FUZZER_DEPENDENCIES_Tar LibArchive) set(FUZZER_DEPENDENCIES_TextDecoder LibTextCodec) set(FUZZER_DEPENDENCIES_TGALoader LibGfx) diff --git a/Meta/ShellCompletions/zsh/_serenity b/Meta/ShellCompletions/zsh/_serenity index 69545c57266..662cc23cd4b 100644 --- a/Meta/ShellCompletions/zsh/_serenity +++ b/Meta/ShellCompletions/zsh/_serenity @@ -10,7 +10,7 @@ get_lagom_executables() { # If the Lagom binary directory is missing, this creates an empty list instead of erroring. # Known false positives need to be filtered manually; please add new ones. find "${LADYBIRD_SOURCE_DIR}/Build/lagom" -mindepth 1 -type f -executable -not -name '*.so*' \ - -not \( -name 'SQLServer' -o -name 'a.out' -o -name 'CMake*.bin' \) \ + -not \( -name 'a.out' -o -name 'CMake*.bin' \) \ -printf '%f\n' 2>/dev/null || true } diff --git a/Meta/gn/secondary/AK/BUILD.gn b/Meta/gn/secondary/AK/BUILD.gn index 51974a0a2bf..f03a6f1626f 100644 --- a/Meta/gn/secondary/AK/BUILD.gn +++ b/Meta/gn/secondary/AK/BUILD.gn @@ -327,8 +327,6 @@ write_cmake_config("ak_debug_gen") { "SOLITAIRE_DEBUG=", "SPAM_DEBUG=", "SPICE_AGENT_DEBUG=", - "SQLSERVER_DEBUG=", - "SQL_DEBUG=", "SYNTAX_HIGHLIGHTING_DEBUG=", "SYSCALL_1_DEBUG=", "SYSTEMSERVER_DEBUG=", diff --git a/Meta/gn/secondary/Ladybird/BUILD.gn b/Meta/gn/secondary/Ladybird/BUILD.gn index 1e0b5df7c0b..de79dc3bf18 100644 --- a/Meta/gn/secondary/Ladybird/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/BUILD.gn @@ -54,7 +54,6 @@ config("ladybird_config") { ladybird_helper_processes = [ "ImageDecoder", "RequestServer", - "SQLServer", "WebContent", "WebWorker", ] @@ -72,7 +71,6 @@ executable("ladybird_executable") { "//Userland/Libraries/LibJS", "//Userland/Libraries/LibMain", "//Userland/Libraries/LibProtocol", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibURL", "//Userland/Libraries/LibWeb", "//Userland/Libraries/LibWebView", @@ -188,7 +186,6 @@ executable("headless-browser") { "//Userland/Libraries/LibJS", "//Userland/Libraries/LibMain", "//Userland/Libraries/LibProtocol", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibTLS", "//Userland/Libraries/LibURL", "//Userland/Libraries/LibWeb", @@ -343,7 +340,6 @@ if (current_os != "mac") { ":ladybird_executable", "ImageDecoder", "RequestServer", - "SQLServer", "WebContent", "WebDriver", "WebWorker", @@ -354,7 +350,6 @@ if (current_os != "mac") { "$root_out_dir/bin/headless-browser", "$root_out_dir/libexec/ImageDecoder", "$root_out_dir/libexec/RequestServer", - "$root_out_dir/libexec/SQLServer", "$root_out_dir/libexec/WebContent", "$root_out_dir/libexec/WebWorker", ] @@ -381,7 +376,6 @@ if (current_os != "mac") { "//Userland/Libraries/LibProtocol", "//Userland/Libraries/LibRIFF", "//Userland/Libraries/LibRegex", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibSyntax", "//Userland/Libraries/LibTLS", "//Userland/Libraries/LibTextCodec", @@ -414,7 +408,6 @@ if (current_os != "mac") { "$root_out_dir/lib/liblagom-protocol.dylib", "$root_out_dir/lib/liblagom-regex.dylib", "$root_out_dir/lib/liblagom-riff.dylib", - "$root_out_dir/lib/liblagom-sql.dylib", "$root_out_dir/lib/liblagom-syntax.dylib", "$root_out_dir/lib/liblagom-textcodec.dylib", "$root_out_dir/lib/liblagom-threading.dylib", diff --git a/Meta/gn/secondary/Ladybird/SQLServer/BUILD.gn b/Meta/gn/secondary/Ladybird/SQLServer/BUILD.gn deleted file mode 100644 index 05ab555508f..00000000000 --- a/Meta/gn/secondary/Ladybird/SQLServer/BUILD.gn +++ /dev/null @@ -1,21 +0,0 @@ -executable("SQLServer") { - configs += [ "//Ladybird:ladybird_config" ] - include_dirs = [ - "//Userland/Libraries", - "//Userland/Services", - ] - deps = [ - "//AK", - "//Userland/Libraries/LibCore", - "//Userland/Libraries/LibIPC", - "//Userland/Libraries/LibMain", - "//Userland/Libraries/LibSQL", - ] - sources = [ - "//Userland/Services/SQLServer/ConnectionFromClient.cpp", - "//Userland/Services/SQLServer/DatabaseConnection.cpp", - "//Userland/Services/SQLServer/SQLStatement.cpp", - "main.cpp", - ] - output_dir = "$root_out_dir/libexec" -} diff --git a/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn b/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn index 49aa6fbf734..252befeab51 100644 --- a/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/WebContent/BUILD.gn @@ -47,7 +47,6 @@ executable("WebContent") { "//Userland/Libraries/LibJS", "//Userland/Libraries/LibMain", "//Userland/Libraries/LibProtocol", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibURL", "//Userland/Libraries/LibWeb", "//Userland/Libraries/LibWebSocket", diff --git a/Meta/gn/secondary/Ladybird/WebWorker/BUILD.gn b/Meta/gn/secondary/Ladybird/WebWorker/BUILD.gn index 949d65221be..ec6bec47e6f 100644 --- a/Meta/gn/secondary/Ladybird/WebWorker/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/WebWorker/BUILD.gn @@ -15,7 +15,6 @@ executable("WebWorker") { "//Userland/Libraries/LibLocale", "//Userland/Libraries/LibMain", "//Userland/Libraries/LibProtocol", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibURL", "//Userland/Libraries/LibWeb", "//Userland/Libraries/LibWeb:WebWorkerClientEndpoint", diff --git a/Meta/gn/secondary/Userland/Libraries/LibSQL/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibSQL/BUILD.gn deleted file mode 100644 index 02b2bb21c7c..00000000000 --- a/Meta/gn/secondary/Userland/Libraries/LibSQL/BUILD.gn +++ /dev/null @@ -1,73 +0,0 @@ -import("//Meta/gn/build/compiled_action.gni") - -compiled_action("SQLClientEndpoint") { - tool = "//Meta/Lagom/Tools/CodeGenerators/IPCCompiler" - inputs = [ "//Userland/Services/SQLServer/SQLClient.ipc" ] - outputs = [ "$root_gen_dir/SQLServer/SQLClientEndpoint.h" ] - args = [ - rebase_path(inputs[0], root_build_dir), - "-o", - rebase_path(outputs[0], root_build_dir), - ] -} - -compiled_action("SQLServerEndpoint") { - tool = "//Meta/Lagom/Tools/CodeGenerators/IPCCompiler" - inputs = [ "//Userland/Services/SQLServer/SQLServer.ipc" ] - outputs = [ "$root_gen_dir/SQLServer/SQLServerEndpoint.h" ] - args = [ - rebase_path(inputs[0], root_build_dir), - "-o", - rebase_path(outputs[0], root_build_dir), - ] -} - -shared_library("LibSQL") { - output_name = "sql" - include_dirs = [ - "//Userland/Libraries", - "//Userland", - ] - sources = [ - "AST/CreateSchema.cpp", - "AST/CreateTable.cpp", - "AST/Delete.cpp", - "AST/Describe.cpp", - "AST/Expression.cpp", - "AST/Insert.cpp", - "AST/Lexer.cpp", - "AST/Parser.cpp", - "AST/Select.cpp", - "AST/Statement.cpp", - "AST/SyntaxHighlighter.cpp", - "AST/Token.cpp", - "AST/Update.cpp", - "BTree.cpp", - "BTreeIterator.cpp", - "Database.cpp", - "Heap.cpp", - "Index.cpp", - "Key.cpp", - "Meta.cpp", - "Result.cpp", - "ResultSet.cpp", - "Row.cpp", - "SQLClient.cpp", - "Serializer.cpp", - "TreeNode.cpp", - "Tuple.cpp", - "Value.cpp", - ] - sources += get_target_outputs(":SQLClientEndpoint") + - get_target_outputs(":SQLServerEndpoint") - deps = [ - ":SQLClientEndpoint", - ":SQLServerEndpoint", - "//AK", - "//Userland/Libraries/LibCore", - "//Userland/Libraries/LibFileSystem", - "//Userland/Libraries/LibIPC", - "//Userland/Libraries/LibRegex", - "//Userland/Libraries/LibSyntax", - ] -} diff --git a/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn index 9be2adf77e5..8bf5e478354 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn @@ -136,7 +136,6 @@ shared_library("LibWebView") { "//Userland/Libraries/LibIPC", "//Userland/Libraries/LibJS", "//Userland/Libraries/LibProtocol", - "//Userland/Libraries/LibSQL", "//Userland/Libraries/LibURL", "//Userland/Libraries/LibWeb", ] diff --git a/README.md b/README.md index eac7516fa68..79d77499dd6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ We aim to build a complete, usable browser for the modern web. Ladybird uses a multi-process architecture with a main UI process, several WebContent renderer processes, -an ImageDecoder process, a RequestServer process, and a SQLServer process for holding cookies. +an ImageDecoder process, and a RequestServer process. Image decoding and network connections are done out of process to be more robust against malicious content. Each tab has its own renderer process, which is sandboxed from the rest of the system. diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index a7ede0bfe57..8f674b6ef01 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -7,7 +7,6 @@ add_subdirectory(LibGfx) add_subdirectory(LibJS) add_subdirectory(LibLocale) add_subdirectory(LibRegex) -add_subdirectory(LibSQL) add_subdirectory(LibTest) add_subdirectory(LibTextCodec) add_subdirectory(LibThreading) diff --git a/Tests/LibSQL/CMakeLists.txt b/Tests/LibSQL/CMakeLists.txt deleted file mode 100644 index 016570b5581..00000000000 --- a/Tests/LibSQL/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(TEST_SOURCES - TestSqlBtreeIndex.cpp - TestSqlDatabase.cpp - TestSqlExpressionParser.cpp - TestSqlHeap.cpp - TestSqlStatementExecution.cpp - TestSqlStatementParser.cpp - TestSqlValueAndTuple.cpp -) - -foreach(source IN LISTS TEST_SOURCES) - serenity_test("${source}" LibSQL LIBS LibSQL LibIPC) -endforeach() diff --git a/Tests/LibSQL/TestSqlBtreeIndex.cpp b/Tests/LibSQL/TestSqlBtreeIndex.cpp deleted file mode 100644 index c46eb8b6cca..00000000000 --- a/Tests/LibSQL/TestSqlBtreeIndex.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2021, Jan de Visser - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -constexpr static int keys[] = { - 39, - 87, - 77, - 42, - 98, - 40, - 53, - 8, - 37, - 12, - 90, - 72, - 73, - 11, - 88, - 22, - 10, - 82, - 25, - 61, - 97, - 18, - 60, - 68, - 21, - 3, - 58, - 29, - 13, - 17, - 89, - 81, - 16, - 64, - 5, - 41, - 36, - 91, - 38, - 24, - 32, - 50, - 34, - 94, - 49, - 47, - 1, - 6, - 44, - 76, -}; -constexpr static u32 pointers[] = { - 92, - 4, - 50, - 47, - 68, - 73, - 24, - 28, - 50, - 93, - 60, - 36, - 92, - 72, - 53, - 26, - 91, - 84, - 25, - 43, - 88, - 12, - 62, - 35, - 96, - 27, - 96, - 27, - 99, - 30, - 21, - 89, - 54, - 60, - 37, - 68, - 35, - 55, - 80, - 2, - 33, - 26, - 93, - 70, - 45, - 44, - 3, - 66, - 75, - 4, -}; - -NonnullRefPtr setup_btree(SQL::Serializer&); -void insert_and_get_to_and_from_btree(int); -void insert_into_and_scan_btree(int); - -NonnullRefPtr setup_btree(SQL::Serializer& serializer) -{ - NonnullRefPtr tuple_descriptor = adopt_ref(*new SQL::TupleDescriptor); - tuple_descriptor->append({ "schema", "table", "key_value", SQL::SQLType::Integer, SQL::Order::Ascending }); - - auto root_pointer = serializer.heap().user_value(0); - if (!root_pointer) { - root_pointer = serializer.heap().request_new_block_index(); - serializer.heap().set_user_value(0, root_pointer); - } - auto btree = MUST(SQL::BTree::create(serializer, tuple_descriptor, true, root_pointer)); - btree->on_new_root = [&]() { - serializer.heap().set_user_value(0, btree->root()); - }; - return btree; -} - -void insert_and_get_to_and_from_btree(int num_keys) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - { - auto heap = MUST(SQL::Heap::create("/tmp/test.db")); - TRY_OR_FAIL(heap->open()); - SQL::Serializer serializer(heap); - auto btree = setup_btree(serializer); - - for (auto ix = 0; ix < num_keys; ix++) { - SQL::Key k(btree->descriptor()); - k[0] = keys[ix]; - k.set_block_index(pointers[ix]); - btree->insert(k); - } -#ifdef LIST_TREE - btree->list_tree(); -#endif - } - - { - auto heap = MUST(SQL::Heap::create("/tmp/test.db")); - TRY_OR_FAIL(heap->open()); - SQL::Serializer serializer(heap); - auto btree = setup_btree(serializer); - - for (auto ix = 0; ix < num_keys; ix++) { - SQL::Key k(btree->descriptor()); - k[0] = keys[ix]; - auto pointer_opt = btree->get(k); - VERIFY(pointer_opt.has_value()); - EXPECT_EQ(pointer_opt.value(), pointers[ix]); - } - } -} - -void insert_into_and_scan_btree(int num_keys) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - { - auto heap = MUST(SQL::Heap::create("/tmp/test.db")); - TRY_OR_FAIL(heap->open()); - SQL::Serializer serializer(heap); - auto btree = setup_btree(serializer); - - for (auto ix = 0; ix < num_keys; ix++) { - SQL::Key k(btree->descriptor()); - k[0] = keys[ix]; - k.set_block_index(pointers[ix]); - btree->insert(k); - } - -#ifdef LIST_TREE - btree->list_tree(); -#endif - } - - { - auto heap = MUST(SQL::Heap::create("/tmp/test.db")); - TRY_OR_FAIL(heap->open()); - SQL::Serializer serializer(heap); - auto btree = setup_btree(serializer); - - int count = 0; - SQL::Tuple prev; - for (auto iter = btree->begin(); !iter.is_end(); iter++, count++) { - auto key = (*iter); - if (prev.size()) - EXPECT(prev < key); - auto key_value = key[0].to_int(); - for (auto ix = 0; ix < num_keys; ix++) { - if (keys[ix] == key_value) { - EXPECT_EQ(key.block_index(), pointers[ix]); - break; - } - } - prev = key; - } - EXPECT_EQ(count, num_keys); - } -} - -TEST_CASE(btree_one_key) -{ - insert_and_get_to_and_from_btree(1); -} - -TEST_CASE(btree_four_keys) -{ - insert_and_get_to_and_from_btree(4); -} - -TEST_CASE(btree_five_keys) -{ - insert_and_get_to_and_from_btree(5); -} - -TEST_CASE(btree_10_keys) -{ - insert_and_get_to_and_from_btree(10); -} - -TEST_CASE(btree_13_keys) -{ - insert_and_get_to_and_from_btree(13); -} - -TEST_CASE(btree_20_keys) -{ - insert_and_get_to_and_from_btree(20); -} - -TEST_CASE(btree_25_keys) -{ - insert_and_get_to_and_from_btree(25); -} - -TEST_CASE(btree_30_keys) -{ - insert_and_get_to_and_from_btree(30); -} - -TEST_CASE(btree_35_keys) -{ - insert_and_get_to_and_from_btree(35); -} - -TEST_CASE(btree_40_keys) -{ - insert_and_get_to_and_from_btree(40); -} - -TEST_CASE(btree_45_keys) -{ - insert_and_get_to_and_from_btree(45); -} - -TEST_CASE(btree_50_keys) -{ - insert_and_get_to_and_from_btree(50); -} - -TEST_CASE(btree_scan_one_key) -{ - insert_into_and_scan_btree(1); -} - -TEST_CASE(btree_scan_four_keys) -{ - insert_into_and_scan_btree(4); -} - -TEST_CASE(btree_scan_five_keys) -{ - insert_into_and_scan_btree(5); -} - -TEST_CASE(btree_scan_10_keys) -{ - insert_into_and_scan_btree(10); -} - -TEST_CASE(btree_scan_15_keys) -{ - insert_into_and_scan_btree(15); -} - -TEST_CASE(btree_scan_30_keys) -{ - insert_into_and_scan_btree(15); -} - -TEST_CASE(btree_scan_50_keys) -{ - insert_into_and_scan_btree(50); -} diff --git a/Tests/LibSQL/TestSqlDatabase.cpp b/Tests/LibSQL/TestSqlDatabase.cpp deleted file mode 100644 index 1d5e66c1e83..00000000000 --- a/Tests/LibSQL/TestSqlDatabase.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2021, Jan de Visser - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static NonnullRefPtr setup_schema(SQL::Database& db) -{ - auto schema = MUST(SQL::SchemaDef::create("TestSchema")); - MUST(db.add_schema(schema)); - return schema; -} - -// FIXME: using the return value for SQL::TableDef to insert a row results in a segfault -static NonnullRefPtr setup_table(SQL::Database& db) -{ - auto schema = setup_schema(db); - auto table = MUST(SQL::TableDef::create(schema, "TestTable")); - table->append_column("TextColumn", SQL::SQLType::Text); - table->append_column("IntColumn", SQL::SQLType::Integer); - EXPECT_EQ(table->num_columns(), 2u); - MUST(db.add_table(table)); - return table; -} - -static void insert_into_table(SQL::Database& db, int count) -{ - auto table = MUST(db.get_table("TestSchema", "TestTable")); - - for (int ix = 0; ix < count; ix++) { - SQL::Row row(*table); - StringBuilder builder; - builder.appendff("Test{}", ix); - - row["TextColumn"] = builder.to_byte_string(); - row["IntColumn"] = ix; - TRY_OR_FAIL(db.insert(row)); - } -} - -static void verify_table_contents(SQL::Database& db, int expected_count) -{ - auto table = MUST(db.get_table("TestSchema", "TestTable")); - - int sum = 0; - int count = 0; - auto rows = TRY_OR_FAIL(db.select_all(*table)); - for (auto& row : rows) { - StringBuilder builder; - builder.appendff("Test{}", row["IntColumn"].to_int().value()); - EXPECT_EQ(row["TextColumn"].to_byte_string(), builder.to_byte_string()); - count++; - sum += row["IntColumn"].to_int().value(); - } - EXPECT_EQ(count, expected_count); - EXPECT_EQ(sum, (expected_count * (expected_count - 1)) / 2); -} - -static void commit(SQL::Database& db) -{ - TRY_OR_FAIL(db.commit()); -} - -static void insert_and_verify(int count) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_table(db); - commit(db); - } - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - insert_into_table(db, count); - commit(db); - } - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - verify_table_contents(db, count); - } -} - -TEST_CASE(create_heap) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - auto heap = MUST(SQL::Heap::create("/tmp/test.db")); - TRY_OR_FAIL(heap->open()); - EXPECT_EQ(heap->version(), SQL::Heap::VERSION); -} - -TEST_CASE(create_from_dev_random) -{ - auto heap = MUST(SQL::Heap::create("/dev/random")); - auto should_be_error = heap->open(); - EXPECT(should_be_error.is_error()); -} - -TEST_CASE(create_from_unreadable_file) -{ - auto heap = MUST(SQL::Heap::create("/etc/shadow")); - auto should_be_error = heap->open(); - EXPECT(should_be_error.is_error()); -} - -TEST_CASE(create_in_non_existing_dir) -{ - auto heap = MUST(SQL::Heap::create("/tmp/bogus/test.db")); - auto should_be_error = heap->open(); - EXPECT(should_be_error.is_error()); -} - -TEST_CASE(create_database) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - commit(db); -} - -TEST_CASE(add_schema_to_database) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_schema(db); - commit(db); -} - -TEST_CASE(get_schema_from_database) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_schema(db); - commit(db); - } - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - auto schema = MUST(db->get_schema("TestSchema")); - } -} - -TEST_CASE(add_table_to_database) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_table(db); - commit(db); -} - -TEST_CASE(get_table_from_database) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_table(db); - commit(db); - } - { - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - - auto table = MUST(db->get_table("TestSchema", "TestTable")); - EXPECT_EQ(table->name(), "TestTable"); - EXPECT_EQ(table->num_columns(), 2u); - } -} - -TEST_CASE(insert_one_into_and_select_from_table) -{ - insert_and_verify(1); -} - -TEST_CASE(insert_two_into_table) -{ - insert_and_verify(2); -} - -TEST_CASE(insert_10_into_table) -{ - insert_and_verify(10); -} - -TEST_CASE(insert_100_into_table) -{ - insert_and_verify(100); -} - -TEST_CASE(reuse_row_storage) -{ - ScopeGuard guard([]() { unlink("/tmp/test.db"); }); - auto db = MUST(SQL::Database::create("/tmp/test.db")); - MUST(db->open()); - (void)setup_table(db); - auto table = MUST(db->get_table("TestSchema", "TestTable")); - - // Insert row - SQL::Row row(*table); - row["TextColumn"] = "text value"; - row["IntColumn"] = 12345; - TRY_OR_FAIL(db->insert(row)); - TRY_OR_FAIL(db->commit()); - auto original_size_in_bytes = MUST(db->file_size_in_bytes()); - - // Remove row - TRY_OR_FAIL(db->remove(row)); - TRY_OR_FAIL(db->commit()); - auto size_in_bytes_after_removal = MUST(db->file_size_in_bytes()); - EXPECT(size_in_bytes_after_removal <= original_size_in_bytes); - - // Insert same row again - TRY_OR_FAIL(db->insert(row)); - TRY_OR_FAIL(db->commit()); - auto size_in_bytes_after_reinsertion = MUST(db->file_size_in_bytes()); - EXPECT(size_in_bytes_after_reinsertion <= original_size_in_bytes); -} diff --git a/Tests/LibSQL/TestSqlExpressionParser.cpp b/Tests/LibSQL/TestSqlExpressionParser.cpp deleted file mode 100644 index 41fb46849b6..00000000000 --- a/Tests/LibSQL/TestSqlExpressionParser.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (c) 2021, Tim Flynn - * Copyright (c) 2021, Jan de Visser - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -class ExpressionParser : public SQL::AST::Parser { -public: - explicit ExpressionParser(SQL::AST::Lexer lexer) - : SQL::AST::Parser(move(lexer)) - { - } - - NonnullRefPtr parse() - { - return SQL::AST::Parser::parse_expression(); - } -}; - -using ParseResult = AK::Result, ByteString>; - -ParseResult parse(StringView sql) -{ - auto parser = ExpressionParser(SQL::AST::Lexer(sql)); - auto expression = parser.parse(); - - if (parser.has_errors()) { - return parser.errors()[0].to_byte_string(); - } - - return expression; -} - -} - -TEST_CASE(numeric_literal) -{ - // FIXME Right now the "1a" test fails (meaning the parse succeeds). - // This is obviously inconsistent. - // See the FIXME in lexer.cpp, method consume_exponent() about - // solutions. - // EXPECT(parse("1e"sv).is_error()); - // EXPECT(parse("1a"sv).is_error()); - // EXPECT(parse("0x"sv).is_error()); - - auto validate = [](StringView sql, double expected_value) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& literal = static_cast(*expression); - EXPECT_EQ(literal.value(), expected_value); - }; - - validate("123"sv, 123); - validate("3.14"sv, 3.14); - validate("0xA"sv, 10); - validate("0xff"sv, 255); - validate("0x100"sv, 256); - validate("1e3"sv, 1000); -} - -TEST_CASE(string_literal) -{ - EXPECT(parse("'"sv).is_error()); - EXPECT(parse("'unterminated"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_value) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& literal = static_cast(*expression); - EXPECT_EQ(literal.value(), expected_value); - }; - - validate("''"sv, ""sv); - validate("'hello friends'"sv, "hello friends"sv); - validate("'hello ''friends'''"sv, "hello 'friends'"sv); -} - -TEST_CASE(blob_literal) -{ - EXPECT(parse("x'"sv).is_error()); - EXPECT(parse("x'unterminated"sv).is_error()); - EXPECT(parse("x'NOTHEX'"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_value) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& literal = static_cast(*expression); - EXPECT_EQ(literal.value(), expected_value); - }; - - validate("x''"sv, ""sv); - validate("x'DEADC0DE'"sv, "DEADC0DE"sv); -} - -TEST_CASE(boolean_literal) -{ - auto validate = [](StringView sql, bool expected_value) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& literal = static_cast(*expression); - EXPECT_EQ(literal.value(), expected_value); - }; - - validate("TRUE"sv, true); - validate("FALSE"sv, false); -} - -TEST_CASE(null_literal) -{ - auto validate = [](StringView sql) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - }; - - validate("NULL"sv); -} - -TEST_CASE(bind_parameter) -{ - auto validate = [](StringView sql) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - }; - - validate("?"sv); -} - -TEST_CASE(column_name) -{ - EXPECT(parse(".column_name"sv).is_error()); - EXPECT(parse("table_name."sv).is_error()); - EXPECT(parse("schema_name.table_name."sv).is_error()); - EXPECT(parse("\"unterminated"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_column) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& column = static_cast(*expression); - EXPECT_EQ(column.schema_name(), expected_schema); - EXPECT_EQ(column.table_name(), expected_table); - EXPECT_EQ(column.column_name(), expected_column); - }; - - validate("column_name"sv, {}, {}, "COLUMN_NAME"sv); - validate("table_name.column_name"sv, {}, "TABLE_NAME"sv, "COLUMN_NAME"sv); - validate("schema_name.table_name.column_name"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "COLUMN_NAME"sv); - validate("\"Column_Name\""sv, {}, {}, "Column_Name"sv); - validate("\"Column\n_Name\""sv, {}, {}, "Column\n_Name"sv); -} - -TEST_CASE(unary_operator) -{ - EXPECT(parse("-"sv).is_error()); - EXPECT(parse("--"sv).is_error()); - EXPECT(parse("+"sv).is_error()); - EXPECT(parse("++"sv).is_error()); - EXPECT(parse("~"sv).is_error()); - EXPECT(parse("~~"sv).is_error()); - EXPECT(parse("NOT"sv).is_error()); - - auto validate = [](StringView sql, SQL::AST::UnaryOperator expected_operator) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& unary = static_cast(*expression); - EXPECT_EQ(unary.type(), expected_operator); - - auto const& secondary_expression = unary.expression(); - EXPECT(!is(*secondary_expression)); - }; - - validate("-15"sv, SQL::AST::UnaryOperator::Minus); - validate("+15"sv, SQL::AST::UnaryOperator::Plus); - validate("~15"sv, SQL::AST::UnaryOperator::BitwiseNot); - validate("NOT 15"sv, SQL::AST::UnaryOperator::Not); -} - -TEST_CASE(binary_operator) -{ - HashMap operators { - { "||"sv, SQL::AST::BinaryOperator::Concatenate }, - { "*"sv, SQL::AST::BinaryOperator::Multiplication }, - { "/"sv, SQL::AST::BinaryOperator::Division }, - { "%"sv, SQL::AST::BinaryOperator::Modulo }, - { "+"sv, SQL::AST::BinaryOperator::Plus }, - { "-"sv, SQL::AST::BinaryOperator::Minus }, - { "<<"sv, SQL::AST::BinaryOperator::ShiftLeft }, - { ">>"sv, SQL::AST::BinaryOperator::ShiftRight }, - { "&"sv, SQL::AST::BinaryOperator::BitwiseAnd }, - { "|"sv, SQL::AST::BinaryOperator::BitwiseOr }, - { "<"sv, SQL::AST::BinaryOperator::LessThan }, - { "<="sv, SQL::AST::BinaryOperator::LessThanEquals }, - { ">"sv, SQL::AST::BinaryOperator::GreaterThan }, - { ">="sv, SQL::AST::BinaryOperator::GreaterThanEquals }, - { "="sv, SQL::AST::BinaryOperator::Equals }, - { "=="sv, SQL::AST::BinaryOperator::Equals }, - { "!="sv, SQL::AST::BinaryOperator::NotEquals }, - { "<>"sv, SQL::AST::BinaryOperator::NotEquals }, - { "AND"sv, SQL::AST::BinaryOperator::And }, - { "OR"sv, SQL::AST::BinaryOperator::Or }, - }; - - for (auto op : operators) { - EXPECT(parse(op.key).is_error()); - - StringBuilder builder; - builder.append("1 "sv); - builder.append(op.key); - EXPECT(parse(builder.to_byte_string()).is_error()); - - builder.clear(); - - if (op.key != "+" && op.key != "-") { // "+1" and "-1" are fine (unary operator). - builder.append(op.key); - builder.append(" 1"sv); - EXPECT(parse(builder.to_byte_string()).is_error()); - } - } - - auto validate = [](StringView sql, SQL::AST::BinaryOperator expected_operator) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& binary = static_cast(*expression); - EXPECT(!is(*binary.lhs())); - EXPECT(!is(*binary.rhs())); - EXPECT_EQ(binary.type(), expected_operator); - }; - - for (auto op : operators) { - StringBuilder builder; - builder.append("1 "sv); - builder.append(op.key); - builder.append(" 1"sv); - validate(builder.to_byte_string(), op.value); - } -} - -TEST_CASE(chained_expression) -{ - EXPECT(parse("()"sv).is_error()); - EXPECT(parse("(,)"sv).is_error()); - EXPECT(parse("(15,)"sv).is_error()); - - auto validate = [](StringView sql, size_t expected_chain_size) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& chain = static_cast(*expression).expressions(); - EXPECT_EQ(chain.size(), expected_chain_size); - - for (auto const& chained_expression : chain) - EXPECT(!is(chained_expression)); - }; - - validate("(15)"sv, 1); - validate("(15, 16)"sv, 2); - validate("(15, 16, column_name)"sv, 3); -} - -TEST_CASE(cast_expression) -{ - EXPECT(parse("CAST"sv).is_error()); - EXPECT(parse("CAST ("sv).is_error()); - EXPECT(parse("CAST ()"sv).is_error()); - EXPECT(parse("CAST (15)"sv).is_error()); - EXPECT(parse("CAST (15 AS"sv).is_error()); - EXPECT(parse("CAST (15 AS)"sv).is_error()); - EXPECT(parse("CAST (15 AS int"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_type_name) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& cast = static_cast(*expression); - EXPECT(!is(*cast.expression())); - - auto const& type_name = cast.type_name(); - EXPECT_EQ(type_name->name(), expected_type_name); - }; - - validate("CAST (15 AS int)"sv, "INT"sv); - // FIXME The syntax in the test below fails on both sqlite3 and psql (PostgreSQL). - // Also fails here because null is interpreted as the NULL keyword and not the - // identifier null (which is not a type) - // validate("CAST ('NULL' AS null)"sv, "null"sv); - validate("CAST (15 AS varchar(255))"sv, "VARCHAR"sv); -} - -TEST_CASE(case_expression) -{ - EXPECT(parse("CASE"sv).is_error()); - EXPECT(parse("CASE END"sv).is_error()); - EXPECT(parse("CASE 15"sv).is_error()); - EXPECT(parse("CASE 15 END"sv).is_error()); - EXPECT(parse("CASE WHEN"sv).is_error()); - EXPECT(parse("CASE WHEN THEN"sv).is_error()); - EXPECT(parse("CASE WHEN 15 THEN 16"sv).is_error()); - EXPECT(parse("CASE WHEN 15 THEN 16 ELSE"sv).is_error()); - EXPECT(parse("CASE WHEN 15 THEN 16 ELSE END"sv).is_error()); - - auto validate = [](StringView sql, bool expect_case_expression, size_t expected_when_then_size, bool expect_else_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& case_ = static_cast(*expression); - - auto const& case_expression = case_.case_expression(); - EXPECT_EQ(case_expression.is_null(), !expect_case_expression); - if (case_expression) - EXPECT(!is(*case_expression)); - - auto const& when_then_clauses = case_.when_then_clauses(); - EXPECT_EQ(when_then_clauses.size(), expected_when_then_size); - for (auto const& when_then_clause : when_then_clauses) { - EXPECT(!is(*when_then_clause.when)); - EXPECT(!is(*when_then_clause.then)); - } - - auto const& else_expression = case_.else_expression(); - EXPECT_EQ(else_expression.is_null(), !expect_else_expression); - if (else_expression) - EXPECT(!is(*else_expression)); - }; - - validate("CASE WHEN 16 THEN 17 END"sv, false, 1, false); - validate("CASE WHEN 16 THEN 17 WHEN 18 THEN 19 END"sv, false, 2, false); - validate("CASE WHEN 16 THEN 17 WHEN 18 THEN 19 ELSE 20 END"sv, false, 2, true); - - validate("CASE 15 WHEN 16 THEN 17 END"sv, true, 1, false); - validate("CASE 15 WHEN 16 THEN 17 WHEN 18 THEN 19 END"sv, true, 2, false); - validate("CASE 15 WHEN 16 THEN 17 WHEN 18 THEN 19 ELSE 20 END"sv, true, 2, true); -} - -TEST_CASE(exists_expression) -{ - EXPECT(parse("EXISTS"sv).is_error()); - EXPECT(parse("EXISTS ("sv).is_error()); - EXPECT(parse("EXISTS (SELECT"sv).is_error()); - EXPECT(parse("EXISTS (SELECT)"sv).is_error()); - EXPECT(parse("EXISTS (SELECT * FROM table_name"sv).is_error()); - EXPECT(parse("NOT EXISTS"sv).is_error()); - EXPECT(parse("NOT EXISTS ("sv).is_error()); - EXPECT(parse("NOT EXISTS (SELECT"sv).is_error()); - EXPECT(parse("NOT EXISTS (SELECT)"sv).is_error()); - EXPECT(parse("NOT EXISTS (SELECT * FROM table_name"sv).is_error()); - EXPECT(parse("("sv).is_error()); - EXPECT(parse("(SELECT"sv).is_error()); - EXPECT(parse("(SELECT)"sv).is_error()); - EXPECT(parse("(SELECT * FROM table_name"sv).is_error()); - - auto validate = [](StringView sql, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& exists = static_cast(*expression); - EXPECT_EQ(exists.invert_expression(), expected_invert_expression); - }; - - validate("EXISTS (SELECT * FROM table_name)"sv, false); - validate("NOT EXISTS (SELECT * FROM table_name)"sv, true); - validate("(SELECT * FROM table_name)"sv, false); -} - -TEST_CASE(collate_expression) -{ - EXPECT(parse("COLLATE"sv).is_error()); - EXPECT(parse("COLLATE name"sv).is_error()); - EXPECT(parse("15 COLLATE"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_collation_name) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& collate = static_cast(*expression); - EXPECT(!is(*collate.expression())); - EXPECT_EQ(collate.collation_name(), expected_collation_name); - }; - - validate("15 COLLATE fifteen"sv, "FIFTEEN"sv); - validate("(15, 16) COLLATE \"chain\""sv, "chain"sv); -} - -TEST_CASE(is_expression) -{ - EXPECT(parse("IS"sv).is_error()); - EXPECT(parse("IS 1"sv).is_error()); - EXPECT(parse("1 IS"sv).is_error()); - EXPECT(parse("IS NOT"sv).is_error()); - EXPECT(parse("IS NOT 1"sv).is_error()); - EXPECT(parse("1 IS NOT"sv).is_error()); - - auto validate = [](StringView sql, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& is_ = static_cast(*expression); - EXPECT(!is(*is_.lhs())); - EXPECT(!is(*is_.rhs())); - EXPECT_EQ(is_.invert_expression(), expected_invert_expression); - }; - - validate("1 IS NULL"sv, false); - validate("1 IS NOT NULL"sv, true); -} - -TEST_CASE(match_expression) -{ - HashMap operators { - { "LIKE"sv, SQL::AST::MatchOperator::Like }, - { "GLOB"sv, SQL::AST::MatchOperator::Glob }, - { "MATCH"sv, SQL::AST::MatchOperator::Match }, - { "REGEXP"sv, SQL::AST::MatchOperator::Regexp }, - }; - - for (auto op : operators) { - EXPECT(parse(op.key).is_error()); - - StringBuilder builder; - builder.append("1 "sv); - builder.append(op.key); - EXPECT(parse(builder.to_byte_string()).is_error()); - - builder.clear(); - builder.append(op.key); - builder.append(" 1"sv); - EXPECT(parse(builder.to_byte_string()).is_error()); - } - - auto validate = [](StringView sql, SQL::AST::MatchOperator expected_operator, bool expected_invert_expression, bool expect_escape) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& match = static_cast(*expression); - EXPECT(!is(*match.lhs())); - EXPECT(!is(*match.rhs())); - EXPECT_EQ(match.type(), expected_operator); - EXPECT_EQ(match.invert_expression(), expected_invert_expression); - EXPECT(match.escape() || !expect_escape); - }; - - for (auto op : operators) { - StringBuilder builder; - builder.append("1 "sv); - builder.append(op.key); - builder.append(" 1"sv); - validate(builder.to_byte_string(), op.value, false, false); - - builder.clear(); - builder.append("1 NOT "sv); - builder.append(op.key); - builder.append(" 1"sv); - validate(builder.to_byte_string(), op.value, true, false); - - builder.clear(); - builder.append("1 NOT "sv); - builder.append(op.key); - builder.append(" 1 ESCAPE '+'"sv); - validate(builder.to_byte_string(), op.value, true, true); - } -} - -TEST_CASE(null_expression) -{ - EXPECT(parse("ISNULL"sv).is_error()); - EXPECT(parse("NOTNULL"sv).is_error()); - EXPECT(parse("15 NOT"sv).is_error()); - - auto validate = [](StringView sql, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& null = static_cast(*expression); - EXPECT_EQ(null.invert_expression(), expected_invert_expression); - }; - - validate("15 ISNULL"sv, false); - validate("15 NOTNULL"sv, true); - validate("15 NOT NULL"sv, true); -} - -TEST_CASE(between_expression) -{ - EXPECT(parse("BETWEEN"sv).is_error()); - EXPECT(parse("NOT BETWEEN"sv).is_error()); - EXPECT(parse("BETWEEN 10 AND 20"sv).is_error()); - EXPECT(parse("NOT BETWEEN 10 AND 20"sv).is_error()); - EXPECT(parse("15 BETWEEN 10"sv).is_error()); - EXPECT(parse("15 BETWEEN 10 AND"sv).is_error()); - EXPECT(parse("15 BETWEEN AND 20"sv).is_error()); - EXPECT(parse("15 BETWEEN 10 OR 20"sv).is_error()); - - auto validate = [](StringView sql, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& between = static_cast(*expression); - EXPECT(!is(*between.expression())); - EXPECT(!is(*between.lhs())); - EXPECT(!is(*between.rhs())); - EXPECT_EQ(between.invert_expression(), expected_invert_expression); - }; - - validate("15 BETWEEN 10 AND 20"sv, false); - validate("15 NOT BETWEEN 10 AND 20"sv, true); -} - -TEST_CASE(in_table_expression) -{ - EXPECT(parse("IN"sv).is_error()); - EXPECT(parse("IN table_name"sv).is_error()); - EXPECT(parse("NOT IN"sv).is_error()); - EXPECT(parse("NOT IN table_name"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& in = static_cast(*expression); - EXPECT(!is(*in.expression())); - EXPECT_EQ(in.schema_name(), expected_schema); - EXPECT_EQ(in.table_name(), expected_table); - EXPECT_EQ(in.invert_expression(), expected_invert_expression); - }; - - validate("15 IN table_name"sv, {}, "TABLE_NAME"sv, false); - validate("15 IN schema_name.table_name"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, false); - - validate("15 NOT IN table_name"sv, {}, "TABLE_NAME"sv, true); - validate("15 NOT IN schema_name.table_name"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, true); -} - -TEST_CASE(in_chained_expression) -{ - EXPECT(parse("IN ()"sv).is_error()); - EXPECT(parse("NOT IN ()"sv).is_error()); - - auto validate = [](StringView sql, size_t expected_chain_size, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& in = static_cast(*expression); - EXPECT(!is(*in.expression())); - EXPECT_EQ(in.expression_chain()->expressions().size(), expected_chain_size); - EXPECT_EQ(in.invert_expression(), expected_invert_expression); - - for (auto const& chained_expression : in.expression_chain()->expressions()) - EXPECT(!is(chained_expression)); - }; - - validate("15 IN ()"sv, 0, false); - validate("15 IN (15)"sv, 1, false); - validate("15 IN (15, 16)"sv, 2, false); - - validate("15 NOT IN ()"sv, 0, true); - validate("15 NOT IN (15)"sv, 1, true); - validate("15 NOT IN (15, 16)"sv, 2, true); -} - -TEST_CASE(in_selection_expression) -{ - EXPECT(parse("IN (SELECT)"sv).is_error()); - EXPECT(parse("IN (SELECT * FROM table_name, SELECT * FROM table_name);"sv).is_error()); - EXPECT(parse("NOT IN (SELECT)"sv).is_error()); - EXPECT(parse("NOT IN (SELECT * FROM table_name, SELECT * FROM table_name);"sv).is_error()); - - auto validate = [](StringView sql, bool expected_invert_expression) { - auto expression = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*expression)); - - auto const& in = static_cast(*expression); - EXPECT(!is(*in.expression())); - EXPECT_EQ(in.invert_expression(), expected_invert_expression); - }; - - validate("15 IN (SELECT * FROM table_name)"sv, false); - validate("15 NOT IN (SELECT * FROM table_name)"sv, true); -} - -TEST_CASE(expression_tree_depth_limit) -{ - auto too_deep_expression = ByteString::formatted("{:+^{}}1", "", SQL::AST::Limits::maximum_expression_tree_depth); - EXPECT(!parse(too_deep_expression.substring_view(1)).is_error()); - EXPECT(parse(too_deep_expression).is_error()); -} diff --git a/Tests/LibSQL/TestSqlHeap.cpp b/Tests/LibSQL/TestSqlHeap.cpp deleted file mode 100644 index 03041541c8e..00000000000 --- a/Tests/LibSQL/TestSqlHeap.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2023, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -static constexpr auto db_path = "/tmp/test.db"sv; - -static NonnullRefPtr create_heap() -{ - auto heap = MUST(SQL::Heap::create(db_path)); - MUST(heap->open()); - return heap; -} - -TEST_CASE(heap_write_large_storage_without_flush) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - auto heap = create_heap(); - auto storage_block_id = heap->request_new_block_index(); - - // Write large storage spanning multiple blocks - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - - // Read back - auto stored_long_string = TRY_OR_FAIL(heap->read_storage(storage_block_id)); - EXPECT_EQ(long_string.bytes(), stored_long_string.bytes()); -} - -TEST_CASE(heap_write_large_storage_with_flush) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - auto heap = create_heap(); - auto storage_block_id = heap->request_new_block_index(); - - // Write large storage spanning multiple blocks - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - MUST(heap->flush()); - - // Read back - auto stored_long_string = TRY_OR_FAIL(heap->read_storage(storage_block_id)); - EXPECT_EQ(long_string.bytes(), stored_long_string.bytes()); -} - -TEST_CASE(heap_overwrite_large_storage) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - auto heap = create_heap(); - auto storage_block_id = heap->request_new_block_index(); - - // Write large storage spanning multiple blocks - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - MUST(heap->flush()); - auto heap_size = MUST(heap->file_size_in_bytes()); - - // Let's write it again and check whether the Heap reused the same extended blocks - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - MUST(heap->flush()); - auto new_heap_size = MUST(heap->file_size_in_bytes()); - EXPECT_EQ(heap_size, new_heap_size); - - // Write a smaller string and read back - heap size should be at most the previous size - builder.clear(); - MUST(builder.try_append_repeated('y', SQL::Block::DATA_SIZE * 2)); - auto shorter_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, shorter_string.bytes())); - MUST(heap->flush()); - new_heap_size = MUST(heap->file_size_in_bytes()); - EXPECT(new_heap_size <= heap_size); - auto stored_shorter_string = TRY_OR_FAIL(heap->read_storage(storage_block_id)); - EXPECT_EQ(shorter_string.bytes(), stored_shorter_string.bytes()); - - // Write a longer string and read back - heap size is expected to grow - builder.clear(); - MUST(builder.try_append_repeated('z', SQL::Block::DATA_SIZE * 6)); - auto longest_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, longest_string.bytes())); - MUST(heap->flush()); - new_heap_size = MUST(heap->file_size_in_bytes()); - EXPECT(new_heap_size > heap_size); - auto stored_longest_string = TRY_OR_FAIL(heap->read_storage(storage_block_id)); - EXPECT_EQ(longest_string.bytes(), stored_longest_string.bytes()); -} - -TEST_CASE(heap_reuse_freed_blocks_after_storage_trim) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - auto heap = create_heap(); - - // First, write storage spanning 4 blocks - auto first_index = heap->request_new_block_index(); - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes())); - MUST(heap->flush()); - auto original_heap_size = MUST(heap->file_size_in_bytes()); - - // Then, overwrite the first storage and reduce it to 2 blocks - builder.clear(); - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 2)); - long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes())); - MUST(heap->flush()); - auto heap_size_after_reduction = MUST(heap->file_size_in_bytes()); - EXPECT(heap_size_after_reduction <= original_heap_size); - - // Now add the second storage spanning 2 blocks - heap should not have grown compared to the original storage - auto second_index = heap->request_new_block_index(); - TRY_OR_FAIL(heap->write_storage(second_index, long_string.bytes())); - MUST(heap->flush()); - auto heap_size_after_second_storage = MUST(heap->file_size_in_bytes()); - EXPECT(heap_size_after_second_storage <= original_heap_size); -} - -TEST_CASE(heap_reuse_freed_blocks_after_reopening_file) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - - size_t original_heap_size = 0; - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - - { - auto heap = create_heap(); - - // First, write storage spanning 4 blocks - auto first_index = heap->request_new_block_index(); - TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes())); - MUST(heap->flush()); - original_heap_size = MUST(heap->file_size_in_bytes()); - - // Then, overwrite the first storage and reduce it to 2 blocks - builder.clear(); - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 2)); - long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(first_index, long_string.bytes())); - MUST(heap->flush()); - auto heap_size_after_reduction = MUST(heap->file_size_in_bytes()); - EXPECT(heap_size_after_reduction <= original_heap_size); - } - - // Reopen the database file; we expect the heap to support reading back free blocks somehow. - // Add the second storage spanning 2 blocks - heap should not have grown compared to the original storage. - { - auto heap = create_heap(); - auto second_index = heap->request_new_block_index(); - TRY_OR_FAIL(heap->write_storage(second_index, long_string.bytes())); - MUST(heap->flush()); - auto heap_size_after_second_storage = MUST(heap->file_size_in_bytes()); - EXPECT(heap_size_after_second_storage <= original_heap_size); - } -} - -TEST_CASE(heap_free_storage) -{ - ScopeGuard guard([]() { MUST(Core::System::unlink(db_path)); }); - auto heap = create_heap(); - auto storage_block_id = heap->request_new_block_index(); - - // Write large storage spanning multiple blocks - StringBuilder builder; - MUST(builder.try_append_repeated('x', SQL::Block::DATA_SIZE * 4)); - auto long_string = builder.string_view(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - MUST(heap->flush()); - auto heap_size = MUST(heap->file_size_in_bytes()); - - // Free the storage - TRY_OR_FAIL(heap->free_storage(storage_block_id)); - - // Again, write some large storage spanning multiple blocks - storage_block_id = heap->request_new_block_index(); - TRY_OR_FAIL(heap->write_storage(storage_block_id, long_string.bytes())); - MUST(heap->flush()); - auto new_heap_size = MUST(heap->file_size_in_bytes()); - EXPECT(new_heap_size <= heap_size); -} diff --git a/Tests/LibSQL/TestSqlStatementExecution.cpp b/Tests/LibSQL/TestSqlStatementExecution.cpp deleted file mode 100644 index e4c21138f28..00000000000 --- a/Tests/LibSQL/TestSqlStatementExecution.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * Copyright (c) 2021, Jan de Visser - * Copyright (c) 2021, Mahmoud Mandour - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -constexpr char const* db_name = "/tmp/test.db"; - -SQL::ResultOr try_execute(NonnullRefPtr database, ByteString const& sql, Vector placeholder_values = {}) -{ - auto parser = SQL::AST::Parser(SQL::AST::Lexer(sql)); - auto statement = parser.next_statement(); - EXPECT(!parser.has_errors()); - if (parser.has_errors()) - outln("{}", parser.errors()[0].to_byte_string()); - return statement->execute(move(database), placeholder_values); -} - -SQL::ResultSet execute(NonnullRefPtr database, ByteString const& sql, Vector placeholder_values = {}) -{ - auto result = try_execute(move(database), sql, move(placeholder_values)); - if (result.is_error()) { - outln("{}", result.release_error().error_string()); - VERIFY_NOT_REACHED(); - } - return result.release_value(); -} - -template -Vector placeholders(Args&&... args) -{ - return { SQL::Value(forward(args))... }; -} - -void create_schema(NonnullRefPtr database) -{ - auto result = execute(database, "CREATE SCHEMA TestSchema;"); - EXPECT_EQ(result.command(), SQL::SQLCommand::Create); -} - -void create_table(NonnullRefPtr database) -{ - create_schema(database); - auto result = execute(database, "CREATE TABLE TestSchema.TestTable ( TextColumn text, IntColumn integer );"); - EXPECT_EQ(result.command(), SQL::SQLCommand::Create); -} - -void create_two_tables(NonnullRefPtr database) -{ - create_schema(database); - auto result = execute(database, "CREATE TABLE TestSchema.TestTable1 ( TextColumn1 text, IntColumn integer );"); - EXPECT_EQ(result.command(), SQL::SQLCommand::Create); - result = execute(database, "CREATE TABLE TestSchema.TestTable2 ( TextColumn2 text, IntColumn integer );"); - EXPECT_EQ(result.command(), SQL::SQLCommand::Create); -} - -TEST_CASE(create_schema) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_schema(database); - auto schema = MUST(database->get_schema("TESTSCHEMA")); -} - -TEST_CASE(create_table) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto table = MUST(database->get_table("TESTSCHEMA", "TESTTABLE")); -} - -TEST_CASE(insert_into_table) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test', 42 );"); - EXPECT(result.size() == 1); - - auto table = MUST(database->get_table("TESTSCHEMA", "TESTTABLE")); - - int count = 0; - auto rows = TRY_OR_FAIL(database->select_all(*table)); - for (auto& row : rows) { - EXPECT_EQ(row["TEXTCOLUMN"].to_byte_string(), "Test"); - EXPECT_EQ(row["INTCOLUMN"].to_int(), 42); - count++; - } - EXPECT_EQ(count, 1); -} - -TEST_CASE(insert_into_table_wrong_data_types) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES (43, 'Test_2');"); - EXPECT(result.is_error()); - EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidValueType); -} - -TEST_CASE(insert_into_table_multiple_tuples_wrong_data_types) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ('Test_1', 42), (43, 'Test_2');"); - EXPECT(result.is_error()); - EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidValueType); -} - -TEST_CASE(insert_wrong_number_of_values) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( 42 );"); - EXPECT(result.is_error()); - EXPECT(result.release_error().error() == SQL::SQLErrorCode::InvalidNumberOfValues); -} - -TEST_CASE(insert_identifier_as_value) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( identifier, 42 );"); - EXPECT(result.is_error()); - EXPECT(result.release_error().error() == SQL::SQLErrorCode::SyntaxError); -} - -TEST_CASE(insert_quoted_identifier_as_value) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES ( \"QuotedIdentifier\", 42 );"); - EXPECT(result.is_error()); - EXPECT(result.release_error().error() == SQL::SQLErrorCode::SyntaxError); -} - -TEST_CASE(insert_without_column_names) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES ('Test_1', 42), ('Test_2', 43);"); - EXPECT(result.size() == 2); - - auto table = MUST(database->get_table("TESTSCHEMA", "TESTTABLE")); - auto rows = TRY_OR_FAIL(database->select_all(*table)); - EXPECT_EQ(rows.size(), 2u); -} - -TEST_CASE(insert_with_placeholders) -{ - ScopeGuard guard([]() { unlink(db_name); }); - - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - - { - auto result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?);"); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::InvalidNumberOfPlaceholderValues); - - result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?);", placeholders("Test_1"sv)); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::InvalidNumberOfPlaceholderValues); - - result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?);", placeholders(42, 42)); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::InvalidValueType); - - result = try_execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?);", placeholders("Test_1"sv, "Test_2"sv)); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::InvalidValueType); - } - { - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?);", placeholders("Test_1"sv, 42)); - EXPECT_EQ(result.size(), 1u); - - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); - EXPECT_EQ(result.size(), 1u); - - EXPECT_EQ(result[0].row[0], "Test_1"sv); - EXPECT_EQ(result[0].row[1], 42); - } - { - auto result = execute(database, "INSERT INTO TestSchema.TestTable VALUES (?, ?), (?, ?);", placeholders("Test_2"sv, 43, "Test_3"sv, 44)); - EXPECT_EQ(result.size(), 2u); - - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); - EXPECT_EQ(result.size(), 3u); - - EXPECT_EQ(result[0].row[0], "Test_1"sv); - EXPECT_EQ(result[0].row[1], 42); - - EXPECT_EQ(result[1].row[0], "Test_2"sv); - EXPECT_EQ(result[1].row[1], 43); - - EXPECT_EQ(result[2].row[0], "Test_3"sv); - EXPECT_EQ(result[2].row[1], 44); - } -} - -TEST_CASE(select_from_empty_table) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT(result.is_empty()); -} - -TEST_CASE(select_from_table) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 5u); -} - -TEST_CASE(select_with_column_names) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 5u); - EXPECT_EQ(result[0].row.size(), 1u); -} - -TEST_CASE(select_with_nonexisting_column_name) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - - auto insert_result = try_execute(database, "SELECT Bogus FROM TestSchema.TestTable;"); - EXPECT(insert_result.is_error()); - EXPECT(insert_result.release_error().error() == SQL::SQLErrorCode::ColumnDoesNotExist); -} - -TEST_CASE(select_with_where) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable WHERE IntColumn > 44;"); - EXPECT_EQ(result.size(), 2u); - for (auto& row : result) { - EXPECT(row.row[1].to_int().value() > 44); - } -} - -TEST_CASE(select_cross_join) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_two_tables(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable1 ( TextColumn1, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - result = execute(database, - "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " - "( 'Test_10', 40 ), " - "( 'Test_11', 41 ), " - "( 'Test_12', 42 ), " - "( 'Test_13', 47 ), " - "( 'Test_14', 48 );"); - EXPECT(result.size() == 5); - result = execute(database, "SELECT * FROM TestSchema.TestTable1, TestSchema.TestTable2;"); - EXPECT_EQ(result.size(), 25u); - for (auto& row : result) { - EXPECT(row.row.size() == 4); - EXPECT(row.row[1].to_int().value() >= 42); - EXPECT(row.row[1].to_int().value() <= 46); - EXPECT(row.row[3].to_int().value() >= 40); - EXPECT(row.row[3].to_int().value() <= 48); - } -} - -TEST_CASE(select_inner_join) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_two_tables(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable1 ( TextColumn1, IntColumn ) VALUES " - "( 'Test_1', 42 ), " - "( 'Test_2', 43 ), " - "( 'Test_3', 44 ), " - "( 'Test_4', 45 ), " - "( 'Test_5', 46 );"); - EXPECT(result.size() == 5); - result = execute(database, - "INSERT INTO TestSchema.TestTable2 ( TextColumn2, IntColumn ) VALUES " - "( 'Test_10', 40 ), " - "( 'Test_11', 41 ), " - "( 'Test_12', 42 ), " - "( 'Test_13', 47 ), " - "( 'Test_14', 48 );"); - EXPECT(result.size() == 5); - result = execute(database, - "SELECT TestTable1.IntColumn, TextColumn1, TextColumn2 " - "FROM TestSchema.TestTable1, TestSchema.TestTable2 " - "WHERE TestTable1.IntColumn = TestTable2.IntColumn;"); - EXPECT_EQ(result.size(), 1u); - EXPECT_EQ(result[0].row.size(), 3u); - EXPECT_EQ(result[0].row[0].to_int(), 42); - EXPECT_EQ(result[0].row[1].to_byte_string(), "Test_1"); - EXPECT_EQ(result[0].row[2].to_byte_string(), "Test_12"); -} - -TEST_CASE(select_with_like) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test+1', 42 ), " - "( 'Test+2', 43 ), " - "( 'Test+3', 44 ), " - "( 'Test+4', 45 ), " - "( 'Test+5', 46 ), " - "( 'Another+Test_6', 47 );"); - EXPECT(result.size() == 6); - - // Simple match - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'Test+1';"); - EXPECT_EQ(result.size(), 1u); - - // Use % to match most rows - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'T%';"); - EXPECT_EQ(result.size(), 5u); - - // Same as above but invert the match - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn NOT LIKE 'T%';"); - EXPECT_EQ(result.size(), 1u); - - // Use _ and % to match all rows - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%e_t%';"); - EXPECT_EQ(result.size(), 6u); - - // Use escape to match a single row. The escape character happens to be a - // Regex metacharacter, let's make sure we don't get confused by that. - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%Test^_%' ESCAPE '^';"); - EXPECT_EQ(result.size(), 1u); - - // Same as above but escape the escape character happens to be a SQL - // metacharacter - we want to make sure it's treated as an escape. - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%Test__%' ESCAPE '_';"); - EXPECT_EQ(result.size(), 1u); - - // (Unnecessarily) escaping a character that happens to be a Regex - // metacharacter should have no effect. - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE 'Test:+_' ESCAPE ':';"); - EXPECT_EQ(result.size(), 5u); - - // Make sure we error out if the ESCAPE is empty - auto select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE '';"); - EXPECT(select_result.is_error()); - EXPECT(select_result.release_error().error() == SQL::SQLErrorCode::SyntaxError); - - // Make sure we error out if the ESCAPE has more than a single character - select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn LIKE '%' ESCAPE 'whf';"); - EXPECT(select_result.is_error()); - EXPECT(select_result.release_error().error() == SQL::SQLErrorCode::SyntaxError); -} - -TEST_CASE(select_with_order) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_5', 44 ), " - "( 'Test_2', 42 ), " - "( 'Test_1', 47 ), " - "( 'Test_3', 40 ), " - "( 'Test_4', 41 );"); - EXPECT(result.size() == 5); - - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 5u); - EXPECT_EQ(result[0].row[1].to_int(), 40); - EXPECT_EQ(result[1].row[1].to_int(), 41); - EXPECT_EQ(result[2].row[1].to_int(), 42); - EXPECT_EQ(result[3].row[1].to_int(), 44); - EXPECT_EQ(result[4].row[1].to_int(), 47); - - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn;"); - EXPECT_EQ(result.size(), 5u); - EXPECT_EQ(result[0].row[0].to_byte_string(), "Test_1"); - EXPECT_EQ(result[1].row[0].to_byte_string(), "Test_2"); - EXPECT_EQ(result[2].row[0].to_byte_string(), "Test_3"); - EXPECT_EQ(result[3].row[0].to_byte_string(), "Test_4"); - EXPECT_EQ(result[4].row[0].to_byte_string(), "Test_5"); -} - -TEST_CASE(select_with_regexp) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test+1', 42 ), " - "( 'Pröv+2', 43 ), " - "( 'Test(3)', 44 ), " - "( 'Test[4]', 45 ), " - "( 'Test+5', 46 ), " - "( 'Another-Test_6', 47 );"); - EXPECT(result.size() == 6); - - // Simple match - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+1';"); - EXPECT_EQ(result.size(), 1u); - - // Match all - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '.*';"); - EXPECT_EQ(result.size(), 6u); - - // Match with wildcards - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP '^Test.+';"); - EXPECT_EQ(result.size(), 4u); - - // Match with case insensitive basic Latin and case sensitive Swedish ö - // FIXME: If LibRegex is changed to support case insensitive matches of Unicode characters - // This test should be updated and changed to match 'PRÖV'. - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'PRöV.*';"); - EXPECT_EQ(result.size(), 1u); -} - -TEST_CASE(handle_regexp_errors) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test', 0 );"); - EXPECT(result.size() == 1); - - // Malformed regex, unmatched square bracket - auto select_result = try_execute(database, "SELECT TextColumn FROM TestSchema.TestTable WHERE TextColumn REGEXP 'Test\\+[0-9.*';"); - EXPECT(select_result.is_error()); -} - -TEST_CASE(select_with_order_two_columns) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_5', 44 ), " - "( 'Test_2', 42 ), " - "( 'Test_1', 47 ), " - "( 'Test_2', 40 ), " - "( 'Test_4', 41 );"); - EXPECT(result.size() == 5); - - result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY TextColumn, IntColumn;"); - EXPECT_EQ(result.size(), 5u); - EXPECT_EQ(result[0].row[0].to_byte_string(), "Test_1"); - EXPECT_EQ(result[0].row[1].to_int(), 47); - EXPECT_EQ(result[1].row[0].to_byte_string(), "Test_2"); - EXPECT_EQ(result[1].row[1].to_int(), 40); - EXPECT_EQ(result[2].row[0].to_byte_string(), "Test_2"); - EXPECT_EQ(result[2].row[1].to_int(), 42); - EXPECT_EQ(result[3].row[0].to_byte_string(), "Test_4"); - EXPECT_EQ(result[3].row[1].to_int(), 41); - EXPECT_EQ(result[4].row[0].to_byte_string(), "Test_5"); - EXPECT_EQ(result[4].row[1].to_int(), 44); -} - -TEST_CASE(select_with_order_by_column_not_in_result) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, - "INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES " - "( 'Test_5', 44 ), " - "( 'Test_2', 42 ), " - "( 'Test_1', 47 ), " - "( 'Test_3', 40 ), " - "( 'Test_4', 41 );"); - EXPECT(result.size() == 5); - - result = execute(database, "SELECT TextColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 5u); - EXPECT_EQ(result[0].row[0].to_byte_string(), "Test_3"); - EXPECT_EQ(result[1].row[0].to_byte_string(), "Test_4"); - EXPECT_EQ(result[2].row[0].to_byte_string(), "Test_2"); - EXPECT_EQ(result[3].row[0].to_byte_string(), "Test_5"); - EXPECT_EQ(result[4].row[0].to_byte_string(), "Test_1"); -} - -TEST_CASE(select_with_limit) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - for (auto count = 0; count < 100; count++) { - auto result = execute(database, - ByteString::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(result.size() == 1); - } - auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10;"); - auto rows = result; - EXPECT_EQ(rows.size(), 10u); -} - -TEST_CASE(select_with_limit_and_offset) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - for (auto count = 0; count < 100; count++) { - auto result = execute(database, - ByteString::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(result.size() == 1); - } - auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 10;"); - EXPECT_EQ(result.size(), 10u); -} - -TEST_CASE(select_with_order_limit_and_offset) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - for (auto count = 0; count < 100; count++) { - auto result = execute(database, - ByteString::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(result.size() == 1); - } - auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable ORDER BY IntColumn LIMIT 10 OFFSET 10;"); - EXPECT_EQ(result.size(), 10u); - EXPECT_EQ(result[0].row[1].to_int(), 10); - EXPECT_EQ(result[1].row[1].to_int(), 11); - EXPECT_EQ(result[2].row[1].to_int(), 12); - EXPECT_EQ(result[3].row[1].to_int(), 13); - EXPECT_EQ(result[4].row[1].to_int(), 14); - EXPECT_EQ(result[5].row[1].to_int(), 15); - EXPECT_EQ(result[6].row[1].to_int(), 16); - EXPECT_EQ(result[7].row[1].to_int(), 17); - EXPECT_EQ(result[8].row[1].to_int(), 18); - EXPECT_EQ(result[9].row[1].to_int(), 19); -} - -TEST_CASE(select_with_limit_out_of_bounds) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - for (auto count = 0; count < 100; count++) { - auto result = execute(database, - ByteString::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(result.size() == 1); - } - auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 500;"); - EXPECT_EQ(result.size(), 100u); -} - -TEST_CASE(select_with_offset_out_of_bounds) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - for (auto count = 0; count < 100; count++) { - auto result = execute(database, - ByteString::formatted("INSERT INTO TestSchema.TestTable ( TextColumn, IntColumn ) VALUES ( 'Test_{}', {} );", count, count)); - EXPECT(result.size() == 1); - } - auto result = execute(database, "SELECT TextColumn, IntColumn FROM TestSchema.TestTable LIMIT 10 OFFSET 200;"); - EXPECT_EQ(result.size(), 0u); -} - -TEST_CASE(describe_table) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - auto result = execute(database, "DESCRIBE TABLE TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 2u); - - EXPECT_EQ(result[0].row[0].to_byte_string(), "TEXTCOLUMN"); - EXPECT_EQ(result[0].row[1].to_byte_string(), "text"); - - EXPECT_EQ(result[1].row[0].to_byte_string(), "INTCOLUMN"); - EXPECT_EQ(result[1].row[1].to_byte_string(), "int"); -} - -TEST_CASE(binary_operator_execution) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - auto compare_result = [](SQL::ResultSet const& result, Vector const& expected) { - EXPECT_EQ(result.command(), SQL::SQLCommand::Select); - EXPECT_EQ(result.size(), expected.size()); - - Vector result_values; - result_values.ensure_capacity(result.size()); - - for (size_t i = 0; i < result.size(); ++i) { - auto const& result_row = result.at(i).row; - EXPECT_EQ(result_row.size(), 1u); - - auto result_column = result_row[0].to_int(); - result_values.append(result_column.value()); - } - - quick_sort(result_values); - EXPECT_EQ(result_values, expected); - }; - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn + 1) < 5);"); - compare_result(result, { 0, 1, 2, 3 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn + 1) <= 5);"); - compare_result(result, { 0, 1, 2, 3, 4 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn - 1) > 4);"); - compare_result(result, { 6, 7, 8, 9 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn - 1) >= 4);"); - compare_result(result, { 5, 6, 7, 8, 9 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn * 2) < 10);"); - compare_result(result, { 0, 1, 2, 3, 4 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn * 2) <= 10);"); - compare_result(result, { 0, 1, 2, 3, 4, 5 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn / 3) > 2);"); - compare_result(result, { 7, 8, 9 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn / 3) >= 2);"); - compare_result(result, { 6, 7, 8, 9 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn % 2) = 0);"); - compare_result(result, { 0, 2, 4, 6, 8 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn % 2) = 1);"); - compare_result(result, { 1, 3, 5, 7, 9 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((1 << IntColumn) <= 32);"); - compare_result(result, { 0, 1, 2, 3, 4, 5 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((1024 >> IntColumn) >= 32);"); - compare_result(result, { 0, 1, 2, 3, 4, 5 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn | 1) != IntColumn);"); - compare_result(result, { 0, 2, 4, 6, 8 }); - - result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable WHERE ((IntColumn & 1) = 1);"); - compare_result(result, { 1, 3, 5, 7, 9 }); -} - -TEST_CASE(binary_operator_failure) -{ - ScopeGuard guard([]() { unlink(db_name); }); - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - create_table(database); - - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - auto expect_failure = [](auto result, auto op) { - EXPECT(result.is_error()); - - auto error = result.release_error(); - EXPECT_EQ(error.error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - - auto message = ByteString::formatted("NumericOperatorTypeMismatch: Cannot apply '{}' operator to non-numeric operands", op); - EXPECT_EQ(error.error_string(), message); - }; - - auto result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn + TextColumn) < 5);"); - expect_failure(move(result), '+'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn - TextColumn) < 5);"); - expect_failure(move(result), '-'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn * TextColumn) < 5);"); - expect_failure(move(result), '*'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn / TextColumn) < 5);"); - expect_failure(move(result), '/'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn % TextColumn) < 5);"); - expect_failure(move(result), '%'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn << TextColumn) < 5);"); - expect_failure(move(result), "<<"sv); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn >> TextColumn) < 5);"); - expect_failure(move(result), ">>"sv); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn | TextColumn) < 5);"); - expect_failure(move(result), '|'); - - result = try_execute(database, "SELECT * FROM TestSchema.TestTable WHERE ((IntColumn & TextColumn) < 5);"); - expect_failure(move(result), '&'); -} - -TEST_CASE(describe_large_table_after_persist) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "CREATE TABLE Cookies ( name TEXT, value TEXT, same_site INTEGER, creation_time INTEGER, last_access_time INTEGER, expiry_time INTEGER, domain TEXT, path TEXT, secure INTEGER, http_only INTEGER, host_only INTEGER, persistent INTEGER );"); - EXPECT_EQ(result.command(), SQL::SQLCommand::Create); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "DESCRIBE TABLE Cookies;"); - EXPECT_EQ(result.size(), 12u); - } -} - -TEST_CASE(delete_single_row) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 10u); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - execute(database, "DELETE FROM TestSchema.TestTable WHERE (IntColumn = 4);"); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 9u); - - for (auto i = 0u; i < 4; ++i) - EXPECT_EQ(result[i].row[0], i); - for (auto i = 5u; i < 9; ++i) - EXPECT_EQ(result[i].row[0], i + 1); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 9u); - - for (auto i = 0u; i < 4; ++i) - EXPECT_EQ(result[i].row[0], i); - for (auto i = 5u; i < 9; ++i) - EXPECT_EQ(result[i].row[0], i + 1); - } -} - -TEST_CASE(delete_multiple_rows) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 10u); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - execute(database, "DELETE FROM TestSchema.TestTable WHERE (IntColumn >= 4);"); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 4u); - - for (auto i = 0u; i < result.size(); ++i) - EXPECT_EQ(result[i].row[0], i); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 4u); - - for (auto i = 0u; i < result.size(); ++i) - EXPECT_EQ(result[i].row[0], i); - } -} - -TEST_CASE(delete_all_rows) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT_EQ(result.size(), 10u); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - execute(database, "DELETE FROM TestSchema.TestTable;"); - - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT(result.is_empty()); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT * FROM TestSchema.TestTable;"); - EXPECT(result.is_empty()); - } -} - -TEST_CASE(update_single_row) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - execute(database, "UPDATE TestSchema.TestTable SET IntColumn=123456 WHERE (TextColumn = 'T3');"); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) { - if (i < 3) - EXPECT_EQ(result[i].row[0], i); - else if (i < 9) - EXPECT_EQ(result[i].row[0], i + 1); - else - EXPECT_EQ(result[i].row[0], 123456); - } - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) { - if (i < 3) - EXPECT_EQ(result[i].row[0], i); - else if (i < 9) - EXPECT_EQ(result[i].row[0], i + 1); - else - EXPECT_EQ(result[i].row[0], 123456); - } - } -} - -TEST_CASE(update_multiple_rows) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - execute(database, "UPDATE TestSchema.TestTable SET IntColumn=123456 WHERE (IntColumn > 4);"); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) { - if (i < 5) - EXPECT_EQ(result[i].row[0], i); - else - EXPECT_EQ(result[i].row[0], 123456); - } - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) { - if (i < 5) - EXPECT_EQ(result[i].row[0], i); - else - EXPECT_EQ(result[i].row[0], 123456); - } - } -} - -TEST_CASE(update_all_rows) -{ - ScopeGuard guard([]() { unlink(db_name); }); - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - create_table(database); - for (auto count = 0; count < 10; ++count) { - auto result = execute(database, ByteString::formatted("INSERT INTO TestSchema.TestTable VALUES ( 'T{}', {} );", count, count)); - EXPECT_EQ(result.size(), 1u); - } - - execute(database, "UPDATE TestSchema.TestTable SET IntColumn=123456;"); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) - EXPECT_EQ(result[i].row[0], 123456); - } - { - auto database = MUST(SQL::Database::create(db_name)); - MUST(database->open()); - - auto result = execute(database, "SELECT IntColumn FROM TestSchema.TestTable ORDER BY IntColumn;"); - EXPECT_EQ(result.size(), 10u); - - for (auto i = 0u; i < 10; ++i) - EXPECT_EQ(result[i].row[0], 123456); - } -} - -} diff --git a/Tests/LibSQL/TestSqlStatementParser.cpp b/Tests/LibSQL/TestSqlStatementParser.cpp deleted file mode 100644 index 8e03c00bf2d..00000000000 --- a/Tests/LibSQL/TestSqlStatementParser.cpp +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (c) 2021, Tim Flynn - * Copyright (c) 2021, Jan de Visser - * Copyright (c) 2021, Mahmoud Mandour - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -using ParseResult = AK::Result, ByteString>; - -ParseResult parse(StringView sql) -{ - auto parser = SQL::AST::Parser(SQL::AST::Lexer(sql)); - auto statement = parser.next_statement(); - - if (parser.has_errors()) { - return parser.errors()[0].to_byte_string(); - } - - return statement; -} - -} - -TEST_CASE(create_table) -{ - EXPECT(parse("CREATE TABLE"sv).is_error()); - EXPECT(parse("CREATE TABLE test"sv).is_error()); - EXPECT(parse("CREATE TABLE test ()"sv).is_error()); - EXPECT(parse("CREATE TABLE test ();"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 "sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 )"sv).is_error()); - EXPECT(parse("CREATE TABLE IF test ( column1 );"sv).is_error()); - EXPECT(parse("CREATE TABLE IF NOT test ( column1 );"sv).is_error()); - EXPECT(parse("CREATE TABLE AS;"sv).is_error()); - EXPECT(parse("CREATE TABLE AS SELECT;"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar()"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(abc)"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(123 )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(123, )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(123, ) )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(.) )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(.abc) )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(0x) )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 varchar(0xzzz) )"sv).is_error()); - EXPECT(parse("CREATE TABLE test ( column1 int ) AS SELECT * FROM table_name;"sv).is_error()); - EXPECT(parse("CREATE TABLE test AS SELECT * FROM table_name ( column1 int ) ;"sv).is_error()); - - struct Column { - StringView name; - StringView type; - Vector signed_numbers {}; - }; - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, Vector expected_columns, bool expected_is_temporary = false, bool expected_is_error_if_table_exists = true) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& table = static_cast(*statement); - EXPECT_EQ(table.schema_name(), expected_schema); - EXPECT_EQ(table.table_name(), expected_table); - EXPECT_EQ(table.is_temporary(), expected_is_temporary); - EXPECT_EQ(table.is_error_if_table_exists(), expected_is_error_if_table_exists); - - bool expect_select_statement = expected_columns.is_empty(); - EXPECT_EQ(table.has_selection(), expect_select_statement); - EXPECT_EQ(table.has_columns(), !expect_select_statement); - - auto const& select_statement = table.select_statement(); - EXPECT_EQ(select_statement.is_null(), !expect_select_statement); - - auto const& columns = table.columns(); - EXPECT_EQ(columns.size(), expected_columns.size()); - - for (size_t i = 0; i < columns.size(); ++i) { - auto const& column = columns[i]; - auto const& expected_column = expected_columns[i]; - EXPECT_EQ(column->name(), expected_column.name); - - auto const& type_name = column->type_name(); - EXPECT_EQ(type_name->name(), expected_column.type); - - auto const& signed_numbers = type_name->signed_numbers(); - EXPECT_EQ(signed_numbers.size(), expected_column.signed_numbers.size()); - - for (size_t j = 0; j < signed_numbers.size(); ++j) { - double signed_number = signed_numbers[j]->value(); - double expected_signed_number = expected_column.signed_numbers[j]; - EXPECT_EQ(signed_number, expected_signed_number); - } - } - }; - - validate("CREATE TABLE test ( column1 );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }); - validate("Create Table test ( column1 );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }); - validate(R"(CREATE TABLE "test" ( "column1" );)"sv, {}, "test"sv, { { "column1"sv, "BLOB"sv } }); - validate(R"(CREATE TABLE "te""st" ( "co""lumn1" );)"sv, {}, "te\"st"sv, { { "co\"lumn1"sv, "BLOB"sv } }); - validate("CREATE TABLE schema_name.test ( column1 );"sv, "SCHEMA_NAME"sv, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }); - validate("CREATE TABLE \"schema\".test ( column1 );"sv, "schema"sv, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }); - validate("CREATE TEMP TABLE test ( column1 );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }, true, true); - validate("CREATE TEMPORARY TABLE test ( column1 );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }, true, true); - validate("CREATE TABLE IF NOT EXISTS test ( column1 );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "BLOB"sv } }, false, false); - - validate("CREATE TABLE test AS SELECT * FROM table_name;"sv, {}, "TEST"sv, {}); - - validate("CREATE TABLE test ( column1 int );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "INT"sv } }); - validate("CREATE TABLE test ( column1 varchar );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv } }); - validate("CREATE TABLE test ( column1 varchar(255) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 255 } } }); - validate("CREATE TABLE test ( column1 varchar(255, 123) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 255, 123 } } }); - validate("CREATE TABLE test ( column1 varchar(255, -123) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 255, -123 } } }); - validate("CREATE TABLE test ( column1 varchar(0xff) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 255 } } }); - validate("CREATE TABLE test ( column1 varchar(3.14) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 3.14 } } }); - validate("CREATE TABLE test ( column1 varchar(1e3) );"sv, {}, "TEST"sv, { { "COLUMN1"sv, "VARCHAR"sv, { 1000 } } }); -} - -TEST_CASE(alter_table) -{ - // This test case only contains common error cases of the AlterTable subclasses. - EXPECT(parse("ALTER"sv).is_error()); - EXPECT(parse("ALTER TABLE"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name;"sv).is_error()); -} - -TEST_CASE(alter_table_rename_table) -{ - EXPECT(parse("ALTER TABLE table_name RENAME"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME TO"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME TO new_table"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_new_table) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& alter = static_cast(*statement); - EXPECT_EQ(alter.schema_name(), expected_schema); - EXPECT_EQ(alter.table_name(), expected_table); - EXPECT_EQ(alter.new_table_name(), expected_new_table); - }; - - validate("ALTER TABLE table_name RENAME TO new_table;"sv, {}, "TABLE_NAME"sv, "NEW_TABLE"sv); - validate("ALTER TABLE schema_name.table_name RENAME TO new_table;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "NEW_TABLE"sv); -} - -TEST_CASE(alter_table_rename_column) -{ - EXPECT(parse("ALTER TABLE table_name RENAME"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME COLUMN"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME COLUMN column_name"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME COLUMN column_name TO"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME COLUMN column_name TO new_column"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME column_name"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME column_name TO"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name RENAME column_name TO new_column"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_column, StringView expected_new_column) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& alter = static_cast(*statement); - EXPECT_EQ(alter.schema_name(), expected_schema); - EXPECT_EQ(alter.table_name(), expected_table); - EXPECT_EQ(alter.column_name(), expected_column); - EXPECT_EQ(alter.new_column_name(), expected_new_column); - }; - - validate("ALTER TABLE table_name RENAME column_name TO new_column;"sv, {}, "TABLE_NAME"sv, "COLUMN_NAME"sv, "NEW_COLUMN"sv); - validate("ALTER TABLE table_name RENAME COLUMN column_name TO new_column;"sv, {}, "TABLE_NAME"sv, "COLUMN_NAME"sv, "NEW_COLUMN"sv); - validate("ALTER TABLE schema_name.table_name RENAME column_name TO new_column;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "COLUMN_NAME"sv, "NEW_COLUMN"sv); - validate("ALTER TABLE schema_name.table_name RENAME COLUMN column_name TO new_column;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "COLUMN_NAME"sv, "NEW_COLUMN"sv); -} - -TEST_CASE(alter_table_add_column) -{ - EXPECT(parse("ALTER TABLE table_name ADD"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name ADD COLUMN"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name ADD COLUMN column_name"sv).is_error()); - - struct Column { - StringView name; - StringView type; - Vector signed_numbers {}; - }; - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, Column expected_column) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& alter = static_cast(*statement); - EXPECT_EQ(alter.schema_name(), expected_schema); - EXPECT_EQ(alter.table_name(), expected_table); - - auto const& column = alter.column(); - EXPECT_EQ(column->name(), expected_column.name); - - auto const& type_name = column->type_name(); - EXPECT_EQ(type_name->name(), expected_column.type); - - auto const& signed_numbers = type_name->signed_numbers(); - EXPECT_EQ(signed_numbers.size(), expected_column.signed_numbers.size()); - - for (size_t j = 0; j < signed_numbers.size(); ++j) { - double signed_number = signed_numbers[j]->value(); - double expected_signed_number = expected_column.signed_numbers[j]; - EXPECT_EQ(signed_number, expected_signed_number); - } - }; - - validate("ALTER TABLE test ADD column1;"sv, {}, "TEST"sv, { "COLUMN1"sv, "BLOB"sv }); - validate("ALTER TABLE test ADD column1 int;"sv, {}, "TEST"sv, { "COLUMN1"sv, "INT"sv }); - validate("ALTER TABLE test ADD column1 varchar;"sv, {}, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv }); - validate("ALTER TABLE test ADD column1 varchar(255);"sv, {}, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv, { 255 } }); - validate("ALTER TABLE test ADD column1 varchar(255, 123);"sv, {}, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv, { 255, 123 } }); - - validate("ALTER TABLE schema_name.test ADD COLUMN column1;"sv, "SCHEMA_NAME"sv, "TEST"sv, { "COLUMN1"sv, "BLOB"sv }); - validate("ALTER TABLE schema_name.test ADD COLUMN column1 int;"sv, "SCHEMA_NAME"sv, "TEST"sv, { "COLUMN1"sv, "INT"sv }); - validate("ALTER TABLE schema_name.test ADD COLUMN column1 varchar;"sv, "SCHEMA_NAME"sv, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv }); - validate("ALTER TABLE schema_name.test ADD COLUMN column1 varchar(255);"sv, "SCHEMA_NAME"sv, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv, { 255 } }); - validate("ALTER TABLE schema_name.test ADD COLUMN column1 varchar(255, 123);"sv, "SCHEMA_NAME"sv, "TEST"sv, { "COLUMN1"sv, "VARCHAR"sv, { 255, 123 } }); -} - -TEST_CASE(alter_table_drop_column) -{ - EXPECT(parse("ALTER TABLE table_name DROP"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name DROP COLUMN"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name DROP column_name"sv).is_error()); - EXPECT(parse("ALTER TABLE table_name DROP COLUMN column_name"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_column) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& alter = static_cast(*statement); - EXPECT_EQ(alter.schema_name(), expected_schema); - EXPECT_EQ(alter.table_name(), expected_table); - EXPECT_EQ(alter.column_name(), expected_column); - }; - - validate("ALTER TABLE table_name DROP column_name;"sv, {}, "TABLE_NAME"sv, "COLUMN_NAME"sv); - validate("ALTER TABLE table_name DROP COLUMN column_name;"sv, {}, "TABLE_NAME"sv, "COLUMN_NAME"sv); - validate("ALTER TABLE schema_name.table_name DROP column_name;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "COLUMN_NAME"sv); - validate("ALTER TABLE schema_name.table_name DROP COLUMN column_name;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "COLUMN_NAME"sv); -} - -TEST_CASE(drop_table) -{ - EXPECT(parse("DROP"sv).is_error()); - EXPECT(parse("DROP TABLE"sv).is_error()); - EXPECT(parse("DROP TABLE test"sv).is_error()); - EXPECT(parse("DROP TABLE IF test;"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, bool expected_is_error_if_table_does_not_exist = true) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& table = static_cast(*statement); - EXPECT_EQ(table.schema_name(), expected_schema); - EXPECT_EQ(table.table_name(), expected_table); - EXPECT_EQ(table.is_error_if_table_does_not_exist(), expected_is_error_if_table_does_not_exist); - }; - - validate("DROP TABLE test;"sv, {}, "TEST"sv); - validate("DROP TABLE schema_name.test;"sv, "SCHEMA_NAME"sv, "TEST"sv); - validate("DROP TABLE IF EXISTS test;"sv, {}, "TEST"sv, false); -} - -TEST_CASE(insert) -{ - EXPECT(parse("INSERT"sv).is_error()); - EXPECT(parse("INSERT INTO"sv).is_error()); - EXPECT(parse("INSERT INTO table_name"sv).is_error()); - EXPECT(parse("INSERT INTO table_name (column_name)"sv).is_error()); - EXPECT(parse("INSERT INTO table_name (column_name, ) DEFAULT VALUES;"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES ();"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES (1)"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES SELECT"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES EXISTS"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES NOT"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES EXISTS (SELECT 1)"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES (SELECT)"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES (EXISTS SELECT)"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES ((SELECT))"sv).is_error()); - EXPECT(parse("INSERT INTO table_name VALUES (EXISTS (SELECT))"sv).is_error()); - EXPECT(parse("INSERT INTO table_name SELECT"sv).is_error()); - EXPECT(parse("INSERT INTO table_name SELECT * from table_name"sv).is_error()); - EXPECT(parse("INSERT OR INTO table_name DEFAULT VALUES;"sv).is_error()); - EXPECT(parse("INSERT OR foo INTO table_name DEFAULT VALUES;"sv).is_error()); - - auto validate = [](StringView sql, SQL::AST::ConflictResolution expected_conflict_resolution, StringView expected_schema, StringView expected_table, StringView expected_alias, Vector expected_column_names, Vector expected_chain_sizes, bool expect_select_statement) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& insert = static_cast(*statement); - EXPECT_EQ(insert.conflict_resolution(), expected_conflict_resolution); - EXPECT_EQ(insert.schema_name(), expected_schema); - EXPECT_EQ(insert.table_name(), expected_table); - EXPECT_EQ(insert.alias(), expected_alias); - - auto const& column_names = insert.column_names(); - EXPECT_EQ(column_names.size(), expected_column_names.size()); - for (size_t i = 0; i < column_names.size(); ++i) - EXPECT_EQ(column_names[i], expected_column_names[i]); - - EXPECT_EQ(insert.has_expressions(), !expected_chain_sizes.is_empty()); - if (insert.has_expressions()) { - auto const& chained_expressions = insert.chained_expressions(); - EXPECT_EQ(chained_expressions.size(), expected_chain_sizes.size()); - - for (size_t i = 0; i < chained_expressions.size(); ++i) { - auto const& chained_expression = chained_expressions[i]; - auto const& expressions = chained_expression->expressions(); - EXPECT_EQ(expressions.size(), expected_chain_sizes[i]); - - for (auto const& expression : expressions) - EXPECT(!is(expression)); - } - } - - EXPECT_EQ(insert.has_selection(), expect_select_statement); - EXPECT_EQ(insert.default_values(), expected_chain_sizes.is_empty() && !expect_select_statement); - }; - - validate("INSERT OR ABORT INTO table_name DEFAULT VALUES;"sv, SQL::AST::ConflictResolution::Abort, {}, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT OR FAIL INTO table_name DEFAULT VALUES;"sv, SQL::AST::ConflictResolution::Fail, {}, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT OR IGNORE INTO table_name DEFAULT VALUES;"sv, SQL::AST::ConflictResolution::Ignore, {}, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT OR REPLACE INTO table_name DEFAULT VALUES;"sv, SQL::AST::ConflictResolution::Replace, {}, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT OR ROLLBACK INTO table_name DEFAULT VALUES;"sv, SQL::AST::ConflictResolution::Rollback, {}, "TABLE_NAME"sv, {}, {}, {}, false); - - auto resolution = SQL::AST::ConflictResolution::Abort; - validate("INSERT INTO table_name DEFAULT VALUES;"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT INTO schema_name.table_name DEFAULT VALUES;"sv, resolution, "SCHEMA_NAME"sv, "TABLE_NAME"sv, {}, {}, {}, false); - validate("INSERT INTO table_name AS foo DEFAULT VALUES;"sv, resolution, {}, "TABLE_NAME"sv, "FOO"sv, {}, {}, false); - - validate("INSERT INTO table_name (column_name) DEFAULT VALUES;"sv, resolution, {}, "TABLE_NAME"sv, {}, { "COLUMN_NAME"sv }, {}, false); - validate("INSERT INTO table_name (column1, column2) DEFAULT VALUES;"sv, resolution, {}, "TABLE_NAME"sv, {}, { "COLUMN1"sv, "COLUMN2"sv }, {}, false); - - validate("INSERT INTO table_name VALUES (1);"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 1 }, false); - validate("INSERT INTO table_name VALUES (1, 2);"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 2 }, false); - validate("INSERT INTO table_name VALUES (1, 2), (3, 4, 5);"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 2, 3 }, false); - - validate("INSERT INTO table_name VALUES ((SELECT 1));"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 1 }, false); - validate("INSERT INTO table_name VALUES (EXISTS (SELECT 1));"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 1 }, false); - validate("INSERT INTO table_name VALUES (NOT EXISTS (SELECT 1));"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 1 }, false); - validate("INSERT INTO table_name VALUES ((SELECT 1), (SELECT 1));"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 2 }, false); - validate("INSERT INTO table_name VALUES ((SELECT 1), (SELECT 1)), ((SELECT 1), (SELECT 1), (SELECT 1));"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, { 2, 3 }, false); - - validate("INSERT INTO table_name SELECT * FROM table_name;"sv, resolution, {}, "TABLE_NAME"sv, {}, {}, {}, true); -} - -TEST_CASE(update) -{ - EXPECT(parse("UPDATE"sv).is_error()); - EXPECT(parse("UPDATE table_name"sv).is_error()); - EXPECT(parse("UPDATE table_name SET"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4, ;"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=4"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=EXISTS"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=SELECT"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=(SELECT)"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=NOT (SELECT 1)"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name)=4, ;"sv).is_error()); - EXPECT(parse("UPDATE table_name SET (column_name, )=4;"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 FROM"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 FROM table_name"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE EXISTS"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE NOT"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE NOT EXISTS"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE SELECT"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE (SELECT)"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE NOT (SELECT)"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 WHERE 1==1"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 RETURNING"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 RETURNING *"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 RETURNING column_name"sv).is_error()); - EXPECT(parse("UPDATE table_name SET column_name=4 RETURNING column_name AS"sv).is_error()); - EXPECT(parse("UPDATE OR table_name SET column_name=4;"sv).is_error()); - EXPECT(parse("UPDATE OR foo table_name SET column_name=4;"sv).is_error()); - - auto validate = [](StringView sql, SQL::AST::ConflictResolution expected_conflict_resolution, StringView expected_schema, StringView expected_table, StringView expected_alias, Vector> expected_update_columns, bool expect_where_clause, bool expect_returning_clause, Vector expected_returned_column_aliases) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& update = static_cast(*statement); - EXPECT_EQ(update.conflict_resolution(), expected_conflict_resolution); - - auto const& qualified_table_name = update.qualified_table_name(); - EXPECT_EQ(qualified_table_name->schema_name(), expected_schema); - EXPECT_EQ(qualified_table_name->table_name(), expected_table); - EXPECT_EQ(qualified_table_name->alias(), expected_alias); - - auto const& update_columns = update.update_columns(); - EXPECT_EQ(update_columns.size(), expected_update_columns.size()); - for (size_t i = 0; i < update_columns.size(); ++i) { - auto const& update_column = update_columns[i]; - auto const& expected_update_column = expected_update_columns[i]; - EXPECT_EQ(update_column.column_names.size(), expected_update_column.size()); - EXPECT(!is(*update_column.expression)); - - for (size_t j = 0; j < update_column.column_names.size(); ++j) - EXPECT_EQ(update_column.column_names[j], expected_update_column[j]); - } - - auto const& where_clause = update.where_clause(); - EXPECT_EQ(where_clause.is_null(), !expect_where_clause); - if (where_clause) - EXPECT(!is(*where_clause)); - - auto const& returning_clause = update.returning_clause(); - EXPECT_EQ(returning_clause.is_null(), !expect_returning_clause); - if (returning_clause) { - EXPECT_EQ(returning_clause->columns().size(), expected_returned_column_aliases.size()); - - for (size_t i = 0; i < returning_clause->columns().size(); ++i) { - auto const& column = returning_clause->columns()[i]; - auto const& expected_column_alias = expected_returned_column_aliases[i]; - - EXPECT(!is(*column.expression)); - EXPECT_EQ(column.column_alias, expected_column_alias); - } - } - }; - - Vector> update_columns { { "COLUMN_NAME" } }; - validate("UPDATE OR ABORT table_name SET column_name=1;"sv, SQL::AST::ConflictResolution::Abort, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE OR FAIL table_name SET column_name=1;"sv, SQL::AST::ConflictResolution::Fail, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE OR IGNORE table_name SET column_name=1;"sv, SQL::AST::ConflictResolution::Ignore, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE OR REPLACE table_name SET column_name=1;"sv, SQL::AST::ConflictResolution::Replace, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE OR ROLLBACK table_name SET column_name=1;"sv, SQL::AST::ConflictResolution::Rollback, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - - auto resolution = SQL::AST::ConflictResolution::Abort; - validate("UPDATE table_name SET column_name=1;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE schema_name.table_name SET column_name=1;"sv, resolution, "SCHEMA_NAME"sv, "TABLE_NAME"sv, {}, update_columns, false, false, {}); - validate("UPDATE table_name AS foo SET column_name=1;"sv, resolution, {}, "TABLE_NAME"sv, "FOO"sv, update_columns, false, false, {}); - - validate("UPDATE table_name SET column_name=1;"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, false, false, {}); - validate("UPDATE table_name SET column_name=(SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, false, false, {}); - validate("UPDATE table_name SET column_name=EXISTS (SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, false, false, {}); - validate("UPDATE table_name SET column_name=NOT EXISTS (SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, false, false, {}); - validate("UPDATE table_name SET column1=1, column2=2;"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN1"sv }, { "COLUMN2"sv } }, false, false, {}); - validate("UPDATE table_name SET (column1, column2)=1, column3=2;"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN1"sv, "COLUMN2"sv }, { "COLUMN3"sv } }, false, false, {}); - - validate("UPDATE table_name SET column_name=1 WHERE 1==1;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, true, false, {}); - - validate("UPDATE table_name SET column_name=1 WHERE (SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, true, false, {}); - validate("UPDATE table_name SET column_name=1 WHERE EXISTS (SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, true, false, {}); - validate("UPDATE table_name SET column_name=1 WHERE NOT EXISTS (SELECT 1);"sv, resolution, {}, "TABLE_NAME"sv, {}, { { "COLUMN_NAME"sv } }, true, false, {}); - - validate("UPDATE table_name SET column_name=1 RETURNING *;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, false, true, {}); - validate("UPDATE table_name SET column_name=1 RETURNING column_name;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, false, true, { {} }); - validate("UPDATE table_name SET column_name=1 RETURNING column_name AS alias;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, false, true, { "ALIAS"sv }); - validate("UPDATE table_name SET column_name=1 RETURNING column1 AS alias1, column2 AS alias2;"sv, resolution, {}, "TABLE_NAME"sv, {}, update_columns, false, true, { "ALIAS1"sv, "ALIAS2"sv }); -} - -TEST_CASE(delete_) -{ - EXPECT(parse("DELETE"sv).is_error()); - EXPECT(parse("DELETE FROM"sv).is_error()); - EXPECT(parse("DELETE FROM table_name"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE EXISTS"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE NOT"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE NOT (SELECT 1)"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE NOT EXISTS"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE SELECT"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE (SELECT)"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE 15"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE 15 RETURNING"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE 15 RETURNING *"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE 15 RETURNING column_name"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE 15 RETURNING column_name AS;"sv).is_error()); - EXPECT(parse("DELETE FROM table_name WHERE (');"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table, StringView expected_alias, bool expect_where_clause, bool expect_returning_clause, Vector expected_returned_column_aliases) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& delete_ = static_cast(*statement); - - auto const& qualified_table_name = delete_.qualified_table_name(); - EXPECT_EQ(qualified_table_name->schema_name(), expected_schema); - EXPECT_EQ(qualified_table_name->table_name(), expected_table); - EXPECT_EQ(qualified_table_name->alias(), expected_alias); - - auto const& where_clause = delete_.where_clause(); - EXPECT_EQ(where_clause.is_null(), !expect_where_clause); - if (where_clause) - EXPECT(!is(*where_clause)); - - auto const& returning_clause = delete_.returning_clause(); - EXPECT_EQ(returning_clause.is_null(), !expect_returning_clause); - if (returning_clause) { - EXPECT_EQ(returning_clause->columns().size(), expected_returned_column_aliases.size()); - - for (size_t i = 0; i < returning_clause->columns().size(); ++i) { - auto const& column = returning_clause->columns()[i]; - auto const& expected_column_alias = expected_returned_column_aliases[i]; - - EXPECT(!is(*column.expression)); - EXPECT_EQ(column.column_alias, expected_column_alias); - } - } - }; - - validate("DELETE FROM table_name;"sv, {}, "TABLE_NAME"sv, {}, false, false, {}); - validate("DELETE FROM schema_name.table_name;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, {}, false, false, {}); - validate("DELETE FROM schema_name.table_name AS alias;"sv, "SCHEMA_NAME"sv, "TABLE_NAME"sv, "ALIAS"sv, false, false, {}); - validate("DELETE FROM table_name WHERE (1 == 1);"sv, {}, "TABLE_NAME"sv, {}, true, false, {}); - validate("DELETE FROM table_name WHERE EXISTS (SELECT 1);"sv, {}, "TABLE_NAME"sv, {}, true, false, {}); - validate("DELETE FROM table_name WHERE NOT EXISTS (SELECT 1);"sv, {}, "TABLE_NAME"sv, {}, true, false, {}); - validate("DELETE FROM table_name WHERE (SELECT 1);"sv, {}, "TABLE_NAME"sv, {}, true, false, {}); - validate("DELETE FROM table_name RETURNING *;"sv, {}, "TABLE_NAME"sv, {}, false, true, {}); - validate("DELETE FROM table_name RETURNING column_name;"sv, {}, "TABLE_NAME"sv, {}, false, true, { {} }); - validate("DELETE FROM table_name RETURNING column_name AS alias;"sv, {}, "TABLE_NAME"sv, {}, false, true, { "ALIAS"sv }); - validate("DELETE FROM table_name RETURNING column1 AS alias1, column2 AS alias2;"sv, {}, "TABLE_NAME"sv, {}, false, true, { "ALIAS1"sv, "ALIAS2"sv }); -} - -TEST_CASE(select) -{ - EXPECT(parse("SELECT"sv).is_error()); - EXPECT(parse("SELECT;"sv).is_error()); - EXPECT(parse("SELECT DISTINCT;"sv).is_error()); - EXPECT(parse("SELECT ALL;"sv).is_error()); - EXPECT(parse("SELECT *"sv).is_error()); - EXPECT(parse("SELECT * FROM;"sv).is_error()); - EXPECT(parse("SELECT table_name. FROM table_name;"sv).is_error()); - EXPECT(parse("SELECT column_name AS FROM table_name;"sv).is_error()); - EXPECT(parse("SELECT * FROM ("sv).is_error()); - EXPECT(parse("SELECT * FROM ()"sv).is_error()); - EXPECT(parse("SELECT * FROM ();"sv).is_error()); - EXPECT(parse("SELECT * FROM (table_name1)"sv).is_error()); - EXPECT(parse("SELECT * FROM (table_name1, )"sv).is_error()); - EXPECT(parse("SELECT * FROM (table_name1, table_name2)"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name AS;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name WHERE;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name WHERE 1 ==1"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name GROUP;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name GROUP BY;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name GROUP BY column_name"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER:"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER BY column_name"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER BY column_name COLLATE:"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER BY column_name COLLATE collation"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER BY column_name NULLS;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name ORDER BY column_name NULLS SECOND;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name LIMIT;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name LIMIT 12"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name LIMIT 12 OFFSET;"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name LIMIT 12 OFFSET 15"sv).is_error()); - EXPECT(parse("SELECT * FROM table_name LIMIT 15, 16;"sv).is_error()); - - struct Type { - SQL::AST::ResultType type; - StringView table_name_or_column_alias {}; - }; - - struct From { - StringView schema_name; - StringView table_name; - StringView table_alias; - }; - - struct Ordering { - ByteString collation_name; - SQL::Order order; - SQL::Nulls nulls; - }; - - auto validate = [](StringView sql, Vector expected_columns, Vector expected_from_list, bool expect_where_clause, size_t expected_group_by_size, bool expect_having_clause, Vector expected_ordering, bool expect_limit_clause, bool expect_offset_clause) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& select = static_cast(*statement); - - auto const& result_column_list = select.result_column_list(); - EXPECT_EQ(result_column_list.size(), expected_columns.size()); - for (size_t i = 0; i < result_column_list.size(); ++i) { - auto const& result_column = result_column_list[i]; - auto const& expected_column = expected_columns[i]; - EXPECT_EQ(result_column->type(), expected_column.type); - - switch (result_column->type()) { - case SQL::AST::ResultType::All: - EXPECT(expected_column.table_name_or_column_alias.is_null()); - break; - case SQL::AST::ResultType::Table: - EXPECT_EQ(result_column->table_name(), expected_column.table_name_or_column_alias); - break; - case SQL::AST::ResultType::Expression: - EXPECT_EQ(result_column->column_alias(), expected_column.table_name_or_column_alias); - break; - } - } - - auto const& table_or_subquery_list = select.table_or_subquery_list(); - EXPECT_EQ(table_or_subquery_list.size(), expected_from_list.size()); - for (size_t i = 0; i < table_or_subquery_list.size(); ++i) { - auto const& result_from = table_or_subquery_list[i]; - auto const& expected_from = expected_from_list[i]; - EXPECT_EQ(result_from->schema_name(), expected_from.schema_name); - EXPECT_EQ(result_from->table_name(), expected_from.table_name); - EXPECT_EQ(result_from->table_alias(), expected_from.table_alias); - } - - auto const& where_clause = select.where_clause(); - EXPECT_EQ(where_clause.is_null(), !expect_where_clause); - if (where_clause) - EXPECT(!is(*where_clause)); - - auto const& group_by_clause = select.group_by_clause(); - EXPECT_EQ(group_by_clause.is_null(), (expected_group_by_size == 0)); - if (group_by_clause) { - auto const& group_by_list = group_by_clause->group_by_list(); - EXPECT_EQ(group_by_list.size(), expected_group_by_size); - for (size_t i = 0; i < group_by_list.size(); ++i) - EXPECT(!is(group_by_list[i])); - - auto const& having_clause = group_by_clause->having_clause(); - EXPECT_EQ(having_clause.is_null(), !expect_having_clause); - if (having_clause) - EXPECT(!is(*having_clause)); - } - - auto const& ordering_term_list = select.ordering_term_list(); - EXPECT_EQ(ordering_term_list.size(), expected_ordering.size()); - for (size_t i = 0; i < ordering_term_list.size(); ++i) { - auto const& result_order = ordering_term_list[i]; - auto const& expected_order = expected_ordering[i]; - EXPECT(!is(*result_order->expression())); - EXPECT_EQ(result_order->collation_name(), expected_order.collation_name); - EXPECT_EQ(result_order->order(), expected_order.order); - EXPECT_EQ(result_order->nulls(), expected_order.nulls); - } - - auto const& limit_clause = select.limit_clause(); - EXPECT_EQ(limit_clause.is_null(), !expect_limit_clause); - if (limit_clause) { - auto const& limit_expression = limit_clause->limit_expression(); - EXPECT(!is(*limit_expression)); - - auto const& offset_expression = limit_clause->offset_expression(); - EXPECT_EQ(offset_expression.is_null(), !expect_offset_clause); - if (offset_expression) - EXPECT(!is(*offset_expression)); - } - }; - - Vector all { { SQL::AST::ResultType::All } }; - Vector from { { {}, "TABLE_NAME"sv, {} } }; - - validate("SELECT * FROM table_name;"sv, { { SQL::AST::ResultType::All } }, from, false, 0, false, {}, false, false); - validate("SELECT table_name.* FROM table_name;"sv, { { SQL::AST::ResultType::Table, "TABLE_NAME"sv } }, from, false, 0, false, {}, false, false); - validate("SELECT column_name AS alias FROM table_name;"sv, { { SQL::AST::ResultType::Expression, "ALIAS"sv } }, from, false, 0, false, {}, false, false); - validate("SELECT table_name.column_name AS alias FROM table_name;"sv, { { SQL::AST::ResultType::Expression, "ALIAS"sv } }, from, false, 0, false, {}, false, false); - validate("SELECT schema_name.table_name.column_name AS alias FROM table_name;"sv, { { SQL::AST::ResultType::Expression, "ALIAS"sv } }, from, false, 0, false, {}, false, false); - validate("SELECT column_name AS alias, *, table_name.* FROM table_name;"sv, { { SQL::AST::ResultType::Expression, "ALIAS"sv }, { SQL::AST::ResultType::All }, { SQL::AST::ResultType::Table, "TABLE_NAME"sv } }, from, false, 0, false, {}, false, false); - - validate("SELECT * FROM table_name;"sv, all, { { {}, "TABLE_NAME"sv, {} } }, false, 0, false, {}, false, false); - validate("SELECT * FROM schema_name.table_name;"sv, all, { { "SCHEMA_NAME"sv, "TABLE_NAME"sv, {} } }, false, 0, false, {}, false, false); - validate("SELECT * FROM schema_name.table_name AS alias;"sv, all, { { "SCHEMA_NAME"sv, "TABLE_NAME"sv, "ALIAS"sv } }, false, 0, false, {}, false, false); - validate("SELECT * FROM schema_name.table_name AS alias, table_name2, table_name3 AS table_name4;"sv, all, { { "SCHEMA_NAME"sv, "TABLE_NAME"sv, "ALIAS"sv }, { {}, "TABLE_NAME2"sv, {} }, { {}, "TABLE_NAME3"sv, "TABLE_NAME4"sv } }, false, 0, false, {}, false, false); - - validate("SELECT * FROM table_name WHERE column_name IS NOT NULL;"sv, all, from, true, 0, false, {}, false, false); - - validate("SELECT * FROM table_name GROUP BY column_name;"sv, all, from, false, 1, false, {}, false, false); - validate("SELECT * FROM table_name GROUP BY column1, column2, column3;"sv, all, from, false, 3, false, {}, false, false); - validate("SELECT * FROM table_name GROUP BY column_name HAVING 'abc';"sv, all, from, false, 1, true, {}, false, false); - - validate("SELECT * FROM table_name ORDER BY column_name;"sv, all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name COLLATE collation;"sv, all, from, false, 0, false, { { "COLLATION"sv, SQL::Order::Ascending, SQL::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name ASC;"sv, all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name DESC;"sv, all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::Last } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name ASC NULLS LAST;"sv, all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false); - validate("SELECT * FROM table_name ORDER BY column_name DESC NULLS FIRST;"sv, all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::First } }, false, false); - validate("SELECT * FROM table_name ORDER BY column1, column2 DESC, column3 NULLS LAST;"sv, all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First }, { {}, SQL::Order::Descending, SQL::Nulls::Last }, { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false); - - validate("SELECT * FROM table_name LIMIT 15;"sv, all, from, false, 0, false, {}, true, false); - validate("SELECT * FROM table_name LIMIT 15 OFFSET 16;"sv, all, from, false, 0, false, {}, true, true); -} - -TEST_CASE(common_table_expression) -{ - EXPECT(parse("WITH"sv).is_error()); - EXPECT(parse("WITH;"sv).is_error()); - EXPECT(parse("WITH DELETE FROM table_name;"sv).is_error()); - EXPECT(parse("WITH table_name DELETE FROM table_name;"sv).is_error()); - EXPECT(parse("WITH table_name AS DELETE FROM table_name;"sv).is_error()); - EXPECT(parse("WITH RECURSIVE table_name DELETE FROM table_name;"sv).is_error()); - EXPECT(parse("WITH RECURSIVE table_name AS DELETE FROM table_name;"sv).is_error()); - - // Below are otherwise valid common-table-expressions, but attached to statements which do not allow them. - EXPECT(parse("WITH table_name AS (SELECT * AS TABLE) CREATE TABLE test ( column1 );"sv).is_error()); - EXPECT(parse("WITH table_name AS (SELECT * FROM table_name) DROP TABLE test;"sv).is_error()); - - struct SelectedTableList { - struct SelectedTable { - StringView table_name {}; - Vector column_names {}; - }; - - bool recursive { false }; - Vector selected_tables {}; - }; - - auto validate = [](StringView sql, SelectedTableList expected_selected_tables) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& delete_ = static_cast(*statement); - - auto const& common_table_expression_list = delete_.common_table_expression_list(); - EXPECT(!common_table_expression_list.is_null()); - - EXPECT_EQ(common_table_expression_list->recursive(), expected_selected_tables.recursive); - - auto const& common_table_expressions = common_table_expression_list->common_table_expressions(); - EXPECT_EQ(common_table_expressions.size(), expected_selected_tables.selected_tables.size()); - - for (size_t i = 0; i < common_table_expressions.size(); ++i) { - auto const& common_table_expression = common_table_expressions[i]; - auto const& expected_common_table_expression = expected_selected_tables.selected_tables[i]; - EXPECT_EQ(common_table_expression->table_name(), expected_common_table_expression.table_name); - EXPECT_EQ(common_table_expression->column_names().size(), expected_common_table_expression.column_names.size()); - - for (size_t j = 0; j < common_table_expression->column_names().size(); ++j) - EXPECT_EQ(common_table_expression->column_names()[j], expected_common_table_expression.column_names[j]); - } - }; - - validate("WITH table_name AS (SELECT * FROM table_name) DELETE FROM table_name;"sv, { false, { { "TABLE_NAME"sv } } }); - validate("WITH table_name (column_name) AS (SELECT * FROM table_name) DELETE FROM table_name;"sv, { false, { { "TABLE_NAME"sv, { "COLUMN_NAME"sv } } } }); - validate("WITH table_name (column1, column2) AS (SELECT * FROM table_name) DELETE FROM table_name;"sv, { false, { { "TABLE_NAME"sv, { "COLUMN1"sv, "COLUMN2"sv } } } }); - validate("WITH RECURSIVE table_name AS (SELECT * FROM table_name) DELETE FROM table_name;"sv, { true, { { "TABLE_NAME"sv, {} } } }); -} - -TEST_CASE(nested_subquery_limit) -{ - auto subquery = ByteString::formatted("{:(^{}}table_name{:)^{}}", "", SQL::AST::Limits::maximum_subquery_depth - 1, "", SQL::AST::Limits::maximum_subquery_depth - 1); - EXPECT(!parse(ByteString::formatted("SELECT * FROM {};"sv, subquery)).is_error()); - EXPECT(parse(ByteString::formatted("SELECT * FROM ({});"sv, subquery)).is_error()); -} - -TEST_CASE(bound_parameter_limit) -{ - auto subquery = ByteString::repeated("?, "sv, SQL::AST::Limits::maximum_bound_parameters); - EXPECT(!parse(ByteString::formatted("INSERT INTO table_name VALUES ({}42);"sv, subquery)).is_error()); - EXPECT(parse(ByteString::formatted("INSERT INTO table_name VALUES ({}?);"sv, subquery)).is_error()); -} - -TEST_CASE(describe_table) -{ - EXPECT(parse("DESCRIBE"sv).is_error()); - EXPECT(parse("DESCRIBE;"sv).is_error()); - EXPECT(parse("DESCRIBE TABLE;"sv).is_error()); - EXPECT(parse("DESCRIBE table_name;"sv).is_error()); - - auto validate = [](StringView sql, StringView expected_schema, StringView expected_table) { - auto statement = TRY_OR_FAIL(parse(sql)); - EXPECT(is(*statement)); - - auto const& describe_table_statement = static_cast(*statement); - EXPECT_EQ(describe_table_statement.qualified_table_name()->schema_name(), expected_schema); - EXPECT_EQ(describe_table_statement.qualified_table_name()->table_name(), expected_table); - }; - - validate("DESCRIBE TABLE TableName;"sv, {}, "TABLENAME"sv); - validate("DESCRIBE TABLE SchemaName.TableName;"sv, "SCHEMANAME"sv, "TABLENAME"sv); -} diff --git a/Tests/LibSQL/TestSqlValueAndTuple.cpp b/Tests/LibSQL/TestSqlValueAndTuple.cpp deleted file mode 100644 index 970716895d2..00000000000 --- a/Tests/LibSQL/TestSqlValueAndTuple.cpp +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * Copyright (c) 2021, Jan de Visser - * Copyright (c) 2022, Tim Flynn - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include -#include -#include -#include -#include - -TEST_CASE(null_value) -{ - SQL::Value v(SQL::SQLType::Null); - EXPECT_EQ(v.type(), SQL::SQLType::Null); - EXPECT_EQ(v.to_byte_string(), "(null)"sv); - EXPECT(!v.to_bool().has_value()); - EXPECT(!v.to_int().has_value()); - EXPECT(!v.to_int().has_value()); - EXPECT(!v.to_double().has_value()); -} - -TEST_CASE(assign_null) -{ - SQL::Value v("Test"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT(!v.is_null()); - - v = SQL::Value(); - EXPECT_EQ(v.type(), SQL::SQLType::Null); - EXPECT(v.is_null()); -} - -TEST_CASE(text_value) -{ - { - SQL::Value v(SQL::SQLType::Text); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT(v.is_null()); - - v = "Test"sv; - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v.to_byte_string(), "Test"sv); - } - { - SQL::Value v(ByteString("String Test"sv)); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v.to_byte_string(), "String Test"sv); - - v = ByteString("String Test 2"sv); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v.to_byte_string(), "String Test 2"sv); - } - { - SQL::Value v("const char * Test"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v.to_byte_string(), "const char * Test"sv); - - v = "const char * Test 2"; - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v.to_byte_string(), "const char * Test 2"sv); - } -} - -TEST_CASE(text_value_to_other_types) -{ - { - SQL::Value v("42"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 42); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 42.0) < NumericLimits().epsilon()); - } - { - SQL::Value v("true"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - - EXPECT(v.to_bool().has_value()); - EXPECT(v.to_bool().value()); - } - { - SQL::Value v("false"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - - EXPECT(v.to_bool().has_value()); - EXPECT(!v.to_bool().value()); - } - { - SQL::Value v("foo"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - - EXPECT(!v.to_bool().has_value()); - EXPECT(!v.to_int().has_value()); - EXPECT(!v.to_int().has_value()); - EXPECT(!v.to_double().has_value()); - } - { - SQL::Value v("3.14"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 3.14) < NumericLimits().epsilon()); - } -} - -TEST_CASE(assign_int_to_text_value) -{ - SQL::Value v(SQL::SQLType::Text); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT(v.is_null()); - - v = 42; - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - EXPECT_EQ(v, 42); -} - -TEST_CASE(serialize_text_value) -{ - SQL::Value v("Test"); - EXPECT_EQ(v.type(), SQL::SQLType::Text); - EXPECT_EQ(v, "Test"sv); - - SQL::Serializer serializer; - serializer.serialize(v); - - serializer.rewind(); - auto v2 = serializer.deserialize(); - EXPECT_EQ(v2.type(), SQL::SQLType::Text); - EXPECT_EQ(v2, "Test"sv); - EXPECT_EQ(v2, v); -} - -TEST_CASE(integer_value) -{ - { - SQL::Value v(SQL::SQLType::Integer); - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - EXPECT(v.is_null()); - - v = 42; - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 42); - EXPECT_EQ(v.to_byte_string(), "42"sv); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 42.0) < NumericLimits().epsilon()); - - EXPECT(v.to_bool().has_value()); - EXPECT(v.to_bool().value()); - } - { - SQL::Value v(0); - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 0); - - EXPECT(v.to_bool().has_value()); - EXPECT(!v.to_bool().value()); - } - { - SQL::Value v(42); - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 42); - } - { - SQL::Value text("42"); - SQL::Value integer(SQL::SQLType::Integer); - integer = text; - - EXPECT(integer.to_int().has_value()); - EXPECT_EQ(integer.to_int().value(), 42); - } -} - -TEST_CASE(serialize_int_value) -{ - SQL::Value v(42); - EXPECT_EQ(v.type(), SQL::SQLType::Integer); - EXPECT_EQ(v, 42); - - SQL::Serializer serializer; - serializer.serialize(v); - - serializer.rewind(); - auto v2 = serializer.deserialize(); - EXPECT_EQ(v2.type(), SQL::SQLType::Integer); - EXPECT_EQ(v2, 42); - EXPECT_EQ(v2, v); -} - -TEST_CASE(serialize_downsized_int_value) -{ - auto run_test_for_value = [](auto value) { - using T = decltype(value); - SQL::Value v(value); - - SQL::Serializer serializer; - serializer.serialize(v); - serializer.rewind(); - - auto type_flags = serializer.deserialize(); - auto type_data = type_flags & 0xf0; - auto type = static_cast(type_flags & 0x0f); - - EXPECT_NE(type_data, 0); - EXPECT_EQ(type, SQL::SQLType::Integer); - - auto deserialized = serializer.deserialize(); - EXPECT_EQ(deserialized, value); - }; - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); - - run_test_for_value(NumericLimits::min()); - run_test_for_value(NumericLimits::max()); -} - -TEST_CASE(float_value) -{ - { - SQL::Value v(SQL::SQLType::Float); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - EXPECT(v.is_null()); - - v = 3.14; - EXPECT_EQ(v.type(), SQL::SQLType::Float); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 3.14) < NumericLimits().epsilon()); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 3); - EXPECT_EQ(v.to_byte_string(), "3.14"); - - EXPECT(v.to_bool().has_value()); - EXPECT(v.to_bool().value()); - - v = 0.0; - EXPECT_EQ(v.type(), SQL::SQLType::Float); - - EXPECT(v.to_double().has_value()); - EXPECT(v.to_double().value() < NumericLimits().epsilon()); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 0); - EXPECT_EQ(v.to_byte_string(), "0"sv); - - EXPECT(v.to_bool().has_value()); - EXPECT(!v.to_bool().value()); - } - { - SQL::Value v(3.14); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - EXPECT((v.to_double().value() - 3.14) < NumericLimits().epsilon()); - } - { - SQL::Value v(3.51); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 4); - } - { - SQL::Value v(-3.14); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), -3); - } - { - SQL::Value v(-3.51); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), -4); - } -} - -TEST_CASE(serialize_float_value) -{ - SQL::Value v(3.14); - EXPECT_EQ(v.type(), SQL::SQLType::Float); - EXPECT(v.to_double().value() - 3.14 < NumericLimits().epsilon()); - - SQL::Serializer serializer; - serializer.serialize(v); - - serializer.rewind(); - auto v2 = serializer.deserialize(); - EXPECT_EQ(v2.type(), SQL::SQLType::Float); - EXPECT((v.to_double().value() - 3.14) < NumericLimits().epsilon()); - EXPECT_EQ(v2, v); -} - -TEST_CASE(copy_value) -{ - SQL::Value text("42"); - SQL::Value copy(text); - EXPECT_EQ(copy, "42"sv); -} - -TEST_CASE(to_int) -{ - SQL::Value text("42"); - SQL::Value integer(42); - EXPECT_EQ(text, integer); - EXPECT_EQ(integer, text); - - SQL::Value int_64 { static_cast(123) }; - EXPECT_EQ(int_64.to_int(), 123); - EXPECT_EQ(int_64.to_int(), 123); - EXPECT_EQ(int_64.to_int(), 123); - EXPECT_EQ(int_64.to_int(), 123u); - EXPECT_EQ(int_64.to_int(), 123u); - EXPECT_EQ(int_64.to_int(), 123u); - EXPECT_EQ(int_64.to_int(), 123u); - - SQL::Value uint_64 { static_cast(123) }; - EXPECT_EQ(uint_64.to_int(), 123); - EXPECT_EQ(uint_64.to_int(), 123); - EXPECT_EQ(uint_64.to_int(), 123); - EXPECT_EQ(uint_64.to_int(), 123); - EXPECT_EQ(uint_64.to_int(), 123u); - EXPECT_EQ(uint_64.to_int(), 123u); - EXPECT_EQ(uint_64.to_int(), 123u); -} - -TEST_CASE(to_int_failures) -{ - SQL::Value large_int_64 { NumericLimits::max() }; - EXPECT(!large_int_64.to_int().has_value()); - EXPECT(!large_int_64.to_int().has_value()); - EXPECT(!large_int_64.to_int().has_value()); - EXPECT(!large_int_64.to_int().has_value()); - EXPECT(!large_int_64.to_int().has_value()); - EXPECT(!large_int_64.to_int().has_value()); - - SQL::Value large_int_32 { NumericLimits::max() }; - EXPECT(!large_int_32.to_int().has_value()); - EXPECT(!large_int_32.to_int().has_value()); - EXPECT(!large_int_32.to_int().has_value()); - EXPECT(!large_int_32.to_int().has_value()); - - SQL::Value small_int_64 { NumericLimits::min() }; - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - EXPECT(!small_int_64.to_int().has_value()); - - SQL::Value small_int_32 { NumericLimits::min() }; - EXPECT(!small_int_32.to_int().has_value()); - EXPECT(!small_int_32.to_int().has_value()); - EXPECT(!small_int_32.to_int().has_value()); - EXPECT(!small_int_32.to_int().has_value()); - EXPECT(!small_int_32.to_int().has_value()); - EXPECT(!small_int_32.to_int().has_value()); - - SQL::Value large_uint_64 { NumericLimits::max() }; - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - EXPECT(!large_uint_64.to_int().has_value()); - - SQL::Value large_uint_32 { NumericLimits::max() }; - EXPECT(!large_uint_32.to_int().has_value()); - EXPECT(!large_uint_32.to_int().has_value()); - EXPECT(!large_uint_32.to_int().has_value()); - EXPECT(!large_uint_32.to_int().has_value()); -} - -TEST_CASE(bool_value) -{ - { - SQL::Value v(SQL::SQLType::Boolean); - EXPECT_EQ(v.type(), SQL::SQLType::Boolean); - EXPECT(v.is_null()); - - v = true; - EXPECT_EQ(v.type(), SQL::SQLType::Boolean); - - EXPECT(v.to_bool().has_value()); - EXPECT(v.to_bool().value()); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 1); - EXPECT_EQ(v.to_byte_string(), "true"sv); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 1.0) < NumericLimits().epsilon()); - } - { - SQL::Value v(false); - EXPECT_EQ(v.type(), SQL::SQLType::Boolean); - - EXPECT(v.to_bool().has_value()); - EXPECT(!v.to_bool().value()); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 0); - EXPECT_EQ(v.to_byte_string(), "false"sv); - - EXPECT(v.to_double().has_value()); - EXPECT(v.to_double().value() < NumericLimits().epsilon()); - } - { - SQL::Value v(true); - EXPECT_EQ(v.type(), SQL::SQLType::Boolean); - - EXPECT(v.to_bool().has_value()); - EXPECT(v.to_bool().value()); - - EXPECT(v.to_int().has_value()); - EXPECT_EQ(v.to_int().value(), 1); - EXPECT_EQ(v.to_byte_string(), "true"sv); - - EXPECT(v.to_double().has_value()); - EXPECT((v.to_double().value() - 1.0) < NumericLimits().epsilon()); - } -} - -TEST_CASE(serialize_boolean_value) -{ - SQL::Value v(true); - EXPECT_EQ(v.type(), SQL::SQLType::Boolean); - EXPECT_EQ(v.to_bool(), true); - - SQL::Serializer serializer; - serializer.serialize(v); - - serializer.rewind(); - auto v2 = serializer.deserialize(); - EXPECT_EQ(v2.type(), SQL::SQLType::Boolean); - EXPECT_EQ(v2.to_bool(), true); - EXPECT_EQ(v, v2); -} - -TEST_CASE(unix_date_time_value) -{ - auto now = UnixDateTime::now(); - { - SQL::Value value(now); - EXPECT_EQ(value.type(), SQL::SQLType::Integer); - - auto result = value.to_unix_date_time(); - VERIFY(result.has_value()); - EXPECT_EQ(result->milliseconds_since_epoch(), now.milliseconds_since_epoch()); - } - { - auto now_plus_10s = now + Duration::from_seconds(10); - - SQL::Value value(now_plus_10s); - EXPECT_EQ(value.type(), SQL::SQLType::Integer); - - auto result = value.to_unix_date_time(); - VERIFY(result.has_value()); - EXPECT_EQ(result->milliseconds_since_epoch(), now_plus_10s.milliseconds_since_epoch()); - } - { - auto now_minus_10s = now - Duration::from_seconds(10); - - SQL::Value value(now_minus_10s); - EXPECT_EQ(value.type(), SQL::SQLType::Integer); - - auto result = value.to_unix_date_time(); - VERIFY(result.has_value()); - EXPECT_EQ(result->milliseconds_since_epoch(), now_minus_10s.milliseconds_since_epoch()); - } -} - -TEST_CASE(tuple_value) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend("Test"); - values.empend(42); - MUST(v.assign_tuple(values)); - - auto values2 = v.to_vector(); - EXPECT(values2.has_value()); - EXPECT_EQ(values, values2.value()); -} - -TEST_CASE(copy_tuple_value) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend("Test"); - values.empend(42); - MUST(v.assign_tuple(values)); - - auto values2 = v; - EXPECT_EQ(values2.type(), v.type()); - EXPECT_EQ(v.type(), SQL::SQLType::Tuple); - EXPECT_EQ(values, values2.to_vector().value()); -} - -TEST_CASE(tuple_value_wrong_type) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend(42); - - auto result = v.assign_tuple(move(values)); - EXPECT(result.is_error()); -} - -TEST_CASE(tuple_value_too_many_values) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend("Test"); - values.empend(42); - - auto result = v.assign_tuple(move(values)); - EXPECT(result.is_error()); -} - -TEST_CASE(tuple_value_not_enough_values) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Ascending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend("Test"); - MUST(v.assign_tuple(values)); - - EXPECT_EQ(v.type(), SQL::SQLType::Tuple); - - auto values_opt = v.to_vector(); - EXPECT(values_opt.has_value()); - EXPECT_EQ(values_opt.value().size(), 2u); - - auto col2 = values_opt.value()[1]; - EXPECT_EQ(col2.type(), SQL::SQLType::Integer); -} - -TEST_CASE(serialize_tuple_value) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - auto v = MUST(SQL::Value::create_tuple(move(descriptor))); - - Vector values; - values.empend("Test"); - values.empend(42); - MUST(v.assign_tuple(values)); - - SQL::Serializer serializer; - serializer.serialize(v); - - serializer.rewind(); - auto v2 = serializer.deserialize(); - EXPECT_EQ(v2.type(), SQL::SQLType::Tuple); - EXPECT_EQ(v, v2); -} - -TEST_CASE(order_text_values) -{ - SQL::Value v1(SQL::SQLType::Text); - v1 = "Test_A"; - SQL::Value v2(SQL::SQLType::Text); - v2 = "Test_B"; - EXPECT(v1 <= v2); - EXPECT(v1 < v2); - EXPECT(v2 >= v1); - EXPECT(v2 > v1); -} - -TEST_CASE(order_int_values) -{ - SQL::Value v1(SQL::SQLType::Integer); - v1 = 12; - SQL::Value v2(SQL::SQLType::Integer); - v2 = 42; - EXPECT(v1 <= v2); - EXPECT(v1 < v2); - EXPECT(v2 >= v1); - EXPECT(v2 > v1); -} - -TEST_CASE(tuple) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - SQL::Tuple tuple(descriptor); - - tuple["col1"] = "Test"; - tuple["col2"] = 42; - EXPECT_EQ(tuple[0], "Test"sv); - EXPECT_EQ(tuple[1], 42); -} - -TEST_CASE(serialize_tuple) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - SQL::Tuple tuple(descriptor); - - tuple["col1"] = "Test"; - tuple["col2"] = 42; - - EXPECT_EQ(tuple[0], "Test"sv); - EXPECT_EQ(tuple[1], 42); - - SQL::Serializer serializer; - serializer.serialize(tuple); - - serializer.rewind(); - auto tuple2 = serializer.deserialize(); - EXPECT_EQ(tuple2[0], "Test"sv); - EXPECT_EQ(tuple2[1], 42); -} - -TEST_CASE(copy_tuple) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - SQL::Tuple tuple(descriptor); - - tuple["col1"] = "Test"; - tuple["col2"] = 42; - - SQL::Tuple copy; - copy = tuple; - EXPECT_EQ(tuple, copy); - - SQL::Tuple copy_2(copy); - EXPECT_EQ(tuple, copy_2); -} - -TEST_CASE(compare_tuples) -{ - NonnullRefPtr descriptor = adopt_ref(*new SQL::TupleDescriptor); - descriptor->append({ "schema", "table", "col1", SQL::SQLType::Text, SQL::Order::Ascending }); - descriptor->append({ "schema", "table", "col2", SQL::SQLType::Integer, SQL::Order::Descending }); - - SQL::Tuple tuple1(descriptor); - tuple1["col1"] = "Test"; - tuple1["col2"] = 42; - - SQL::Tuple tuple2(descriptor); - tuple2["col1"] = "Test"; - tuple2["col2"] = 12; - - SQL::Tuple tuple3(descriptor); - tuple3["col1"] = "Text"; - tuple3["col2"] = 12; - - EXPECT(tuple1 <= tuple2); - EXPECT(tuple1 < tuple2); - EXPECT(tuple2 >= tuple1); - EXPECT(tuple2 > tuple1); - - EXPECT(tuple1 <= tuple3); - EXPECT(tuple1 < tuple3); - EXPECT(tuple3 >= tuple1); - EXPECT(tuple3 > tuple1); -} - -TEST_CASE(add) -{ - { - SQL::Value value1 { 21 }; - SQL::Value value2 { 42 }; - - auto result = value1.add(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 63); - } - { - SQL::Value value1 { 21 }; - SQL::Value value2 { static_cast(42) }; - - auto result = value1.add(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 63); - } - { - SQL::Value value1 { static_cast(21) }; - SQL::Value value2 { 42 }; - - auto result = value1.add(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 63); - } - { - SQL::Value value1 { static_cast(21) }; - SQL::Value value2 { 42 }; - - auto result = value1.add(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 63); - } - { - SQL::Value value1 { static_cast(21.5) }; - SQL::Value value2 { 42 }; - - auto result = value1.add(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Float); - EXPECT((result.value().to_double().value() - 63.5) < NumericLimits().epsilon()); - } -} - -TEST_CASE(add_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.add(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.add(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.add(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to a number. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.add(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(subtract) -{ - { - SQL::Value value1 { 21 }; - SQL::Value value2 { 42 }; - - auto result = value1.subtract(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), -21); - } - { - SQL::Value value1 { 21 }; - SQL::Value value2 { static_cast(42) }; - - auto result = value1.subtract(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), -21); - } - { - SQL::Value value1 { static_cast(42) }; - SQL::Value value2 { 21 }; - - auto result = value1.subtract(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 21); - } - { - SQL::Value value1 { static_cast(21) }; - SQL::Value value2 { 42 }; - - auto result = value1.subtract(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), -21); - } - { - SQL::Value value1 { static_cast(21.5) }; - SQL::Value value2 { 42 }; - - auto result = value1.subtract(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Float); - EXPECT((result.value().to_double().value() - 20.5) < NumericLimits().epsilon()); - } -} - -TEST_CASE(subtract_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.subtract(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.subtract(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { static_cast(0) }; - SQL::Value value2 { static_cast(1) }; - - auto result = value1.subtract(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to a number. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.subtract(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(multiply) -{ - { - SQL::Value value1 { 2 }; - SQL::Value value2 { 21 }; - - auto result = value1.multiply(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 42); - } - { - SQL::Value value1 { 2 }; - SQL::Value value2 { static_cast(21) }; - - auto result = value1.multiply(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 42); - } - { - SQL::Value value1 { static_cast(2) }; - SQL::Value value2 { 21 }; - - auto result = value1.multiply(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 42); - } - { - SQL::Value value1 { static_cast(2) }; - SQL::Value value2 { 21 }; - - auto result = value1.multiply(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 42); - } - { - SQL::Value value1 { static_cast(2.5) }; - SQL::Value value2 { 21 }; - - auto result = value1.multiply(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Float); - EXPECT((result.value().to_double().value() - 52.5) < NumericLimits().epsilon()); - } -} - -TEST_CASE(multiply_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.multiply(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.multiply(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { NumericLimits::max() }; - SQL::Value value2 { 2 }; - - auto result = value1.multiply(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to a number. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.multiply(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(divide) -{ - { - SQL::Value value1 { 42 }; - SQL::Value value2 { -2 }; - - auto result = value1.divide(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), -21); - } - { - SQL::Value value1 { 42 }; - SQL::Value value2 { static_cast(2) }; - - auto result = value1.divide(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 21); - } - { - SQL::Value value1 { static_cast(42) }; - SQL::Value value2 { 2 }; - - auto result = value1.divide(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 21); - } - { - SQL::Value value1 { static_cast(42) }; - SQL::Value value2 { 2 }; - - auto result = value1.divide(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 21); - } - { - SQL::Value value1 { static_cast(43) }; - SQL::Value value2 { 2 }; - - auto result = value1.divide(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Float); - EXPECT((result.value().to_double().value() - 21.5) < NumericLimits().epsilon()); - } -} - -TEST_CASE(divide_error) -{ - { - // The operation itself would overflow. - SQL::Value value1 { 1 }; - SQL::Value value2 { 0 }; - - auto result = value1.divide(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to a number. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.divide(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(modulo) -{ - { - SQL::Value value1 { 21 }; - SQL::Value value2 { 2 }; - - auto result = value1.modulo(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 1); - } - { - SQL::Value value1 { 21 }; - SQL::Value value2 { static_cast(2) }; - - auto result = value1.modulo(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 1); - } - { - SQL::Value value1 { static_cast(21) }; - SQL::Value value2 { 2 }; - - auto result = value1.modulo(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 1); - } - { - SQL::Value value1 { static_cast(21) }; - SQL::Value value2 { 2 }; - - auto result = value1.modulo(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 1); - } -} - -TEST_CASE(modulo_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.modulo(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.modulo(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { 21 }; - SQL::Value value2 { 0 }; - - auto result = value1.modulo(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.modulo(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { static_cast(21.5) }; - SQL::Value value2 { 2 }; - - auto result = value1.modulo(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(shift_left) -{ - { - SQL::Value value1 { 0b0011'0000 }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_left(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b1100'0000); - } - { - SQL::Value value1 { 0b0011'0000 }; - SQL::Value value2 { static_cast(2) }; - - auto result = value1.shift_left(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b1100'0000); - } - { - SQL::Value value1 { static_cast(0b0011'0000) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_left(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b1100'0000); - } - { - SQL::Value value1 { static_cast(0b0011'0000) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_left(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b1100'0000); - } -} - -TEST_CASE(shift_left_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { 21 }; - SQL::Value value2 { -1 }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { 21 }; - SQL::Value value2 { 64 }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { static_cast(21.5) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_left(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} - -TEST_CASE(shift_right) -{ - { - SQL::Value value1 { 0b0011'0000 }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_right(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b0000'1100); - } - { - SQL::Value value1 { 0b0011'0000 }; - SQL::Value value2 { static_cast(2) }; - - auto result = value1.shift_right(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b0000'1100); - } - { - SQL::Value value1 { static_cast(0b0011'0000) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_right(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b0000'1100); - } - { - SQL::Value value1 { static_cast(0b0011'0000) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_right(value2); - EXPECT_EQ(result.value().type(), SQL::SQLType::Integer); - EXPECT_EQ(result.value(), 0b0000'1100); - } -} - -TEST_CASE(shift_right_error) -{ - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { 1 }; - SQL::Value value2 { NumericLimits::max() }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Fails to coerce value2 to the signedness of value1. - SQL::Value value1 { static_cast(1) }; - SQL::Value value2 { -1 }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { 21 }; - SQL::Value value2 { -1 }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // The operation itself would overflow. - SQL::Value value1 { 21 }; - SQL::Value value2 { 64 }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::IntegerOverflow); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { 1 }; - SQL::Value value2 { "foo"sv }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } - { - // Cannot convert value to an integer. - SQL::Value value1 { static_cast(21.5) }; - SQL::Value value2 { 2 }; - - auto result = value1.shift_right(value2); - EXPECT(result.is_error()); - EXPECT_EQ(result.error().error(), SQL::SQLErrorCode::NumericOperatorTypeMismatch); - } -} diff --git a/Tests/LibTest/CMakeLists.txt b/Tests/LibTest/CMakeLists.txt index 84610cb211f..2c57325b858 100644 --- a/Tests/LibTest/CMakeLists.txt +++ b/Tests/LibTest/CMakeLists.txt @@ -4,5 +4,5 @@ set(TEST_SOURCES ) foreach(source IN LISTS TEST_SOURCES) - serenity_test("${source}" LibSQL LIBS LibSQL LibIPC) + serenity_test("${source}" Test) endforeach() diff --git a/Userland/Libraries/CMakeLists.txt b/Userland/Libraries/CMakeLists.txt index 8144afb9d11..161120f69cd 100644 --- a/Userland/Libraries/CMakeLists.txt +++ b/Userland/Libraries/CMakeLists.txt @@ -24,7 +24,6 @@ add_subdirectory(LibProtocol) add_subdirectory(LibRegex) add_subdirectory(LibRIFF) add_subdirectory(LibSanitizer) -add_subdirectory(LibSQL) add_subdirectory(LibSyntax) add_subdirectory(LibTest) add_subdirectory(LibTextCodec) diff --git a/Userland/Libraries/LibSQL/AST/AST.h b/Userland/Libraries/LibSQL/AST/AST.h deleted file mode 100644 index e007c93cdc6..00000000000 --- a/Userland/Libraries/LibSQL/AST/AST.h +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * Copyright (c) 2021, Tim Flynn - * Copyright (c) 2021, Mahmoud Mandour - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace SQL::AST { - -template -static inline NonnullRefPtr -create_ast_node(Args&&... args) -{ - return adopt_ref(*new T(forward(args)...)); -} - -class ASTNode : public RefCounted { -public: - virtual ~ASTNode() = default; - -protected: - ASTNode() = default; -}; - -//================================================================================================== -// Language types -//================================================================================================== - -class SignedNumber final : public ASTNode { -public: - explicit SignedNumber(double value) - : m_value(value) - { - } - - double value() const { return m_value; } - -private: - double m_value; -}; - -class TypeName : public ASTNode { -public: - TypeName(ByteString name, Vector> signed_numbers) - : m_name(move(name)) - , m_signed_numbers(move(signed_numbers)) - { - VERIFY(m_signed_numbers.size() <= 2); - } - - ByteString const& name() const { return m_name; } - Vector> const& signed_numbers() const { return m_signed_numbers; } - -private: - ByteString m_name; - Vector> m_signed_numbers; -}; - -class ColumnDefinition : public ASTNode { -public: - ColumnDefinition(ByteString name, NonnullRefPtr type_name) - : m_name(move(name)) - , m_type_name(move(type_name)) - { - } - - ByteString const& name() const { return m_name; } - NonnullRefPtr const& type_name() const { return m_type_name; } - -private: - ByteString m_name; - NonnullRefPtr m_type_name; -}; - -class CommonTableExpression : public ASTNode { -public: - CommonTableExpression(ByteString table_name, Vector column_names, NonnullRefPtr const& select_statement() const { return m_select_statement; } - -private: - ByteString m_table_name; - Vector m_column_names; - NonnullRefPtr select_statement, bool invert_expression) - : m_select_statement(move(select_statement)) - , m_invert_expression(invert_expression) - { - } - - NonnullRefPtr m_select_statement; - bool m_invert_expression; -}; - -class CollateExpression : public NestedExpression { -public: - CollateExpression(NonnullRefPtr expression, ByteString collation_name) - : NestedExpression(move(expression)) - , m_collation_name(move(collation_name)) - { - } - - ByteString const& collation_name() const { return m_collation_name; } - -private: - ByteString m_collation_name; -}; - -enum class MatchOperator { - Like, - Glob, - Match, - Regexp, -}; - -class MatchExpression : public InvertibleNestedDoubleExpression { -public: - MatchExpression(MatchOperator type, NonnullRefPtr lhs, NonnullRefPtr rhs, RefPtr escape, bool invert_expression) - : InvertibleNestedDoubleExpression(move(lhs), move(rhs), invert_expression) - , m_type(type) - , m_escape(move(escape)) - { - } - - MatchOperator type() const { return m_type; } - RefPtr const& escape() const { return m_escape; } - virtual ResultOr evaluate(ExecutionContext&) const override; - -private: - MatchOperator m_type; - RefPtr m_escape; -}; - -class NullExpression : public InvertibleNestedExpression { -public: - NullExpression(NonnullRefPtr expression, bool invert_expression) - : InvertibleNestedExpression(move(expression), invert_expression) - { - } -}; - -class IsExpression : public InvertibleNestedDoubleExpression { -public: - IsExpression(NonnullRefPtr lhs, NonnullRefPtr rhs, bool invert_expression) - : InvertibleNestedDoubleExpression(move(lhs), move(rhs), invert_expression) - { - } -}; - -class BetweenExpression : public InvertibleNestedDoubleExpression { -public: - BetweenExpression(NonnullRefPtr expression, NonnullRefPtr lhs, NonnullRefPtr rhs, bool invert_expression) - : InvertibleNestedDoubleExpression(move(lhs), move(rhs), invert_expression) - , m_expression(move(expression)) - { - } - - NonnullRefPtr const& expression() const { return m_expression; } - -private: - NonnullRefPtr m_expression; -}; - -class InSelectionExpression : public InvertibleNestedExpression { -public: - InSelectionExpression(NonnullRefPtr expression, NonnullRefPtr const& select_statement() const { return m_select_statement; } - -private: - NonnullRefPtr select_statement, bool is_temporary, bool is_error_if_table_exists) - : m_schema_name(move(schema_name)) - , m_table_name(move(table_name)) - , m_select_statement(move(select_statement)) - , m_is_temporary(is_temporary) - , m_is_error_if_table_exists(is_error_if_table_exists) - { - } - - CreateTable(ByteString schema_name, ByteString table_name, Vector> columns, bool is_temporary, bool is_error_if_table_exists) - : m_schema_name(move(schema_name)) - , m_table_name(move(table_name)) - , m_columns(move(columns)) - , m_is_temporary(is_temporary) - , m_is_error_if_table_exists(is_error_if_table_exists) - { - } - - ByteString const& schema_name() const { return m_schema_name; } - ByteString const& table_name() const { return m_table_name; } - - bool has_selection() const { return !m_select_statement.is_null(); } - RefPtr m_select_statement; - Vector> m_columns; - bool m_is_temporary; - bool m_is_error_if_table_exists; -}; - -class AlterTable : public Statement { -public: - ByteString const& schema_name() const { return m_schema_name; } - ByteString const& table_name() const { return m_table_name; } - -protected: - AlterTable(ByteString schema_name, ByteString table_name) - : m_schema_name(move(schema_name)) - , m_table_name(move(table_name)) - { - } - -private: - ByteString m_schema_name; - ByteString m_table_name; -}; - -class RenameTable : public AlterTable { -public: - RenameTable(ByteString schema_name, ByteString table_name, ByteString new_table_name) - : AlterTable(move(schema_name), move(table_name)) - , m_new_table_name(move(new_table_name)) - { - } - - ByteString const& new_table_name() const { return m_new_table_name; } - -private: - ByteString m_new_table_name; -}; - -class RenameColumn : public AlterTable { -public: - RenameColumn(ByteString schema_name, ByteString table_name, ByteString column_name, ByteString new_column_name) - : AlterTable(move(schema_name), move(table_name)) - , m_column_name(move(column_name)) - , m_new_column_name(move(new_column_name)) - { - } - - ByteString const& column_name() const { return m_column_name; } - ByteString const& new_column_name() const { return m_new_column_name; } - -private: - ByteString m_column_name; - ByteString m_new_column_name; -}; - -class AddColumn : public AlterTable { -public: - AddColumn(ByteString schema_name, ByteString table_name, NonnullRefPtr column) - : AlterTable(move(schema_name), move(table_name)) - , m_column(move(column)) - { - } - - NonnullRefPtr const& column() const { return m_column; } - -private: - NonnullRefPtr m_column; -}; - -class DropColumn : public AlterTable { -public: - DropColumn(ByteString schema_name, ByteString table_name, ByteString column_name) - : AlterTable(move(schema_name), move(table_name)) - , m_column_name(move(column_name)) - { - } - - ByteString const& column_name() const { return m_column_name; } - -private: - ByteString m_column_name; -}; - -class DropTable : public Statement { -public: - DropTable(ByteString schema_name, ByteString table_name, bool is_error_if_table_does_not_exist) - : m_schema_name(move(schema_name)) - , m_table_name(move(table_name)) - , m_is_error_if_table_does_not_exist(is_error_if_table_does_not_exist) - { - } - - ByteString const& schema_name() const { return m_schema_name; } - ByteString const& table_name() const { return m_table_name; } - bool is_error_if_table_does_not_exist() const { return m_is_error_if_table_does_not_exist; } - -private: - ByteString m_schema_name; - ByteString m_table_name; - bool m_is_error_if_table_does_not_exist; -}; - -enum class ConflictResolution { - Abort, - Fail, - Ignore, - Replace, - Rollback, -}; - -class Insert : public Statement { -public: - Insert(RefPtr common_table_expression_list, ConflictResolution conflict_resolution, ByteString schema_name, ByteString table_name, ByteString alias, Vector column_names, Vector> chained_expressions) - : m_common_table_expression_list(move(common_table_expression_list)) - , m_conflict_resolution(conflict_resolution) - , m_schema_name(move(schema_name)) - , m_table_name(move(table_name)) - , m_alias(move(alias)) - , m_column_names(move(column_names)) - , m_chained_expressions(move(chained_expressions)) - { - } - - Insert(RefPtr common_table_expression_list, ConflictResolution conflict_resolution, ByteString schema_name, ByteString table_name, ByteString alias, Vector column_names, RefPtr const& select_statement() const { return m_select_statement; } - - virtual ResultOr execute(ExecutionContext&) const override; - -private: - RefPtr m_common_table_expression_list; - ConflictResolution m_conflict_resolution; - ByteString m_schema_name; - ByteString m_table_name; - ByteString m_alias; - Vector m_column_names; - Vector> m_chained_expressions; - RefPtr select_statement; - - if (consume_if(TokenType::Values)) { - parse_comma_separated_list(false, [&]() { - if (auto chained_expression = parse_chained_expression()) { - auto* chained_expr = verify_cast(chained_expression.ptr()); - if ((column_names.size() > 0) && (chained_expr->expressions().size() != column_names.size())) { - syntax_error("Number of expressions does not match number of columns"); - } else { - chained_expressions.append(static_ptr_cast(chained_expression.release_nonnull())); - } - } else { - expected("Chained expression"sv); - } - }); - } else if (match(TokenType::Select)) { - select_statement = parse_select_statement({}); - } else { - consume(TokenType::Default); - consume(TokenType::Values); - } - - RefPtr returning_clause; - if (match(TokenType::Returning)) - returning_clause = parse_returning_clause(); - - // FIXME: Parse 'upsert-clause'. - - if (!chained_expressions.is_empty()) - return create_ast_node(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names), move(chained_expressions)); - if (!select_statement.is_null()) - return create_ast_node(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names), move(select_statement)); - - return create_ast_node(move(common_table_expression_list), conflict_resolution, move(schema_name), move(table_name), move(alias), move(column_names)); -} - -NonnullRefPtr Parser::parse_update_statement(RefPtr common_table_expression_list) -{ - // https://sqlite.org/lang_update.html - consume(TokenType::Update); - auto conflict_resolution = parse_conflict_resolution(); - auto qualified_table_name = parse_qualified_table_name(); - consume(TokenType::Set); - - Vector update_columns; - parse_comma_separated_list(false, [&]() { - Vector column_names; - if (match(TokenType::ParenOpen)) { - parse_comma_separated_list(true, [&]() { column_names.append(consume(TokenType::Identifier).value()); }); - } else { - column_names.append(consume(TokenType::Identifier).value()); - } - - consume(TokenType::Equals); - update_columns.append({ move(column_names), parse_expression() }); - }); - - Vector> table_or_subquery_list; - if (consume_if(TokenType::From)) { - // FIXME: Parse join-clause. - parse_comma_separated_list(false, [&]() { table_or_subquery_list.append(parse_table_or_subquery()); }); - } - - RefPtr where_clause; - if (consume_if(TokenType::Where)) - where_clause = parse_expression(); - - RefPtr returning_clause; - if (match(TokenType::Returning)) - returning_clause = parse_returning_clause(); - - return create_ast_node(move(common_table_expression_list), conflict_resolution, move(qualified_table_name), move(update_columns), move(table_or_subquery_list), move(where_clause), move(returning_clause)); -} - -NonnullRefPtr Parser::parse_delete_statement(RefPtr common_table_expression_list) -{ - // https://sqlite.org/lang_delete.html - consume(TokenType::Delete); - consume(TokenType::From); - auto qualified_table_name = parse_qualified_table_name(); - - RefPtr where_clause; - if (consume_if(TokenType::Where)) - where_clause = parse_expression(); - - RefPtr returning_clause; - if (match(TokenType::Returning)) - returning_clause = parse_returning_clause(); - - return create_ast_node(move(common_table_expression_list), move(qualified_table_name), move(where_clause), move(returning_clause)); -} - -NonnullRefPtr(move(common_table_expression_list), select_all, move(result_column_list), move(table_or_subquery_list), move(where_clause), move(group_by_clause), move(ordering_term_list), move(limit_clause)); -} - -RefPtr Parser::parse_common_table_expression_list() -{ - consume(TokenType::With); - bool recursive = consume_if(TokenType::Recursive); - - Vector> common_table_expression; - parse_comma_separated_list(false, [&]() { common_table_expression.append(parse_common_table_expression()); }); - - if (common_table_expression.is_empty()) { - expected("Common table expression list"sv); - return {}; - } - - return create_ast_node(recursive, move(common_table_expression)); -} - -NonnullRefPtr Parser::parse_expression() -{ - if (++m_parser_state.m_current_expression_depth > Limits::maximum_expression_tree_depth) { - syntax_error(ByteString::formatted("Exceeded maximum expression tree depth of {}", Limits::maximum_expression_tree_depth)); - return create_ast_node(); - } - - // https://sqlite.org/lang_expr.html - auto expression = parse_primary_expression(); - - if (match_secondary_expression()) - expression = parse_secondary_expression(move(expression)); - - // FIXME: Parse 'function-name'. - // FIXME: Parse 'raise-function'. - - --m_parser_state.m_current_expression_depth; - return expression; -} - -NonnullRefPtr Parser::parse_primary_expression() -{ - if (auto expression = parse_literal_value_expression()) - return expression.release_nonnull(); - - if (auto expression = parse_bind_parameter_expression()) - return expression.release_nonnull(); - - if (auto expression = parse_column_name_expression()) - return expression.release_nonnull(); - - if (auto expression = parse_unary_operator_expression()) - return expression.release_nonnull(); - - if (auto expression = parse_cast_expression()) - return expression.release_nonnull(); - - if (auto expression = parse_case_expression()) - return expression.release_nonnull(); - - if (auto invert_expression = consume_if(TokenType::Not); invert_expression || consume_if(TokenType::Exists)) { - if (auto expression = parse_exists_expression(invert_expression)) - return expression.release_nonnull(); - - expected("Exists expression"sv); - } - - if (consume_if(TokenType::ParenOpen)) { - // Encountering a Select token at this point means this must be an ExistsExpression with no EXISTS keyword. - if (match(TokenType::Select)) { - auto select_statement = parse_select_statement({}); - consume(TokenType::ParenClose); - return create_ast_node(move(select_statement), false); - } - - if (auto expression = parse_chained_expression(false)) { - consume(TokenType::ParenClose); - return expression.release_nonnull(); - } - - expected("Chained expression"sv); - } - - expected("Primary Expression"sv); - consume(); - - return create_ast_node(); -} - -NonnullRefPtr Parser::parse_secondary_expression(NonnullRefPtr primary) -{ - if (auto expression = parse_binary_operator_expression(primary)) - return expression.release_nonnull(); - - if (auto expression = parse_collate_expression(primary)) - return expression.release_nonnull(); - - if (auto expression = parse_is_expression(primary)) - return expression.release_nonnull(); - - bool invert_expression = false; - if (consume_if(TokenType::Not)) - invert_expression = true; - - if (auto expression = parse_match_expression(primary, invert_expression)) - return expression.release_nonnull(); - - if (auto expression = parse_null_expression(primary, invert_expression)) - return expression.release_nonnull(); - - if (auto expression = parse_between_expression(primary, invert_expression)) - return expression.release_nonnull(); - - if (auto expression = parse_in_expression(primary, invert_expression)) - return expression.release_nonnull(); - - expected("Secondary Expression"sv); - consume(); - - return create_ast_node(); -} - -bool Parser::match_secondary_expression() const -{ - return match(TokenType::Not) - || match(TokenType::DoublePipe) - || match(TokenType::Asterisk) - || match(TokenType::Divide) - || match(TokenType::Modulus) - || match(TokenType::Plus) - || match(TokenType::Minus) - || match(TokenType::ShiftLeft) - || match(TokenType::ShiftRight) - || match(TokenType::Ampersand) - || match(TokenType::Pipe) - || match(TokenType::LessThan) - || match(TokenType::LessThanEquals) - || match(TokenType::GreaterThan) - || match(TokenType::GreaterThanEquals) - || match(TokenType::Equals) - || match(TokenType::EqualsEquals) - || match(TokenType::NotEquals1) - || match(TokenType::NotEquals2) - || match(TokenType::And) - || match(TokenType::Or) - || match(TokenType::Collate) - || match(TokenType::Is) - || match(TokenType::Like) - || match(TokenType::Glob) - || match(TokenType::Match) - || match(TokenType::Regexp) - || match(TokenType::Isnull) - || match(TokenType::Notnull) - || match(TokenType::Between) - || match(TokenType::In); -} - -RefPtr Parser::parse_literal_value_expression() -{ - if (match(TokenType::NumericLiteral)) { - auto value = consume().double_value(); - return create_ast_node(value); - } - if (match(TokenType::StringLiteral)) { - // TODO: Should the surrounding ' ' be removed here? - auto value = consume().value(); - return create_ast_node(value); - } - if (match(TokenType::BlobLiteral)) { - // TODO: Should the surrounding x' ' be removed here? - auto value = consume().value(); - return create_ast_node(value); - } - if (consume_if(TokenType::True)) - return create_ast_node(true); - if (consume_if(TokenType::False)) - return create_ast_node(false); - if (consume_if(TokenType::Null)) - return create_ast_node(); - - return {}; -} - -// https://sqlite.org/lang_expr.html#varparam -RefPtr Parser::parse_bind_parameter_expression() -{ - // FIXME: Support ?NNN, :AAAA, @AAAA, and $AAAA forms. - if (consume_if(TokenType::Placeholder)) { - auto parameter = m_parser_state.m_bound_parameters; - if (++m_parser_state.m_bound_parameters > Limits::maximum_bound_parameters) - syntax_error(ByteString::formatted("Exceeded maximum number of bound parameters {}", Limits::maximum_bound_parameters)); - - return create_ast_node(parameter); - } - - return {}; -} - -RefPtr Parser::parse_column_name_expression(Optional with_parsed_identifier, bool with_parsed_period) -{ - if (!with_parsed_identifier.has_value() && !match(TokenType::Identifier)) - return {}; - - ByteString first_identifier; - if (!with_parsed_identifier.has_value()) - first_identifier = consume(TokenType::Identifier).value(); - else - first_identifier = with_parsed_identifier.release_value(); - - ByteString schema_name; - ByteString table_name; - ByteString column_name; - - if (with_parsed_period || consume_if(TokenType::Period)) { - ByteString second_identifier = consume(TokenType::Identifier).value(); - - if (consume_if(TokenType::Period)) { - schema_name = move(first_identifier); - table_name = move(second_identifier); - column_name = consume(TokenType::Identifier).value(); - } else { - table_name = move(first_identifier); - column_name = move(second_identifier); - } - } else { - column_name = move(first_identifier); - } - - return create_ast_node(move(schema_name), move(table_name), move(column_name)); -} - -RefPtr Parser::parse_unary_operator_expression() -{ - if (consume_if(TokenType::Minus)) - return create_ast_node(UnaryOperator::Minus, parse_expression()); - - if (consume_if(TokenType::Plus)) - return create_ast_node(UnaryOperator::Plus, parse_expression()); - - if (consume_if(TokenType::Tilde)) - return create_ast_node(UnaryOperator::BitwiseNot, parse_expression()); - - if (consume_if(TokenType::Not)) { - if (match(TokenType::Exists)) - return parse_exists_expression(true); - else - return create_ast_node(UnaryOperator::Not, parse_expression()); - } - - return {}; -} - -RefPtr Parser::parse_binary_operator_expression(NonnullRefPtr lhs) -{ - if (consume_if(TokenType::DoublePipe)) - return create_ast_node(BinaryOperator::Concatenate, move(lhs), parse_expression()); - - if (consume_if(TokenType::Asterisk)) - return create_ast_node(BinaryOperator::Multiplication, move(lhs), parse_expression()); - - if (consume_if(TokenType::Divide)) - return create_ast_node(BinaryOperator::Division, move(lhs), parse_expression()); - - if (consume_if(TokenType::Modulus)) - return create_ast_node(BinaryOperator::Modulo, move(lhs), parse_expression()); - - if (consume_if(TokenType::Plus)) - return create_ast_node(BinaryOperator::Plus, move(lhs), parse_expression()); - - if (consume_if(TokenType::Minus)) - return create_ast_node(BinaryOperator::Minus, move(lhs), parse_expression()); - - if (consume_if(TokenType::ShiftLeft)) - return create_ast_node(BinaryOperator::ShiftLeft, move(lhs), parse_expression()); - - if (consume_if(TokenType::ShiftRight)) - return create_ast_node(BinaryOperator::ShiftRight, move(lhs), parse_expression()); - - if (consume_if(TokenType::Ampersand)) - return create_ast_node(BinaryOperator::BitwiseAnd, move(lhs), parse_expression()); - - if (consume_if(TokenType::Pipe)) - return create_ast_node(BinaryOperator::BitwiseOr, move(lhs), parse_expression()); - - if (consume_if(TokenType::LessThan)) - return create_ast_node(BinaryOperator::LessThan, move(lhs), parse_expression()); - - if (consume_if(TokenType::LessThanEquals)) - return create_ast_node(BinaryOperator::LessThanEquals, move(lhs), parse_expression()); - - if (consume_if(TokenType::GreaterThan)) - return create_ast_node(BinaryOperator::GreaterThan, move(lhs), parse_expression()); - - if (consume_if(TokenType::GreaterThanEquals)) - return create_ast_node(BinaryOperator::GreaterThanEquals, move(lhs), parse_expression()); - - if (consume_if(TokenType::Equals) || consume_if(TokenType::EqualsEquals)) - return create_ast_node(BinaryOperator::Equals, move(lhs), parse_expression()); - - if (consume_if(TokenType::NotEquals1) || consume_if(TokenType::NotEquals2)) - return create_ast_node(BinaryOperator::NotEquals, move(lhs), parse_expression()); - - if (consume_if(TokenType::And)) - return create_ast_node(BinaryOperator::And, move(lhs), parse_expression()); - - if (consume_if(TokenType::Or)) - return create_ast_node(BinaryOperator::Or, move(lhs), parse_expression()); - - return {}; -} - -RefPtr Parser::parse_chained_expression(bool surrounded_by_parentheses) -{ - if (surrounded_by_parentheses && !consume_if(TokenType::ParenOpen)) - return {}; - - Vector> expressions; - parse_comma_separated_list(false, [&]() { expressions.append(parse_expression()); }); - - if (surrounded_by_parentheses) - consume(TokenType::ParenClose); - - return create_ast_node(move(expressions)); -} - -RefPtr Parser::parse_cast_expression() -{ - if (!match(TokenType::Cast)) - return {}; - - consume(TokenType::Cast); - consume(TokenType::ParenOpen); - auto expression = parse_expression(); - consume(TokenType::As); - auto type_name = parse_type_name(); - consume(TokenType::ParenClose); - - return create_ast_node(move(expression), move(type_name)); -} - -RefPtr Parser::parse_case_expression() -{ - if (!match(TokenType::Case)) - return {}; - - consume(); - - RefPtr case_expression; - if (!match(TokenType::When)) { - case_expression = parse_expression(); - } - - Vector when_then_clauses; - - do { - consume(TokenType::When); - auto when = parse_expression(); - consume(TokenType::Then); - auto then = parse_expression(); - - when_then_clauses.append({ move(when), move(then) }); - - if (!match(TokenType::When)) - break; - } while (!match(TokenType::Eof)); - - RefPtr else_expression; - if (consume_if(TokenType::Else)) - else_expression = parse_expression(); - - consume(TokenType::End); - return create_ast_node(move(case_expression), move(when_then_clauses), move(else_expression)); -} - -RefPtr Parser::parse_exists_expression(bool invert_expression) -{ - if (!(match(TokenType::Exists) || match(TokenType::ParenOpen))) - return {}; - - consume_if(TokenType::Exists); - consume(TokenType::ParenOpen); - - auto select_statement = parse_select_statement({}); - consume(TokenType::ParenClose); - - return create_ast_node(move(select_statement), invert_expression); -} - -RefPtr Parser::parse_collate_expression(NonnullRefPtr expression) -{ - if (!match(TokenType::Collate)) - return {}; - - consume(); - ByteString collation_name = consume(TokenType::Identifier).value(); - - return create_ast_node(move(expression), move(collation_name)); -} - -RefPtr Parser::parse_is_expression(NonnullRefPtr expression) -{ - if (!match(TokenType::Is)) - return {}; - - consume(); - - bool invert_expression = false; - if (match(TokenType::Not)) { - consume(); - invert_expression = true; - } - - auto rhs = parse_expression(); - return create_ast_node(move(expression), move(rhs), invert_expression); -} - -RefPtr Parser::parse_match_expression(NonnullRefPtr lhs, bool invert_expression) -{ - auto parse_escape = [this]() { - RefPtr escape; - if (consume_if(TokenType::Escape)) { - escape = parse_expression(); - } - return escape; - }; - - if (consume_if(TokenType::Like)) { - NonnullRefPtr rhs = parse_expression(); - RefPtr escape = parse_escape(); - return create_ast_node(MatchOperator::Like, move(lhs), move(rhs), move(escape), invert_expression); - } - - if (consume_if(TokenType::Glob)) { - NonnullRefPtr rhs = parse_expression(); - RefPtr escape = parse_escape(); - return create_ast_node(MatchOperator::Glob, move(lhs), move(rhs), move(escape), invert_expression); - } - - if (consume_if(TokenType::Match)) { - NonnullRefPtr rhs = parse_expression(); - RefPtr escape = parse_escape(); - return create_ast_node(MatchOperator::Match, move(lhs), move(rhs), move(escape), invert_expression); - } - - if (consume_if(TokenType::Regexp)) { - NonnullRefPtr rhs = parse_expression(); - RefPtr escape = parse_escape(); - return create_ast_node(MatchOperator::Regexp, move(lhs), move(rhs), move(escape), invert_expression); - } - - return {}; -} - -RefPtr Parser::parse_null_expression(NonnullRefPtr expression, bool invert_expression) -{ - if (!match(TokenType::Isnull) && !match(TokenType::Notnull) && !(invert_expression && match(TokenType::Null))) - return {}; - - auto type = consume().type(); - invert_expression |= (type == TokenType::Notnull); - - return create_ast_node(move(expression), invert_expression); -} - -RefPtr Parser::parse_between_expression(NonnullRefPtr expression, bool invert_expression) -{ - if (!match(TokenType::Between)) - return {}; - - consume(); - - auto nested = parse_expression(); - if (!is(*nested)) { - expected("Binary Expression"sv); - return create_ast_node(); - } - - auto const& binary_expression = static_cast(*nested); - if (binary_expression.type() != BinaryOperator::And) { - expected("AND Expression"sv); - return create_ast_node(); - } - - return create_ast_node(move(expression), binary_expression.lhs(), binary_expression.rhs(), invert_expression); -} - -RefPtr Parser::parse_in_expression(NonnullRefPtr expression, bool invert_expression) -{ - if (!match(TokenType::In)) - return {}; - - consume(); - - if (consume_if(TokenType::ParenOpen)) { - if (match(TokenType::Select)) { - auto select_statement = parse_select_statement({}); - return create_ast_node(move(expression), move(select_statement), invert_expression); - } - - // FIXME: Consolidate this with parse_chained_expression(). That method consumes the opening paren as - // well, and also requires at least one expression (whereas this allows for an empty chain). - Vector> expressions; - if (!match(TokenType::ParenClose)) - parse_comma_separated_list(false, [&]() { expressions.append(parse_expression()); }); - - consume(TokenType::ParenClose); - - auto chain = create_ast_node(move(expressions)); - return create_ast_node(move(expression), move(chain), invert_expression); - } - - ByteString schema_name; - ByteString table_name; - parse_schema_and_table_name(schema_name, table_name); - - if (match(TokenType::ParenOpen)) { - // FIXME: Parse "table-function". - return {}; - } - - return create_ast_node(move(expression), move(schema_name), move(table_name), invert_expression); -} - -NonnullRefPtr Parser::parse_column_definition() -{ - // https://sqlite.org/syntax/column-def.html - auto name = consume(TokenType::Identifier).value(); - - auto type_name = match(TokenType::Identifier) - ? parse_type_name() - // https://www.sqlite.org/datatype3.html: If no type is specified then the column has affinity BLOB. - : create_ast_node("BLOB", Vector> {}); - - // FIXME: Parse "column-constraint". - - return create_ast_node(move(name), move(type_name)); -} - -NonnullRefPtr Parser::parse_type_name() -{ - // https: //sqlite.org/syntax/type-name.html - auto name = consume(TokenType::Identifier).value(); - Vector> signed_numbers; - - if (consume_if(TokenType::ParenOpen)) { - signed_numbers.append(parse_signed_number()); - - if (consume_if(TokenType::Comma)) - signed_numbers.append(parse_signed_number()); - - consume(TokenType::ParenClose); - } - - return create_ast_node(move(name), move(signed_numbers)); -} - -NonnullRefPtr Parser::parse_signed_number() -{ - // https://sqlite.org/syntax/signed-number.html - bool is_positive = true; - - if (consume_if(TokenType::Plus)) - is_positive = true; - else if (consume_if(TokenType::Minus)) - is_positive = false; - - if (match(TokenType::NumericLiteral)) { - auto number = consume(TokenType::NumericLiteral).double_value(); - return create_ast_node(is_positive ? number : (number * -1)); - } - - expected("NumericLiteral"sv); - return create_ast_node(0); -} - -NonnullRefPtr Parser::parse_common_table_expression() -{ - // https://sqlite.org/syntax/common-table-expression.html - auto table_name = consume(TokenType::Identifier).value(); - - Vector column_names; - if (match(TokenType::ParenOpen)) - parse_comma_separated_list(true, [&]() { column_names.append(consume(TokenType::Identifier).value()); }); - - consume(TokenType::As); - consume(TokenType::ParenOpen); - auto select_statement = parse_select_statement({}); - consume(TokenType::ParenClose); - - return create_ast_node(move(table_name), move(column_names), move(select_statement)); -} - -NonnullRefPtr Parser::parse_qualified_table_name() -{ - // https://sqlite.org/syntax/qualified-table-name.html - ByteString schema_name; - ByteString table_name; - parse_schema_and_table_name(schema_name, table_name); - - ByteString alias; - if (consume_if(TokenType::As)) - alias = consume(TokenType::Identifier).value(); - - // Note: The qualified-table-name spec may include an "INDEXED BY index-name" or "NOT INDEXED" clause. This is a SQLite extension - // "designed to help detect undesirable query plan changes during regression testing", and "application developers are admonished - // to omit all use of INDEXED BY during application design, implementation, testing, and tuning". Our implementation purposefully - // omits parsing INDEXED BY for now until there is good reason to add support. - - return create_ast_node(move(schema_name), move(table_name), move(alias)); -} - -NonnullRefPtr Parser::parse_returning_clause() -{ - // https://sqlite.org/syntax/returning-clause.html - consume(TokenType::Returning); - - if (consume_if(TokenType::Asterisk)) - return create_ast_node(); - - Vector columns; - parse_comma_separated_list(false, [&]() { - auto expression = parse_expression(); - - ByteString column_alias; - if (consume_if(TokenType::As) || match(TokenType::Identifier)) - column_alias = consume(TokenType::Identifier).value(); - - columns.append({ move(expression), move(column_alias) }); - }); - - return create_ast_node(move(columns)); -} - -NonnullRefPtr Parser::parse_result_column() -{ - // https://sqlite.org/syntax/result-column.html - if (consume_if(TokenType::Asterisk)) - return create_ast_node(); - - // If we match an identifier now, we don't know whether it is a table-name of the form "table-name.*", or if it is the start of a - // column-name-expression, until we try to parse the asterisk. So if we consume an identifier and a period, but don't find an - // asterisk, hold onto that information to form a column-name-expression later. - Optional table_name; - bool parsed_period = false; - - if (match(TokenType::Identifier)) { - table_name = consume().value(); - parsed_period = consume_if(TokenType::Period); - if (parsed_period && consume_if(TokenType::Asterisk)) - return create_ast_node(table_name.release_value()); - } - - auto expression = !table_name.has_value() - ? parse_expression() - : static_cast>(*parse_column_name_expression(move(table_name), parsed_period)); - - ByteString column_alias; - if (consume_if(TokenType::As) || match(TokenType::Identifier)) - column_alias = consume(TokenType::Identifier).value(); - - return create_ast_node(move(expression), move(column_alias)); -} - -NonnullRefPtr Parser::parse_table_or_subquery() -{ - if (++m_parser_state.m_current_subquery_depth > Limits::maximum_subquery_depth) - syntax_error(ByteString::formatted("Exceeded maximum subquery depth of {}", Limits::maximum_subquery_depth)); - - ScopeGuard guard([&]() { --m_parser_state.m_current_subquery_depth; }); - - // https://sqlite.org/syntax/table-or-subquery.html - if (match(TokenType::Identifier)) { - ByteString schema_name; - ByteString table_name; - parse_schema_and_table_name(schema_name, table_name); - - ByteString table_alias; - if (consume_if(TokenType::As) || match(TokenType::Identifier)) - table_alias = consume(TokenType::Identifier).value(); - - return create_ast_node(move(schema_name), move(table_name), move(table_alias)); - } - - // FIXME: Parse join-clause. - - Vector> subqueries; - parse_comma_separated_list(true, [&]() { subqueries.append(parse_table_or_subquery()); }); - - return create_ast_node(move(subqueries)); -} - -NonnullRefPtr Parser::parse_ordering_term() -{ - // https://sqlite.org/syntax/ordering-term.html - auto expression = parse_expression(); - - ByteString collation_name; - if (is(*expression)) { - auto const& collate = static_cast(*expression); - collation_name = collate.collation_name(); - expression = collate.expression(); - } else if (consume_if(TokenType::Collate)) { - collation_name = consume(TokenType::Identifier).value(); - } - - Order order = consume_if(TokenType::Desc) ? Order::Descending : Order::Ascending; - consume_if(TokenType::Asc); // ASC is the default, so ignore it if specified. - - Nulls nulls = order == Order::Ascending ? Nulls::First : Nulls::Last; - if (consume_if(TokenType::Nulls)) { - if (consume_if(TokenType::First)) - nulls = Nulls::First; - else if (consume_if(TokenType::Last)) - nulls = Nulls::Last; - else - expected("FIRST or LAST"sv); - } - - return create_ast_node(move(expression), move(collation_name), order, nulls); -} - -void Parser::parse_schema_and_table_name(ByteString& schema_name, ByteString& table_name) -{ - ByteString schema_or_table_name = consume(TokenType::Identifier).value(); - - if (consume_if(TokenType::Period)) { - schema_name = move(schema_or_table_name); - table_name = consume(TokenType::Identifier).value(); - } else { - table_name = move(schema_or_table_name); - } -} - -ConflictResolution Parser::parse_conflict_resolution() -{ - // https://sqlite.org/lang_conflict.html - if (consume_if(TokenType::Or)) { - if (consume_if(TokenType::Abort)) - return ConflictResolution::Abort; - if (consume_if(TokenType::Fail)) - return ConflictResolution::Fail; - if (consume_if(TokenType::Ignore)) - return ConflictResolution::Ignore; - if (consume_if(TokenType::Replace)) - return ConflictResolution::Replace; - if (consume_if(TokenType::Rollback)) - return ConflictResolution::Rollback; - - expected("ABORT, FAIL, IGNORE, REPLACE, or ROLLBACK"sv); - } - - return ConflictResolution::Abort; -} - -Token Parser::consume() -{ - auto old_token = m_parser_state.m_token; - m_parser_state.m_token = m_parser_state.m_lexer.next(); - return old_token; -} - -Token Parser::consume(TokenType expected_type) -{ - if (!match(expected_type)) { - expected(Token::name(expected_type)); - } - return consume(); -} - -bool Parser::consume_if(TokenType expected_type) -{ - if (!match(expected_type)) - return false; - - consume(); - return true; -} - -bool Parser::match(TokenType type) const -{ - return m_parser_state.m_token.type() == type; -} - -void Parser::expected(StringView what) -{ - syntax_error(ByteString::formatted("Unexpected token {}, expected {}", m_parser_state.m_token.name(), what)); -} - -void Parser::syntax_error(ByteString message) -{ - m_parser_state.m_errors.append({ move(message), position() }); -} - -SourcePosition Parser::position() const -{ - return m_parser_state.m_token.start_position(); -} - -Parser::ParserState::ParserState(Lexer lexer) - : m_lexer(move(lexer)) - , m_token(m_lexer.next()) -{ -} - -} diff --git a/Userland/Libraries/LibSQL/AST/Parser.h b/Userland/Libraries/LibSQL/AST/Parser.h deleted file mode 100644 index 2ec59354897..00000000000 --- a/Userland/Libraries/LibSQL/AST/Parser.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2021, Tim Flynn - * Copyright (c) 2021, Mahmoud Mandour - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace SQL::AST { - -namespace Limits { -// https://www.sqlite.org/limits.html -constexpr size_t maximum_expression_tree_depth = 1000; -constexpr size_t maximum_subquery_depth = 100; -constexpr size_t maximum_bound_parameters = 1000; -} - -class Parser { - struct Error { - ByteString message; - SourcePosition position; - - ByteString to_byte_string() const - { - return ByteString::formatted("{} (line: {}, column: {})", message, position.line, position.column); - } - }; - -public: - explicit Parser(Lexer lexer); - - NonnullRefPtr next_statement(); - - bool has_errors() const { return m_parser_state.m_errors.size(); } - Vector const& errors() const { return m_parser_state.m_errors; } - -protected: - NonnullRefPtr parse_expression(); // Protected for unit testing. - -private: - struct ParserState { - explicit ParserState(Lexer); - - Lexer m_lexer; - Token m_token; - Vector m_errors; - size_t m_current_expression_depth { 0 }; - size_t m_current_subquery_depth { 0 }; - size_t m_bound_parameters { 0 }; - }; - - NonnullRefPtr parse_statement(); - NonnullRefPtr parse_statement_with_expression_list(RefPtr); - NonnullRefPtr parse_create_schema_statement(); - NonnullRefPtr parse_create_table_statement(); - NonnullRefPtr parse_alter_table_statement(); - NonnullRefPtr parse_drop_table_statement(); - NonnullRefPtr parse_describe_table_statement(); - NonnullRefPtr parse_insert_statement(RefPtr); - NonnullRefPtr parse_update_statement(RefPtr); - NonnullRefPtr parse_delete_statement(RefPtr); - NonnullRefPtr