diff --git a/.gitignore b/.gitignore index e257658..8e24b65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,2 @@ -# ---> C++ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - +.idea +cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..27dd1bf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.26) +project(query) + +set(CMAKE_CXX_STANDARD 17) + +include_directories(${PROJECT_SOURCE_DIR}/include) + +add_subdirectory(src) + +add_executable(query main.cpp) +target_link_libraries(query matador) diff --git a/include/matador/sql/column.hpp b/include/matador/sql/column.hpp new file mode 100644 index 0000000..c1ab806 --- /dev/null +++ b/include/matador/sql/column.hpp @@ -0,0 +1,31 @@ +#ifndef QUERY_COLUMN_HPP +#define QUERY_COLUMN_HPP + +#include "matador/sql/types.hpp" + +#include "matador/utils/field_attributes.hpp" + +#include + +namespace matador::sql { + +class column { +public: + template + explicit column(std::string name, utils::field_attributes attr = utils::null_attributes) + : column(std::move(name), data_type_traits::data_type(), attr) + {} + column(std::string name, data_type_t type, utils::field_attributes attr = utils::null_attributes); + + [[nodiscard]] const std::string& name() const; + [[nodiscard]] const utils::field_attributes& attributes() const; + [[nodiscard]] data_type_t type() const; + +private: + std::string name_; + utils::field_attributes attributes_; + data_type_t type_{}; + std::any value_; +}; +} +#endif //QUERY_COLUMN_HPP diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp new file mode 100644 index 0000000..1a200e1 --- /dev/null +++ b/include/matador/sql/dialect.hpp @@ -0,0 +1,152 @@ +#ifndef QUERY_DIALECT_HPP +#define QUERY_DIALECT_HPP + +#include +#include +#include + +namespace matador::sql { + +class dialect +{ +public: + enum class token_t : uint8_t + { + CREATE = 0, + DROP, + REMOVE, + INSERT, + UPDATE, + SELECT, + TABLE, + VALUES, + VALUE, + COLUMNS, + COLUMN, + FROM, + INTO, + WHERE, + AND, + OR, + LIKE, + ORDER_BY, + GROUP_BY, + ASC, + DESC, + TOP, + AS, + OFFSET, + DISTINCT, + CONDITION, + SET, + NOT_NULL, + PRIMARY_KEY, + BEGIN, + COMMIT, + ROLLBACK, + START_QUOTE, + END_QUOTE, + STRING_QUOTE, + NONE + }; + + /** + * Holding enums concerning escaping identifiers + */ + enum class escape_identifier_t : uint8_t { + ESCAPE_BOTH_SAME, /**< The escape quotes are the same */ + ESCAPE_CLOSING_BRACKET /**< The escape quotes differ; escape the closing one */ + }; + +public: + const std::string& token_at(token_t token) const; + + /** + * Prepare sql dialect identifier for execution + * and escape quotes and quote the identifier + * string + * + * @param str The identifier string to be prepared + * @return The prepared string + */ + std::string prepare_identifier(const std::string &str) const; + + /** + * Prepare string literal + * + * @param str String literal to be prepared + */ + std::string prepare_literal(const std::string &str) const; + + /** + * Wrap identifier quotes around a sql identifier keyword + * + * @param str Identifier to put quotes around + */ + void quote_identifier(std::string &str) const; + + /** + * Escape identifier quotes inside identifiers. + * + * @param str Identifier to be escaped + */ + void escape_quotes_in_identifier(std::string &str) const; + + /** + * Escape quotes in string literals + * + * @param str String literal to be escaped + */ + void escape_quotes_in_literals(std::string &str) const; + + /** + * Returns how the identifier quotes should be + * escaped. + * + * @return How the identifier quotes should be escaped + */ + virtual escape_identifier_t identifier_escape_type() const; + +private: + using token_to_string_map = std::unordered_map; + + token_to_string_map tokens_ { + {token_t::CREATE, "CREATE"}, + {token_t::DROP, "DROP"}, + {token_t::REMOVE, "DELETE"}, + {token_t::INSERT, "INSERT"}, + {token_t::INTO, "INTO"}, + {token_t::VALUES, "VALUES"}, + {token_t::UPDATE, "UPDATE"}, + {token_t::SELECT, "SELECT"}, + {token_t::COLUMNS, "COLUMNS"}, + {token_t::COLUMN, "COLUMN"}, + {token_t::FROM, "FROM"}, + {token_t::WHERE, "WHERE"}, + {token_t::AND, "AND"}, + {token_t::OR, "OR"}, + {token_t::LIKE, "LIKE"}, + {token_t::ORDER_BY, "ORDER BY"}, + {token_t::GROUP_BY, "GROUP BY"}, + {token_t::ASC, "ASC"}, + {token_t::DESC, "DESC"}, + {token_t::TOP, "LIMIT"}, + {token_t::AS, "AS"}, + {token_t::OFFSET, "OFFSET"}, + {token_t::DISTINCT, "DISTINCT"}, + {token_t::SET, "SET"}, + {token_t::NOT_NULL, "NOT NULL"}, + {token_t::PRIMARY_KEY, "PRIMARY KEY"}, + {token_t::BEGIN, "BEGIN TRANSACTION"}, + {token_t::COMMIT, "COMMIT TRANSACTION"}, + {token_t::ROLLBACK, "ROLLBACK TRANSACTION"}, + {token_t::START_QUOTE, "\""}, + {token_t::END_QUOTE, "\""}, + {token_t::STRING_QUOTE, "'"}, + {token_t::NONE, ""} + }; +}; + +} + +#endif //QUERY_DIALECT_HPP diff --git a/include/matador/sql/query_builder.hpp b/include/matador/sql/query_builder.hpp new file mode 100644 index 0000000..05b75a3 --- /dev/null +++ b/include/matador/sql/query_builder.hpp @@ -0,0 +1,84 @@ +#ifndef QUERY_QUERY_BUILDER_HPP +#define QUERY_QUERY_BUILDER_HPP + +#include "matador/sql/column.hpp" + +#include +#include +#include +#include + +namespace matador::sql { + +class dialect; + +class query_builder +{ +private: + enum class state_t { + QUERY_INIT, + QUERY_CREATE, + QUERY_TABLE, + QUERY_DROP, + QUERY_SELECT, + QUERY_INSERT, + QUERY_UPDATE, + QUERY_DELETE, + QUERY_SET, + QUERY_FROM, + QUERY_INTO, + QUERY_WHERE, + QUERY_VALUES, + QUERY_ORDER_BY, + QUERY_ORDER_DIRECTION, + QUERY_GROUP_BY, + QUERY_LIMIT, + QUERY_FINISH + }; + + enum class command_t { + UNKNOWN, /**< Unknown query command */ + CREATE, /**< Create query command */ + DROP, /**< Drop query command */ + SELECT, /**< Select query command */ + INSERT, /**< Insert query command */ + UPDATE, /**< Update query command */ + REMOVE /**< Remove query command */ + }; + +public: + explicit query_builder(const dialect &d); + + query_builder& create(); + query_builder& table(const std::string &table, std::initializer_list columns); + + query_builder& select(std::initializer_list column_names); + + query_builder& from(const std::string &table, const std::string &as = ""); + + std::string compile(); + +private: + void transition_to(state_t next); + void initialize(command_t cmd, state_t state); + +private: + const dialect &dialect_; + + command_t command_{command_t::UNKNOWN}; + state_t state_{state_t::QUERY_INIT}; + + std::vector query_parts_; + + using query_state_set = std::unordered_set; + using query_state_transition_map = std::unordered_map; + using query_state_to_string_map = std::unordered_map; + using query_command_to_string_map = std::unordered_map; + + static query_state_transition_map transitions_; + static query_state_to_string_map state_strings_; + static query_command_to_string_map command_strings_; +}; + +} +#endif //QUERY_QUERY_BUILDER_HPP diff --git a/include/matador/sql/types.hpp b/include/matador/sql/types.hpp new file mode 100644 index 0000000..ad38184 --- /dev/null +++ b/include/matador/sql/types.hpp @@ -0,0 +1,215 @@ +#ifndef QUERY_TYPES_HPP +#define QUERY_TYPES_HPP + +#include +#include + +namespace matador::sql { + +/** + * @brief Enumeration type of all supported builtin data types + */ +enum class data_type_t : uint8_t { + type_char = 0, /*!< Data type char */ + type_short, /*!< Data type short */ + type_int, /*!< Data type int */ + type_long, /*!< Data type long */ + type_long_long, /*!< Data type long long */ + type_unsigned_char, /*!< Data type unsigned char */ + type_unsigned_short, /*!< Data type unsigned short */ + type_unsigned_int, /*!< Data type unsigned int */ + type_unsigned_long, /*!< Data type unsigned long */ + type_unsigned_long_long, /*!< Data type unsigned long long */ + type_float, /*!< Data type float */ + type_double, /*!< Data type double */ + type_bool, /*!< Data type bool */ + type_char_pointer, /*!< Data type character pointer */ + type_varchar, /*!< Data type varchar */ + type_text, /*!< Data type text */ + type_date, /*!< Data type date */ + type_time, /*!< Data type time */ + type_blob, /*!< Data type blob */ + type_null, /*!< Data type null */ + type_unknown /*!< Data type unknown */ +}; +/** + * @brief Enumeration of database data types + */ +enum struct database_type_t : uint8_t { + type_char = 0, /*!< Data type char */ + type_float, /*!< Data type float */ + type_double, /*!< Data type double */ + type_smallint, /*!< Data type small int */ + type_int, /*!< Data type integer */ + type_bigint, /*!< Data type big integer */ + type_bool, /*!< Data type bool */ + type_char_pointer, /*!< Data type character pointer */ + type_varchar, /*!< Data type varchar */ + type_text, /*!< Data type text */ + type_date, /*!< Data type date */ + type_time, /*!< Data type time */ + type_blob, /*!< Data type blob */ + type_null, /*!< Data type null */ + type_unknown /*!< Data type unknown */ +}; + +/** + * @tparam T The type of the traits + * @brief Type traits for database types + * + * This class is used to determine and + * provide the correct size information + * for a data type + */ +template < class T > +struct data_type_traits; + +/// @cond MATADOR_DEV +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_char; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; } + inline static unsigned long size() { return sizeof(char); } + inline static const char* name() { return "char"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_smallint; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_short; } + inline static unsigned long size() { return sizeof(short); } + inline static const char* name() { return "short"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_int; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; } + inline static unsigned long size() { return sizeof(int); } + inline static const char* name() { return "int"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_bigint; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long; } + inline static unsigned long size() { return sizeof(long); } + inline static const char* name() { return "long"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_bigint; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long_long; } + inline static unsigned long size() { return sizeof(long long); } + inline static const char* name() { return "long long"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_char; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_char; } + inline static unsigned long size() { return sizeof(unsigned char); } + inline static const char* name() { return "unsigned char"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_smallint; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_short; } + inline static unsigned long size() { return sizeof(unsigned short); } + inline static const char* name() { return "unsigned short"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_int; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_int; } + inline static unsigned long size() { return sizeof(unsigned int); } + inline static const char* name() { return "unsigned int"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/ = 0) { return database_type_t::type_bigint; } + inline static data_type_t builtin_type(std::size_t /*size*/ = 0) { return data_type_t::type_unsigned_long; } + inline static unsigned long size() { return sizeof(unsigned long); } + inline static const char* name() { return "unsigned long"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_bigint; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_long_long; } + inline static unsigned long size() { return sizeof(unsigned long long); } + inline static const char* name() { return "unsigned long long"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_bool; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_bool; } + inline static unsigned long size() { return sizeof(bool); } + inline static const char* name() { return "bool"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_float; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_float; } + inline static unsigned long size() { return sizeof(float); } + inline static const char* name() { return "float"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_double; } + inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_double; } + inline static unsigned long size() { return sizeof(double); } + inline static const char* name() { return "double"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; } + inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } + inline static unsigned long size() { return sizeof(const char*); } + inline static const char* name() { return "const char*"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; } + inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } + inline static unsigned long size() { return sizeof(char*); } + inline static const char* name() { return "char*"; } +}; + +template <> struct data_type_traits +{ + inline static database_type_t type(std::size_t size) { return size == 0 ? database_type_t::type_text : database_type_t::type_varchar; } + inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } + inline static unsigned long size() { return 1023; } + inline static const char* name() { return "std::string"; } +}; + +//template <> struct data_type_traits +//{ +// inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_date; } +// inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_date; } +// inline static unsigned long size() { return 255; } +// inline static const char* name() { return "matador::date"; } +//}; +// +//template <> struct data_type_traits +//{ +// inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_time; } +// inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_time; } +// inline static unsigned long size() { return 255; } +// inline static const char* name() { return "matador::time"; } +//}; + +/// @endcond + +} +#endif //QUERY_TYPES_HPP diff --git a/include/matador/utils/constraints.hpp b/include/matador/utils/constraints.hpp new file mode 100644 index 0000000..e8555f7 --- /dev/null +++ b/include/matador/utils/constraints.hpp @@ -0,0 +1,35 @@ +#ifndef QUERY_CONSTRAINTS_HPP +#define QUERY_CONSTRAINTS_HPP + +namespace matador::utils { + +enum class constraints : unsigned char { + NONE = 0, + NOT_NULL = 1 << 0, + INDEX = 1 << 1, + UNIQUE = 1 << 2, + PRIMARY_KEY = 1 << 3, + FOREIGN_KEY = 1 << 4, + DEFAULT = 1 << 5, + UNIQUE_NOT_NULL = UNIQUE | NOT_NULL +}; + +//static std::unordered_map constraints_to_name_map(); + +inline constraints operator|(constraints a, constraints b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline constraints operator&(constraints a, constraints b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline bool is_constraint_set(constraints source, constraints needle) +{ + return static_cast(source & needle) > 0; +} + +} +#endif //QUERY_CONSTRAINTS_HPP diff --git a/include/matador/utils/field_attributes.hpp b/include/matador/utils/field_attributes.hpp new file mode 100644 index 0000000..1ae7717 --- /dev/null +++ b/include/matador/utils/field_attributes.hpp @@ -0,0 +1,34 @@ +#ifndef QUERY_FIELD_ATTRIBUTES_HPP +#define QUERY_FIELD_ATTRIBUTES_HPP + +#include "constraints.hpp" + +#include + +namespace matador::utils { + +class field_attributes +{ +public: + field_attributes() = default; + field_attributes(size_t size); // NOLINT(*-explicit-constructor) + field_attributes(constraints options); // NOLINT(*-explicit-constructor) + field_attributes(size_t size, constraints options); + field_attributes(const field_attributes &x) = default; + field_attributes& operator=(const field_attributes &x) = default; + field_attributes(field_attributes &&x) = default; + field_attributes& operator=(field_attributes &&x) = default; + ~field_attributes() = default; + + [[nodiscard]] size_t size() const; + [[nodiscard]] constraints options() const; + +private: + size_t size_ = 0; + constraints options_ = constraints::NONE; +}; + +const field_attributes null_attributes {}; + +} +#endif //QUERY_FIELD_ATTRIBUTES_HPP diff --git a/include/matador/utils/string.hpp b/include/matador/utils/string.hpp new file mode 100644 index 0000000..2cba75f --- /dev/null +++ b/include/matador/utils/string.hpp @@ -0,0 +1,19 @@ +#ifndef QUERY_STRING_HPP +#define QUERY_STRING_HPP + +#include + +namespace matador::utils { + +/** + * Replaces all occurrences of string from in given string + * with string to. + * + * @param in Source string where the replacement takes place + * @param from The string to be replaced + * @param to The new string + */ +void replace_all(std::string &in, const std::string &from, const std::string &to); + +} +#endif //QUERY_STRING_HPP diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2b34626 --- /dev/null +++ b/main.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +#include + +using namespace matador; + +int main() { + sql::dialect d; + + sql::query_builder query(d); + + query.create().table("person", { + { "id", sql::data_type_traits::builtin_type() }, + { "name", sql::data_type_traits::builtin_type(255), 255 }, + { "id", sql::data_type_traits::builtin_type() } + }).compile(); + + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..8f92446 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,22 @@ +set(SQL_SOURCES + sql/dialect.cpp + sql/query_builder.cpp + sql/column.cpp) + +set(SQL_HEADER + ../include/matador/sql/dialect.hpp + ../include/matador/sql/query_builder.hpp + ../include/matador/sql/column.hpp + ../include/matador/sql/types.hpp) + +set(UTILS_HEADER + ../include/matador/utils/field_attributes.hpp + ../include/matador/utils/string.hpp + ../include/matador/utils/constraints.hpp) + +set(UTILS_SOURCES + utils/field_attributes.cpp + utils/string.cpp) + +add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER}) +set_target_properties(matador PROPERTIES LINKER_LANGUAGE CXX) diff --git a/src/sql/column.cpp b/src/sql/column.cpp new file mode 100644 index 0000000..e45356e --- /dev/null +++ b/src/sql/column.cpp @@ -0,0 +1,23 @@ +#include + +#include "matador/sql/column.hpp" + +namespace matador::sql { +column::column(std::string name, data_type_t type, utils::field_attributes attr) +: name_(std::move(name)) +, type_(type) +, attributes_(attr) {} + +const std::string &column::name() const { + return name_; +} + +const utils::field_attributes &column::attributes() const { + return attributes_; +} + +data_type_t column::type() const { + return type_; +} + +} \ No newline at end of file diff --git a/src/sql/dialect.cpp b/src/sql/dialect.cpp new file mode 100644 index 0000000..dfe99cd --- /dev/null +++ b/src/sql/dialect.cpp @@ -0,0 +1,53 @@ +#include "matador/sql/dialect.hpp" + +#include "matador/utils/string.hpp" + +namespace matador::sql { +const std::string& dialect::token_at(dialect::token_t token) const { + return tokens_.at(token); +} + +std::string dialect::prepare_identifier(const std::string &str) const +{ + std::string result(str); + escape_quotes_in_identifier(result); + quote_identifier(result); + return result; +} + +std::string dialect::prepare_literal(const std::string &str) const +{ + std::string result(str); + escape_quotes_in_literals(result); + return result; +} + +void dialect::quote_identifier(std::string &str) const +{ + str.insert(0, token_at(token_t::START_QUOTE)); + str += token_at(token_t::END_QUOTE); +} + +void dialect::escape_quotes_in_identifier(std::string &str) const +{ + const std::string open_char(token_at(token_t::START_QUOTE)); + std::string close_char(token_at(token_t::END_QUOTE)); + if (identifier_escape_type() == escape_identifier_t::ESCAPE_CLOSING_BRACKET) { + utils::replace_all(str, close_char, close_char + close_char); + } else { + utils::replace_all(str, open_char, open_char + open_char); + } +} + +void dialect::escape_quotes_in_literals(std::string &str) const +{ + const std::string single_quote(token_at(token_t::STRING_QUOTE)); + const std::string double_quote(token_at(token_t::STRING_QUOTE) + token_at(token_t::STRING_QUOTE)); + utils::replace_all(str, single_quote, double_quote); +} + +dialect::escape_identifier_t dialect::identifier_escape_type() const { + return dialect::escape_identifier_t::ESCAPE_BOTH_SAME; +} + +} \ No newline at end of file diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp new file mode 100644 index 0000000..87276b7 --- /dev/null +++ b/src/sql/query_builder.cpp @@ -0,0 +1,119 @@ +#include "matador/sql/query_builder.hpp" +#include "matador/sql/dialect.hpp" + +#include + +namespace matador::sql { + +// poor mens state machine +// but does the job for query +query_builder::query_state_transition_map query_builder::transitions_{ + {state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}}, + {state_t::QUERY_CREATE, {state_t::QUERY_TABLE}}, + {state_t::QUERY_DROP, {state_t::QUERY_TABLE}}, + {state_t::QUERY_SELECT, {state_t::QUERY_FROM}}, + {state_t::QUERY_INSERT, {state_t::QUERY_INTO}}, + {state_t::QUERY_UPDATE, {state_t::QUERY_SET}}, + {state_t::QUERY_DELETE, {state_t::QUERY_FROM}}, + {state_t::QUERY_TABLE, {state_t::QUERY_FINISH}}, + {state_t::QUERY_FROM, {state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}}, + {state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}}, + {state_t::QUERY_INTO, {state_t::QUERY_VALUES}}, + {state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, + {state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}}, + {state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, + {state_t::QUERY_GROUP_BY, {state_t::QUERY_FINISH}}, + {state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}}, + {state_t::QUERY_VALUES, {state_t::QUERY_FINISH}}, + {state_t::QUERY_FINISH, {}}, +}; + +query_builder::query_state_to_string_map query_builder::state_strings_ { + { state_t::QUERY_INIT, "init" }, + { state_t::QUERY_CREATE, "create" }, + { state_t::QUERY_DROP, "drop" }, + { state_t::QUERY_SELECT, "select" }, + { state_t::QUERY_INSERT, "insert" }, + { state_t::QUERY_UPDATE, "update" }, + { state_t::QUERY_DELETE, "delete" }, + { state_t::QUERY_TABLE, "table" }, + { state_t::QUERY_FROM, "from" }, + { state_t::QUERY_SET, "set" }, + { state_t::QUERY_INTO, "into" }, + { state_t::QUERY_WHERE, "where" }, + { state_t::QUERY_ORDER_BY, "order_by" }, + { state_t::QUERY_GROUP_BY, "group_by" }, + { state_t::QUERY_LIMIT, "limit" }, + { state_t::QUERY_FINISH, "finish" }, +}; + +query_builder::query_command_to_string_map query_builder::command_strings_ { + { command_t::UNKNOWN, "unknown" }, + { command_t::CREATE, "create" }, + { command_t::DROP, "drop" }, + { command_t::SELECT, "select" }, + { command_t::INSERT, "insert" }, + { command_t::UPDATE, "update" }, + { command_t::REMOVE, "remove" }, +}; + +query_builder::query_builder(const dialect &d) +: dialect_(d) {} + +query_builder &query_builder::create() { + initialize(command_t::CREATE, state_t::QUERY_CREATE); + + query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE) + " "); + + return *this; +} + +query_builder &query_builder::table(const std::string &table, std::initializer_list columns) { + transition_to(state_t::QUERY_TABLE); + + query_parts_.emplace_back(dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); +// auto cols = + + return *this; +} + +query_builder &query_builder::select(std::initializer_list column_names) { + initialize(command_t::SELECT, state_t::QUERY_SELECT); + + query_parts_.emplace_back(dialect_.token_at(dialect::token_t::SELECT) + " "); + + return *this; +} + +query_builder &query_builder::from(const std::string &table, const std::string &as) { + transition_to(state_t::QUERY_FROM); + + query_parts_.emplace_back(dialect_.token_at(dialect::token_t::TABLE) + " " + (as.empty() ? "" : as + " ")); + + return *this; +} + +std::string query_builder::compile() { + std::string result; + for (const auto &part : query_parts_) { + result.append(part); + } + return result; +} + +void query_builder::transition_to(query_builder::state_t next) +{ + if (transitions_[state_].count(next) == 0) { + throw std::logic_error("invalid next state " + state_strings_[next]); + } + state_ = next; +} + +void query_builder::initialize(query_builder::command_t cmd, query_builder::state_t state) +{ + command_ = cmd; + state_ = state; + query_parts_.clear(); +} + +} \ No newline at end of file diff --git a/src/utils/field_attributes.cpp b/src/utils/field_attributes.cpp new file mode 100644 index 0000000..2adfebf --- /dev/null +++ b/src/utils/field_attributes.cpp @@ -0,0 +1,28 @@ +#include "matador/utils/field_attributes.hpp" + +namespace matador::utils { + +field_attributes::field_attributes(size_t size) + : size_(size) +{} + +field_attributes::field_attributes(constraints options) + : options_(options) +{} + +field_attributes::field_attributes(size_t size, constraints options) + : size_(size) + , options_(options) +{} + +size_t field_attributes::size() const +{ + return size_; +} + +constraints field_attributes::options() const +{ + return options_; +} + +} \ No newline at end of file diff --git a/src/utils/string.cpp b/src/utils/string.cpp new file mode 100644 index 0000000..e57ba86 --- /dev/null +++ b/src/utils/string.cpp @@ -0,0 +1,17 @@ +#include "matador/utils/string.hpp" + +namespace matador::utils { + +void replace_all(std::string &in, const std::string &from, const std::string &to) +{ + if(from.empty()) { + return; + } + size_t start_pos = 0; + while((start_pos = in.find(from, start_pos)) != std::string::npos) { + in.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } +} + +} \ No newline at end of file