Browse Source

初级select

myuan 2 years ago
parent
commit
a9a13f8579
6 changed files with 110 additions and 21 deletions
  1. 21 5
      run_sql_parser_test.py
  2. 71 11
      src/checker.cpp
  3. 0 0
      src/checker.h
  4. 7 2
      src/utils.cpp
  5. 9 1
      src/utils.h
  6. 2 2
      tests_config.py

+ 21 - 5
run_sql_parser_test.py

@@ -7,11 +7,11 @@ from datetime import datetime
 import orjson
 import os
 import tempfile
-from tests_config import sql_parser_tests
+from tests_config import sql_parser_tests, sql_checker_tests
 
 
 async def run_and_output(
-    *args: str, timeout=5
+    *args: str, timeout=10
 ) -> tuple[bytes, bytes]:
     p = await subprocess.create_subprocess_exec(
         *args,
@@ -75,11 +75,27 @@ async def on_parser_modified():
         print(datetime.now(), colored("all parser tests right!", "green"))
 
 
+async def assert_check():
+    for sql, res in sql_checker_tests:
+        stdout, stderr = await run_and_output(
+            'xmake', 'run', "sql-checker", 
+            "-s", sql
+        )
+        if res is True:
+            assert b'error' not in stdout, stdout.decode("utf-8")
+            assert b'error' not in stderr, stderr.decode('utf-8')
+        elif isinstance(res, str):
+            res = res.encode('utf-8') 
+            assert res in stdout or res in stderr, stdout.decode("utf-8")
+        else:
+            assert False, f"{res} 不是合适的结果"
+
 async def on_checker_modified():
     print(datetime.now(), colored("run checker tests...", "yellow"))
-    stdout, stderr = await run_and_output('xmake', 'run', "sql-checker")
-    print(stdout.decode("utf-8"))
-    print(stderr.decode("utf-8"))
+    try:
+        await assert_check()
+    except Exception as e:
+        print(e)
     print(datetime.now(), colored("all checker tests right!", "green"))
 
 

+ 71 - 11
src/checker.cpp

@@ -2,33 +2,93 @@
 #include <stdio.h>
 
 #include <CLI/CLI.hpp>
+#include <optional>
 
 #include "utils.h"
 
 using json = nlohmann::json;
-using string = std::string;
+using std::nullopt;
+using std::optional;
+using std::string;
+
 auto tables = ExistTables("./tables.json");
 
-void create_table(const std::string& table_name, const TableCols& cols) {
-    tables.set(table_name, cols);
+void create_table(const json& j) {
+    auto table_name = table_name_of(j);
+
+    if (tables.exists(table_name)) {
+        throw std::runtime_error(
+            fmt::format("table `{}` exists\n", table_name));
+    }
+    tables.set(table_name, j["cols"]);
     tables.save();
 }
 
+optional<TableCol> get_column_define(const string table_name,
+                                     const json& select_col) {
+    auto type = type_of(select_col);
+    if (type == "identifier") {
+        auto cols = tables[table_name];
+        string colname = select_col["value"];
+        for (auto& col : cols) {
+            if (col.column_name == colname) {
+                return col;
+            }
+        }
+        return nullopt;
+    } else if (type == "int" || type == "string" || type == "float") {
+        return TableCol{.column_name = select_col["value"].dump(),
+                        .data_type = type,
+                        .type = "const",
+                        .primary_key = false};
+    } else if (type == "select_all_column") {
+        return TableCol{.column_name = "*",
+                        .data_type = "",
+                        .type = "select_all_column",
+                        .primary_key = false};
+    }
+    return nullopt;
+}
+
+void check_select_table(const json& j) {
+    auto table_name = table_name_of(j);
+    auto select_cols = j["select_cols"];
+
+    for (auto& select_col : select_cols) {
+        if (select_col["type"] == "select_all_column") {
+            continue;
+        }
+        auto col_def = get_column_define(table_name, select_col["target"]);
+
+        if (!col_def.has_value()) {
+            throw std::runtime_error(
+                fmt::format("column `{}` not exists in `{}`\n",
+                            select_col["target"].dump(2), table_name));
+        }
+    }
+}
+
 void process_sql(json& j) {
     auto type = j.value("type", "none");
 
     if (type == "create_stmt") {
-        auto table_name = j["table_name"];
-        if (tables.exists(table_name)) {
-            fmt::print("error: table `{}` exists\n", table_name);
+        // 创建表
+        create_table(j);
+    } else {
+        // 其余都会要求表存在
+        auto table_name = table_name_of(j);
+        if (!tables.exists(table_name)) {
+            fmt::print("error: table `{}` does not exist\n", table_name);
+            return;
+        }
+
+        if (type == "select_stmt") {
+            check_select_table(j);
         } else {
-            create_table(j["table_name"], j["cols"]);
+            throw std::runtime_error(fmt::format(
+                "Unknown expression type `{}` total: \n{}", type, j.dump(2)));
         }
-    } else {
-        throw std::runtime_error(
-            fmt::format("Unknown expression type `{}` total: \n{}", type, j.dump(2)));
     }
-
 }
 
 int main(int argc, char** argv) {

+ 0 - 0
src/checker.h


+ 7 - 2
src/utils.cpp

@@ -12,8 +12,10 @@ using json = nlohmann::json;
 
 SQLParserRes parse_sql(const std::string& sql) {
     SQLParserRes res;
-    auto stdout_name = std::tmpnam(nullptr);
-    auto stderr_name = std::tmpnam(nullptr);
+    // auto stdout_name = std::tmpnam(nullptr);
+    // auto stderr_name = std::tmpnam(nullptr);
+    auto stdout_name = "/tmp/sql-parser-stdout";
+    auto stderr_name = "/tmp/sql-parser-stderr";
 
     auto cmd = fmt::format("xmake run sql-parser \"{}\" > {} 2> {}", sql,
                            stdout_name, stderr_name);
@@ -58,6 +60,9 @@ bool ExistTables::exists(const std::string& table_name) {
 void ExistTables::set(const std::string& table_name, const TableCols& cols) {
     tables[table_name] = cols;
 }
+TableCols ExistTables::operator[](const std::string& table_name) {
+    return tables[table_name];
+}
 void ExistTables::remove(const std::string& table_name) {
     tables.erase(table_name);
 }

+ 9 - 1
src/utils.h

@@ -21,7 +21,6 @@ struct TableCol {
 
 using TableCols = std::vector<TableCol>;
 
-
 SQLParserRes parse_sql(const std::string& sql);
 
 class ExistTables {
@@ -33,6 +32,15 @@ class ExistTables {
     ExistTables* read_from_file();
     bool exists(const std::string& table_name);
     void set(const std::string& table_name, const TableCols& cols);
+    TableCols operator[](const std::string& table_name);
     void remove(const std::string& table_name);
     void save();
 };
+
+inline auto table_name_of(const nlohmann::json& j) {
+    return j.value("table_name", "none");
+}
+inline auto type_of(const nlohmann::json& j) {
+    return j.value("type", "none");
+}
+

+ 2 - 2
tests_config.py

@@ -489,6 +489,6 @@ sql_parser_tests = [
 ]
 
 sql_checker_tests = [
-    ('select * from t1 where t1.c1 = t2.c2;', True),
-    
+    ('select c1 from t1 ;', True),
+    ('select * from t1;', True),
 ]