diff --git a/test/CMakeLists.txt b/CMakeLists.txt similarity index 50% rename from test/CMakeLists.txt rename to CMakeLists.txt index fcbefab..10d3108 100644 --- a/test/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,19 +4,27 @@ set(CMAKE_MACOSX_RPATH 0) project(sql-builder) -set(DEBUG_FLAGS "-std=c++11 -g -O1 -Wall -Wextra -pedantic") -set(RELEASE_FLAGS "-std=c++11 -O3 -Wall -Wextra -pedantic") +# set(DEBUG_FLAGS "-std=c++11 -g -O1 -Wall -Wextra -pedantic") +# set(RELEASE_FLAGS "-std=c++11 -O3 -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS ${RELEASE_FLAGS}) set(CMAKE_CXX_FLAGS_DEBUG ${DEBUG_FLAGS}) set(CMAKE_CONFIGURATION_TYPES Debug Release) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) -include_directories(sql-test "../") +include_directories(.) + +set(SQL_TEST_SRC test/test.cpp + adapter.hpp + col.hpp + insert_model.hpp + delete_model.hpp + model.hpp + select_model.hpp + update_model.hpp) -set(SQL_TEST_SRC test.cpp) add_executable(sql-test ${SQL_TEST_SRC}) add_test(all "sql-test") -enable_testing() +enable_testing() \ No newline at end of file diff --git a/README.md b/README.md index 6acfce7..067c330 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,68 @@ ## Examples: ``` c++ - using namespace sql; - - InsertModel i; - i.insert("score", 100) - ("name", std::string("six")) - ("age", (unsigned char)20) - ("address", "beijing") - ("create_time", nullptr) - .into("user"); - std::cout< 60 and (column("age") >= 20 or column("address").is_not_null())) - .group_by("age") - .having(column("age") > 10) - .order_by("age desc") - .limit(10) - .offset(1); - std::cout< 60) and ((age >= 20) or (address is not null)) group by age having age > 10 order by age desc limit 10 offset 1 - - std::vector a = {1, 2, 3}; - UpdateModel u; - u.update("user") - .set("name", "ddc") - ("age", 18) - ("score", nullptr) - ("address", "beijing") - .where(column("id").in(a)); - std::cout< +#include + +#include "col.hpp" +#include "select_model.hpp" +#include "update_model.hpp" +#include "delete_model.hpp" +#include "insert_model.hpp" +#include "adapter.hpp" + +/* +create table if not exists user ( + `id` int(10) unsigned not null auto_increment, + `age` tinyint(8) unsigned, + `score` int(10) unsigned not null default 0, + `name` varchar(128) not null default '', + `address` varchar(256), + `create_time` datetime not null, + primary key(`id`) +) +*/ + +using namespace boosql; + +int main() +{ + shared_ptr a = make_shared(); + + select_model selector(a); + selector.from("users") + .select(col("*")) + .where(boosql::col("hello")["%hello"]) // like + .quote([](select_model & model) { + model.where(col("id") != 1).or_where(col("id") != 2); + }); + select_model group(a); + + group.from("group").select(col("a"), col("b"), col("c")).where(col("a") == 2); + + selector.left_join(group).on("hello")("=", col("a")).or_on("id")("=", col("b")).end(); + + selector.group_by(col("hello")).order_by(col("hello")("DESC")); + cout << selector.str() << endl; + + update_model updater(a); + updater.update("users")("hello", "hello")("world", "world").where(col("id") == 2); + cout << updater.str() << endl; + + delete_model deleter(a); + deleter.from("users").where(col("id") == 1).or_where(col("name")["%hello"]); + cout << deleter.str() << endl; + + insert_model insert(a); + insert.into("users") + ("id", 1) + ("name", "hello") + .next_row() + ("id", 2) + ("name", "world"); + + cout << insert.str() << endl; + + return 0; +} ``` diff --git a/adapter.hpp b/adapter.hpp new file mode 100644 index 0000000..a9bc19e --- /dev/null +++ b/adapter.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace boosql { + +class adapter : public std::enable_shared_from_this +{ +public: + virtual std::string quote_value(const std::string & value) = 0; + virtual std::string quote_field(const std::string & field) = 0; + virtual std::string placeholder() = 0; + virtual adapter * clone() = 0; +}; + +class sqlite_adapter : public adapter +{ +public: + std::string quote_value(const std::string & value) override + { + return "'" + value + "'"; + } + + std::string quote_field(const std::string & field) override + { + return "\"" + field + "\""; + } + + std::string placeholder() override + { + return "?"; + } + + adapter * clone() override + { + return new sqlite_adapter(); + } +}; + +} \ No newline at end of file diff --git a/col.hpp b/col.hpp new file mode 100644 index 0000000..5fb235a --- /dev/null +++ b/col.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include +#include +#include "adapter.hpp" + +namespace boosql +{ + +class col +{ +public: + enum witch {field, value, other}; + struct item { + public: + witch type; + std::string val; + }; + + col() {} + + col(const std::string & c) + { + name(c); + } + + col(const std::string & c, const std::string & tn) + { + name(c); + table_name(tn); + } + + col(const col & c) + { + _items = c._items; + _table_name = c._table_name; + } + + virtual ~col() {} + + col & name(const std::string & c) + { + if (c != "") { + _items.push_back(item{ field, c }); + } + return *this; + } + + col & table_name(const std::string & tn) + { + _table_name = tn; + return *this; + } + + col& as(const std::string& s) + { + _items.push_back(item{other, "AS " + s}); + return *this; + } + + col& is_null() { + _items.push_back(item{other, "IS NULL"}); + return *this; + } + + col& is_not_null() { + _items.push_back(item{other, "IS NOT NULL"}); + return *this; + } + + template + col& in(const std::vector& args) { + return in_or_not(args, "IN"); + } + + template + col& not_in(const std::vector& args) { + return in_or_not(args, "NOT IN"); + } + + template + col & in_or_not(const std::vector& args, std::string in) + { + size_t size = args.size(); + if(size == 1) { + _items.push_back(item{other, "="}); + std::ostringstream str; + str << args[0]; + _items.push_back(item{value, str.str()}); + } else { + _items.push_back(item{other, in + " ("}); + for(size_t i = 0; i < size; ++i) { + std::ostringstream str; + str << args[i]; + _items.push_back(item{value, str.str()}); + if(i < size - 1) { + _items.push_back(item{other, ","}); + } + } + _items.push_back(item{other, ")"}); + } + return *this; + } + + col & o_and() + { + _items.push_back(item{other, "AND"}); + return *this; + } + + col & o_or() + { + _items.push_back(item{other, "OR"}); + return *this; + } + + col & quote_begin() + { + _items.push_back(item{other, "("}); + return *this; + } + + col & quote_end() + { + _items.push_back(item{other, ")"}); + return *this; + } + + col& operator ()(const std::string & any) + { + _items.push_back(item{other, any}); + return *this; + } + + template + col & val(const T& data) + { + std::ostringstream str; + str << data; + _items.push_back(item{value, str.str()}); + + return *this; + } + + template + col & val(const std::vector &data) + { + for (auto i = data.begin(); i != data.end(); ++i) { + std::ostringstream str; + str << *i; + _items.push_back(item{value, str.str()}); + } + + return *this; + } + + col& operator &&(col & condition) + { + o_and(); + merge(condition); + + return *this; + } + + col& operator ||(col& condition) + { + o_or(); + merge(condition); + + return *this; + } + + void merge(col & condition) + { + for (auto i = condition._items.begin(); i != condition._items.end(); ++i) { + _items.push_back(*i); + } + } + + bool empty() + { + return _items.empty(); + } + + const item & last() + { + return _items.back(); + } + + col& operator &&(const std::string& condition) + { + quote_begin(); + _items.push_back(item{other, condition}); + quote_end(); + + return *this; + } + + col& operator ||(const std::string& condition) { + quote_begin(); + _items.push_back(item{other, condition}); + quote_end(); + + return *this; + } + + col& operator &&(const char* condition) { + return operator &&(std::string(condition)); + } + + col& operator ||(const char* condition) { + return operator ||(std::string(condition)); + } + + col& operator [] (const char * data) { + return with_operator(data, "LIKE"); + } + + col& operator [] (const std::string & data) { + return with_operator(data, "LIKE"); + } + + col & escape(const std::string & data) { + _items.push_back(item{other, "{ ESCAPE "}); + _items.push_back(item{value, data}); + _items.push_back(item{other, "}"}); + + return *this; + } + + template + col& operator ==(const T& data) { + return with_operator(data, "="); + } + + template + col& operator !=(const T& data) { + return with_operator(data, "!="); + } + + template + col& operator >=(const T& data) { + return with_operator(data, ">="); + } + + template + col& operator <=(const T& data) { + return with_operator(data, "<="); + } + + template + col& operator >(const T& data) { + return with_operator(data, ">"); + } + + template + col& operator <(const T& data) { + return with_operator(data, "<"); + } + + template + col & with_operator(const T & data, const std::string & oper) + { + std::ostringstream str; + str << data; + _items.push_back(item{other, oper}); + _items.push_back(item{value, str.str()}); + + return *this; + } + + std::string str(adapter * adapter) const + { + return str(adapter, ""); + } + + std::string str(adapter * adapter, std::vector & params) const + { + return str(adapter, "", params); + } + + std::string str(adapter * adapter, std::string table_name) const { + std::string ret = ""; + std::string pre_col = ""; + if (table_name != "") { + pre_col = adapter->quote_field(table_name) + "."; + } else if (_table_name != "") { + pre_col = adapter->quote_field(_table_name) + "."; + } + for(auto i = _items.begin(); i != _items.end(); ++i) { + auto it = *i; + switch(it.type) { + case field: + ret += pre_col + adapter->quote_field(it.val); + break; + case value: + ret += adapter->quote_value(it.val); + break; + case other: + ret += " " + it.val + " "; + } + } + + return ret; + } + + std::string str(adapter * adapter, std::string table_name, std::vector & params) const + { + std::string ret = ""; + std::string pre_col = ""; + if (table_name != "") { + pre_col = adapter->quote_field(table_name) + "."; + } else if (_table_name != "") { + pre_col = adapter->quote_field(_table_name) + "."; + } + for(auto i = _items.begin(); i != _items.end(); ++i) { + auto it = *i; + switch(it.type) { + case field: + ret += pre_col + adapter->quote_field(it.val); + break; + case value: + ret += adapter->placeholder(); + params.push_back(it.val); + break; + case other: + ret += " " + it.val + " "; + } + } + + return ret; + } + + static col pre_quote(const std::string & c) + { + return col().quote_begin().name(c); + } + + operator bool() { + return true; + } +private: + std::vector _items; + std::string _table_name = ""; +}; + +} diff --git a/delete_model.hpp b/delete_model.hpp new file mode 100644 index 0000000..5a6999d --- /dev/null +++ b/delete_model.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include "model.hpp" + +#include + +namespace boosql { + +class delete_model : public model +{ +public: + delete_model() {} + delete_model(adapter * a) : model(a) {} + delete_model(const delete_model & m) : model(m) + { + _table_name = m._table_name; + } + + delete_model(adapter * a, const std::string & table_name) + : model(a, table_name) + {} + + delete_model(const std::string & table_name) + : model(table_name) + {} + + virtual ~delete_model() {} + + delete_model& from(const std::string& table_name) { + _table_name = table_name; + return *this; + } + + delete_model& and_where(const std::string & condition) + { + model::and_where(condition); + return *this; + } + + delete_model& and_where(const col & condition) + { + model::and_where(condition); + return *this; + } + + delete_model& or_where(const std::string & condition) + { + model::or_where(condition); + return *this; + } + + delete_model& or_where(const col & condition) + { + model::or_where(condition); + return *this; + } + + delete_model& quote(std::function callback) + { + model::quote(callback, *this); + return *this; + } + + delete_model& where(const std::string& condition) { + model::where(condition); + return *this; + } + + delete_model& where(const col& condition) { + model::where(condition); + return *this; + } + + const std::string & table_name() + { + return _table_name; + } + + const std::string& str() override { + _sql.clear(); + _sql.append("DELETE FROM "); + _sql.append(_adapter->quote_field(_table_name)); + append_where(); + + return _sql; + } + + const std::string& str(std::vector & params) override { + _sql.clear(); + _sql.append("DELETE FROM "); + _sql.append(_adapter->quote_field(_table_name)); + append_where(params); + + return _sql; + } + + delete_model& reset() { + model::reset(); + return *this; + } + + friend inline std::ostream& operator<< (std::ostream& out, delete_model& mod) { + out << mod.str(); + return out; + } +}; + +} \ No newline at end of file diff --git a/insert_model.hpp b/insert_model.hpp new file mode 100644 index 0000000..00865bd --- /dev/null +++ b/insert_model.hpp @@ -0,0 +1,241 @@ +#pragma once + +#include "model.hpp" + +#include +#include + +namespace boosql +{ + +class insert_model : public model +{ + class row_interface { + public: + virtual void each(std::function) = 0; + virtual const std::string & fields(adapter *) = 0; + virtual const std::string & values(adapter *) = 0; + virtual const std::string & values(adapter *, std::vector &) = 0; + }; + + class row : public row_interface { + public: + row(insert_model & model): _model(model) {} + + template + row& insert(const std::string& c, const T& data) { + std::ostringstream str; + str << data; + _data[c] = str.str(); + return *this; + } + + template + row& operator()(const std::string& c, const T& data) { + return insert(c, data); + } + + void each(std::function handle) + { + for (auto i = _data.begin(); i != _data.end(); ++i) { + handle(i->first, i->second); + } + } + + insert_model & next_row() { + return _model; + } + + const std::string & fields(adapter * adapter) override + { + if (_fields != "") { + return _fields; + } + _fields = "("; + auto size = _data.size(); + unsigned count = 0; + for (auto i = _data.begin(); i != _data.end(); ++i) { + count++; + _fields.append(col(i->first).str(adapter)); + if (count < size) { + _fields.append(", "); + } + } + + _fields.append(")"); + + return _fields; + } + + const std::string & values(adapter * adapter) override + { + if (_values != "") { + return _values; + } + _values = "("; + auto size = _data.size(); + unsigned count = 0; + for (auto i = _data.begin(); i != _data.end(); ++i) { + count++; + _values.append(adapter->quote_value(i->second)); + if (count < size) { + _values.append(", "); + } + } + + _values.append(")"); + + return _values; + } + + const std::string & values(adapter * adapter, std::vector & params) override + { + if (_values != "") { + return _values; + } + _values = "("; + auto size = _data.size(); + unsigned count = 0; + for (auto i = _data.begin(); i != _data.end(); ++i) { + count++; + _values.append(adapter->placeholder()); + params.push_back(i->second); + if (count < size) { + _values.append(", "); + } + } + + _values.append(")"); + + return _values; + } + + private: + insert_model & _model; + std::map _data; + std::string _fields; + std::string _values; + }; + +public: + insert_model() {} + insert_model(adapter * a) : model(a) {} + insert_model(const insert_model & m) : model(m) + { + _replace = m._replace; + _table_name = m._table_name; + for (auto i = m._rows.begin(); i != m._rows.end(); ++i) { + row * r = new row(*this); + (*i)->each([&r](std::string field, std::string val) { + r->insert(field, val); + }); + _rows.push_back(r); + } + } + insert_model(adapter * a, const std::string & table_name) + : model(a, table_name) + {} + + insert_model(const std::string & table_name) + : model(table_name) + {} + + virtual ~insert_model() + { + auto i = _rows.begin(); + while (i != _rows.end()) { + delete *i; + _rows.erase(i); + i = _rows.begin(); + } + } + + row & next_row() { + auto r = new row(*this); + _rows.push_back(r); + return *r; + } + + template + row& operator()(const std::string& c, const T& data) { + return next_row()(c, data); + } + + insert_model& into(const std::string& table_name) { + _table_name = table_name; + return *this; + } + + insert_model& replace(bool var) { + _replace = var; + return *this; + } + + const std::string& str() override { + _sql.clear(); + if (_replace) { + _sql.append("INSERT INTO OR REPLACE INFO "); + }else { + _sql.append("INSERT INTO "); + } + _sql.append(_adapter->quote_field(_table_name)); + auto size = _rows.size(); + unsigned count = 0; + for (auto i = _rows.begin(); i != _rows.end(); ++i) { + count++; + if (count == 1) { + _sql.append((*i)->fields(_adapter)); + _sql.append(" VALUES"); + } + _sql.append((*i)->values(_adapter)); + if (count < size) { + _sql.append(", "); + } + } + + return _sql; + } + + const std::string &str(std::vector ¶ms) override + { + _sql.clear(); + if (_replace) { + _sql.append("INSERT INTO OR REPLACE INFO "); + }else { + _sql.append("INSERT INTO "); + } + _sql.append(_adapter->quote_field(_table_name)); + auto size = _rows.size(); + unsigned count = 0; + for (auto i = _rows.begin(); i != _rows.end(); ++i) { + count++; + if (count == 1) { + _sql.append((*i)->fields(_adapter)); + _sql.append(" VALUES"); + } + _sql.append((*i)->values(_adapter, params)); + if (count < size) { + _sql.append(", "); + } + } + + return _sql; + } + + insert_model& reset() { + _table_name.clear(); + _rows.clear(); + return *this; + } + + friend inline std::ostream& operator << (std::ostream& out, insert_model& mod) { + out << mod.str(); + return out; + } + +protected: + bool _replace = false; + std::vector _rows; +}; + +} \ No newline at end of file diff --git a/makefile b/makefile deleted file mode 100644 index eeca4a6..0000000 --- a/makefile +++ /dev/null @@ -1,6 +0,0 @@ -all: - cd test && mkdir -p build && cd build && cmake .. && make && ctest -test: all - cd test/build && ./sql-test -clean: - rm -rf test/build diff --git a/model.hpp b/model.hpp new file mode 100644 index 0000000..325d8a3 --- /dev/null +++ b/model.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include "adapter.hpp" +#include "col.hpp" + +#include +#include +#include + + +namespace boosql +{ + +class model +{ +public: + const std::string & table_name() + { + return _table_name; + } + + model() + { + _adapter = new sqlite_adapter(); + _auto_delete_adapter = true; + } + + model(const model & m) + { + _adapter = m._adapter->clone(); + _auto_delete_adapter = true; + + _table_name = m._table_name; + _where_condition = m._where_condition; + } + + model(adapter * adapter) : _adapter(adapter) {} + + model(adapter * adapter, const std::string & table_name) + : _adapter(adapter), _table_name(table_name) + { + } + + model(const std::string & table_name) + { + _adapter = new sqlite_adapter(); + _auto_delete_adapter = true; + _table_name = table_name; + } + + virtual std::string where_str() + { + std::string ret; + for(auto i = _where_condition.begin(); i != _where_condition.end(); ++i) { + ret.append((*i).str(_adapter, table_name())); + } + + return ret; + } + + virtual std::string where_str(std::vector & params) + { + std::string ret; + for(auto i = _where_condition.begin(); i != _where_condition.end(); ++i) { + ret.append((*i).str(_adapter, table_name(), params)); + } + + return ret; + } + + virtual ~model() + { + if (_auto_delete_adapter) { + delete _adapter; + } + } + + virtual const std::string& str() = 0; + virtual const std::string& str(std::vector &) = 0; + +protected: + + void append_where() + { + std::string w = where_str(); + if (w.length() > 0) { + _sql.append( " WHERE " ); + _sql.append(w); + } + } + + void append_where(std::vector & params) + { + std::string w = where_str(params); + if (w.length() > 0) { + _sql.append( " WHERE " ); + _sql.append(w); + } + } + + void and_where(const std::string & condition) + { + _where_condition.push_back(col().o_and()); + _where_condition.push_back(col()(condition)); + } + void and_where(const col & condition) + { + _where_condition.push_back(col().o_and()); + _where_condition.push_back(condition); + } + void or_where(const std::string & condition) + { + _where_condition.push_back(col().o_or()); + _where_condition.push_back(col()(condition)); + } + void or_where(const col & condition) + { + _where_condition.push_back(col().o_or()); + _where_condition.push_back(condition); + } + template + void quote(std::function callback, T& model) + { + if (_where_condition.size() > 0) { + _where_condition.push_back(col().o_and()); + } + _where_condition.push_back(col().quote_begin()); + callback(model); + _where_condition.push_back(col().quote_end()); + } + void where(const std::string& condition) { + if (_where_condition.size() > 0) { + _where_condition.push_back(col().o_and()); + } + _where_condition.push_back(col()(condition)); + } + void where(const col& condition) { + int s = _where_condition.size(); + if (s > 0) { + col last = _where_condition.back(); + if (!last.empty() && last.last().val != "(") { + _where_condition.push_back(col().o_and()); + } + } + _where_condition.push_back(condition); + } + + void reset() + { + _table_name.clear(); + _where_condition.clear(); + } + +protected: + std::string _sql; + adapter * _adapter; + bool _auto_delete_adapter = false; + std::string _table_name; + +private: + std::vector _where_condition; +}; + +} diff --git a/select_model.hpp b/select_model.hpp new file mode 100644 index 0000000..0df9a07 --- /dev/null +++ b/select_model.hpp @@ -0,0 +1,463 @@ +#pragma once + +#include +#include +#include + +#include "col.hpp" +#include "model.hpp" + +namespace boosql { + + +class select_model : public model +{ + typedef enum {left, right, inner} join_type; + + class join_t { + public: + join_t(select_model & selector, select_model & model) : _selector(selector), model(model) {} + + join_t & on(std::string main) + { + return on(main, _selector.table_ref()); + } + + join_t & on(std::string main, std::string table_name) + { + if (ons.size() > 0) { + ons.push_back(col().o_and()); + } + + ons.push_back(col(main, table_name)); + + return *this; + } + + join_t & operator () (std::string oper, col second) + { + ons.push_back(col()(oper)); + ons.push_back(second.table_name(model.table_ref())); + + return *this; + } + + join_t & or_on(std::string main) + { + return or_on(main, _selector.table_ref()); + } + + join_t & or_on(std::string main, std::string table_name) + { + ons.push_back(col().o_or()); + ons.push_back(col(main, table_name)); + + return *this; + } + + select_model & end() + { + return _selector; + } + + public: + join_type type; + select_model & model; + std::vector ons; + private: + select_model & _selector; + }; +public: + select_model() {} + select_model(adapter * adapter): model(adapter) {} + select_model(const select_model & m) : model(m) + { + for (auto i = m._joins.begin(); i != m._joins.end(); ++i) { + join_t join(*this, (*i).model); + join.ons = (*i).ons; + _joins.push_back(join); + } + _select = m._select; + _groupby_columns = m._groupby_columns; + _having_condition = m._having_condition; + _order_by = m._order_by; + _limit = m._limit; + _offset = m._offset; + } + select_model(adapter * adapter, const std::string & table_name) + : model(adapter, table_name) + {} + + select_model(const std::string & table_name) : model(table_name) + {} + + virtual ~select_model() {} + + void copy_select(const select_model & m) + { + } + + template + select_model& select(const col& c, Args&&... columns) { + _select.push_back(c); + select(columns...); + return *this; + } + + // for recursion + select_model& select() { + return *this; + } + + select_model& from(const std::string& table_name) + { + _table_name = table_name; + return *this; + } + + select_model & from(select_model & m) + { + _from_model = &m; + _is_from_model = true; + return *this; + } + + select_model& and_where(const std::string & condition) + { + model::and_where(condition); + return *this; + } + + select_model& and_where(const col & condition) + { + model::and_where(condition); + return *this; + } + + select_model& or_where(const std::string & condition) + { + model::or_where(condition); + return *this; + } + + select_model& or_where(const col & condition) + { + model::or_where(condition); + return *this; + } + + select_model& quote(std::function callback) + { + model::quote(callback, *this); + return *this; + } + + select_model& where(const std::string& condition) { + model::where(condition); + return *this; + } + + select_model& where(const col& condition) { + model::where(condition); + return *this; + } + + join_t & left_join(select_model & m) { + return join(m, left); + } + + join_t & right_join(select_model &m) { + return join(m, right); + } + + join_t & inner_join(select_model &m) { + return join(m, inner); + } + + template + select_model& group_by(const col& c, Args&&...columns) { + _groupby_columns.push_back(c); + group_by(columns...); + return *this; + } + + // for recursion + select_model& group_by() { + return *this; + } + + select_model& having(const std::string& condition) { + _having_condition.push_back(col("")(condition)); + return *this; + } + + select_model& having(const col& condition) { + _having_condition.push_back(condition); + return *this; + } + + select_model& order_by(const col& order_by) { + _order_by.push_back(order_by); + return *this; + } + + template + select_model& limit(const T& limit) { + _limit = std::to_string(limit); + + return *this; + } + + select_model& page(const int& page, const int& page_size) { + offset((page - 1) * page_size); + limit(page_size); + return *this; + } + + template + select_model& offset(const T& offset) { + _offset = std::to_string(offset); + return *this; + } + + std::string table_str() + { + if (_is_from_model) { + return "(" + _from_model->str() + ") __m__"; + } + return _table_name; + } + + std::string table_ref() + { + if (_is_from_model) { + return "__m__"; + } + + return _table_name; + } + + std::string table_str(std::vector & p) + { + if (_is_from_model) { + return "(" + _from_model->str(p) + ") __m__"; + } + return _table_name; + } + + const std::string& str() override + { + _sql.clear(); + _sql.append("SELECT "); + _sql.append(select_str()); + _sql.append(" FROM "); + _sql.append(table_str()); + _sql.append(join_str()); + append_where(); + _sql.append(group_by_str()); + _sql.append(having_str()); + _sql.append(order_by_str()); + if(!_limit.empty()) { + _sql.append(" LIMIT "); + _sql.append(_limit); + } + if(!_offset.empty()) { + _sql.append(" OFFSET "); + _sql.append(_offset); + } + return _sql; + } + + const std::string & str(std::vector ¶ms) override + { + _sql.clear(); + _sql.append("SELECT "); + _sql.append(select_str()); + _sql.append(" FROM "); + _sql.append(table_str(params)); + _sql.append(join_str()); + append_where(params); + _sql.append(group_by_str()); + _sql.append(having_str()); + _sql.append(order_by_str()); + if(!_limit.empty()) { + _sql.append(" LIMIT "); + _sql.append(_limit); + } + if(!_offset.empty()) { + _sql.append(" OFFSET "); + _sql.append(_offset); + } + return _sql; + } + + std::string order_by_str() + { + std::string ret; + auto size = _order_by.size(); + if (size > 0) { + ret.append(" ORDER BY "); + for (size_t i = 0; i < size; ++i) { + ret.append(_order_by[i].str(_adapter, table_ref())); + if(i < size - 1) { + ret.append(", "); + } + } + } + + return ret; + } + + std::string join_str() + { + std::string ret; + for (auto i = _joins.begin(); i != _joins.end(); ++i) { + switch ((*i).type) { + case left: + ret.append(" LEFT"); + break; + case right: + ret.append(" RIGHT"); + break; + case inner: + ret.append(" INNER"); + break; + } + ret.append(" JOIN " + _adapter->quote_field((*i).model.table_name())); + ret.append(" ON "); + auto ons = (*i).ons; + for (auto j = ons.begin(); j != ons.end(); ++j) { + ret.append((*j).str(_adapter)); + } + } + + return ret; + } + + std::string where_str() + { + std::string ret = model::where_str(); + for (auto i = _joins.begin(); i != _joins.end(); ++i) { + auto s = (*i).model.where_str(); + if (ret.length() > 0 && s.length() > 0) { + ret.append(" AND (" + s + ")"); + } else if (s.length() > 0) { + ret.append("(" + s + ")"); + } + } + + return ret; + } + + std::string where_str(std::vector & params) + { + std::string ret = model::where_str(params); + for (auto i = _joins.begin(); i != _joins.end(); ++i) { + if (ret.length() > 0) { + ret.append(" AND "); + } + auto s = (*i).model.where_str(params); + if (s.length() > 0) { + ret.append("(" + s + ")"); + } + } + + return ret; + } + + std::string having_str() + { + std::string ret; + auto size = _having_condition.size(); + if(size > 0) { + ret.append(" HAVING "); + for(size_t i = 0; i < size; ++i) { + ret.append(_having_condition[i].str(_adapter, table_ref())); + if(i < size - 1) { + _sql.append(" "); + } + } + } + + return ret; + } + + std::string group_by_str() + { + std::string ret; + auto size = _groupby_columns.size(); + if(size > 0) { + ret.append(" GROUP BY "); + for(size_t i = 0; i < size; ++i) { + ret.append(_groupby_columns[i].str(_adapter, table_ref())); + if(i < size - 1) { + ret.append(", "); + } + } + } + + return ret; + } + + + std::string select_str() + { + std::string ret = ""; + unsigned count = 0; + for (auto i = _select.begin(); i != _select.end(); ++i) { + count++; + ret.append((*i).str(_adapter, table_ref())); + if (count < _select.size()) { + ret.append(", "); + } + } + for (auto i = _joins.begin(); i != _joins.end(); ++i) { + if (ret.length() > 0) { + ret.append(", "); + } + ret.append((*i).model.select_str()); + } + + return ret; + } + + select_model& reset() { + model::reset(); + _select.clear(); + _groupby_columns.clear(); + _having_condition.clear(); + _order_by.clear(); + _limit.clear(); + _offset.clear(); + return *this; + } + + friend inline std::ostream& operator<< (std::ostream& out, select_model& mod) { + out << mod.str(); + return out; + } + + private: + + join_t & join(select_model & m, join_type type) { + join_t j(*this, m); + j.type = type; + _joins.push_back(j); + + return _joins.back(); + } + +private: + std::vector _joins; + std::vector _select; + std::vector _groupby_columns; + std::vector _having_condition; + std::vector _order_by; + std::string _limit; + std::string _offset; + bool _is_from_model = false; + + select_model * _from_model; +}; + +} diff --git a/sql.h b/sql.h deleted file mode 100644 index 5a6e7a5..0000000 --- a/sql.h +++ /dev/null @@ -1,662 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace sql { - -template -inline std::string to_value(const T& data) { - return std::to_string(data); -} - -template -inline std::string to_value(char const(&data)[N]) { - std::string str("'"); - str.append(data); - str.append("'"); - return str; -} - -template <> -inline std::string to_value(const std::string& data) { - std::string str("'"); - str.append(data); - str.append("'"); - return str; -} - -template <> -inline std::string to_value(const char* const& data) { - std::string str("'"); - str.append(data); - str.append("'"); - return str; -} - -/* -template <> -static std::string sql::to_value(const time_t& data) { - char buff[128] = {0}; - struct tm* ttime = localtime(&data); - strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", ttime); - std::string str("'"); - str.append(buff); - str.append("'"); - return str; -} -*/ - -class column -{ -public: - column(const std::string& column) { - _cond = column; - } - virtual ~column() {} - - column& as(const std::string& s) { - _cond.append(" as "); - _cond.append(s); - return *this; - } - - column& is_null() { - _cond.append(" is null"); - return *this; - } - - column& is_not_null() { - _cond.append(" is not null"); - return *this; - } - - template - column& in(const std::vector& args) { - size_t size = args.size(); - if(size == 1) { - _cond.append(" = "); - _cond.append(to_value(args[0])); - } else { - _cond.append(" in ("); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _cond.append(to_value(args[i])); - _cond.append(", "); - } else { - _cond.append(to_value(args[i])); - } - } - _cond.append(")"); - } - return *this; - } - - template - column& not_in(const std::vector& args) { - size_t size = args.size(); - if(size == 1) { - _cond.append(" != "); - _cond.append(to_value(args[0])); - } else { - _cond.append(" not in ("); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _cond.append(to_value(args[i])); - _cond.append(", "); - } else { - _cond.append(to_value(args[i])); - } - } - _cond.append(")"); - } - return *this; - } - - column& operator &&(column& condition) { - std::string str("("); - str.append(_cond); - str.append(") and ("); - str.append(condition._cond); - str.append(")"); - condition._cond = str; - return condition; - } - - column& operator ||(column& condition) { - std::string str("("); - str.append(_cond); - str.append(") or ("); - str.append(condition._cond); - str.append(")"); - condition._cond = str; - return condition; - } - - column& operator &&(const std::string& condition) { - _cond.append(" and "); - _cond.append(condition); - return *this; - } - - column& operator ||(const std::string& condition) { - _cond.append(" or "); - _cond.append(condition); - return *this; - } - - column& operator &&(const char* condition) { - _cond.append(" and "); - _cond.append(condition); - return *this; - } - - column& operator ||(const char* condition) { - _cond.append(" or "); - _cond.append(condition); - return *this; - } - - template - column& operator ==(const T& data) { - _cond.append(" = "); - _cond.append(to_value(data)); - return *this; - } - - template - column& operator !=(const T& data) { - _cond.append(" != "); - _cond.append(to_value(data)); - return *this; - } - - template - column& operator >=(const T& data) { - _cond.append(" >= "); - _cond.append(to_value(data)); - return *this; - } - - template - column& operator <=(const T& data) { - _cond.append(" <= "); - _cond.append(to_value(data)); - return *this; - } - - template - column& operator >(const T& data) { - _cond.append(" > "); - _cond.append(to_value(data)); - return *this; - } - - template - column& operator <(const T& data) { - _cond.append(" < "); - _cond.append(to_value(data)); - return *this; - } - - const std::string& str() const { - return _cond; - } - - operator bool() { - return true; - } -private: - std::string _cond; -}; - - -class SqlModel -{ -public: - SqlModel() {} - virtual ~SqlModel() {} - - virtual const std::string& str() = 0; - const std::string& last_sql() { - return _sql; - } -private: - SqlModel(const SqlModel& m) = delete; - SqlModel& operator =(const SqlModel& data) = delete; -protected: - std::string _sql; -}; - -class SelectModel : public SqlModel -{ -public: - SelectModel() {} - virtual ~SelectModel() {} - - template - SelectModel& select(const std::string& str, Args&&... columns) { - _select_columns.push_back(str); - select(columns...); - return *this; - } - - // for recursion - SelectModel& select() { - return *this; - } - - template - SelectModel& from(const std::string& table_name, Args&&... tables) { - if(_table_name.empty()) { - _table_name = table_name; - } else { - _table_name.append(", "); - _table_name.append(table_name); - } - from(tables...); - return *this; - } - - // for recursion - SelectModel& from() { - return *this; - } - - SelectModel& where(const std::string& condition) { - _where_condition.push_back(condition); - return *this; - } - - SelectModel& where(column& condition) { - _where_condition.push_back(condition.str()); - return *this; - } - - template - SelectModel& group_by(const std::string& str, Args&&...columns) { - _groupby_columns.push_back(str); - group_by(columns...); - return *this; - } - - // for recursion - SelectModel& group_by() { - return *this; - } - - SelectModel& having(const std::string& condition) { - _having_condition.push_back(condition); - return *this; - } - - SelectModel& having(column& condition) { - _having_condition.push_back(condition.str()); - return *this; - } - - SelectModel& order_by(const std::string& order_by) { - _order_by = order_by; - return *this; - } - - template - SelectModel& limit(const T& limit) { - _limit = std::to_string(limit); - return *this; - } - template - SelectModel& limit(const T& offset, const T& limit) { - _offset = std::to_string(offset); - _limit = std::to_string(limit); - return *this; - } - template - SelectModel& offset(const T& offset) { - _offset = std::to_string(offset); - return *this; - } - - virtual const std::string& str() override { - _sql.clear(); - _sql.append("select "); - size_t size = _select_columns.size(); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_select_columns[i]); - _sql.append(", "); - } else { - _sql.append(_select_columns[i]); - } - } - _sql.append(" from "); - _sql.append(_table_name); - size = _where_condition.size(); - if(size > 0) { - _sql.append(" where "); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_where_condition[i]); - _sql.append(" "); - } else { - _sql.append(_where_condition[i]); - } - } - } - size = _groupby_columns.size(); - if(!_groupby_columns.empty()) { - _sql.append(" group by "); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_groupby_columns[i]); - _sql.append(", "); - } else { - _sql.append(_groupby_columns[i]); - } - } - } - size = _having_condition.size(); - if(size > 0) { - _sql.append(" having "); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_having_condition[i]); - _sql.append(" "); - } else { - _sql.append(_having_condition[i]); - } - } - } - if(!_order_by.empty()) { - _sql.append(" order by "); - _sql.append(_order_by); - } - if(!_limit.empty()) { - _sql.append(" limit "); - _sql.append(_limit); - } - if(!_offset.empty()) { - _sql.append(" offset "); - _sql.append(_offset); - } - return _sql; - } - - SelectModel& reset() { - _table_name.clear(); - _select_columns.clear(); - _groupby_columns.clear(); - _where_condition.clear(); - _having_condition.clear(); - _order_by.clear(); - _limit.clear(); - _offset.clear(); - return *this; - } - friend inline std::ostream& operator<< (std::ostream& out, SelectModel& mod) { - out< _select_columns; - std::vector _groupby_columns; - std::string _table_name; - std::vector _where_condition; - std::vector _having_condition; - std::string _order_by; - std::string _limit; - std::string _offset; -}; - - - -class InsertModel : public SqlModel -{ -public: - InsertModel() {} - virtual ~InsertModel() {} - - template - InsertModel& insert(const std::string& c, const T& data) { - _columns.push_back(c); - _values.push_back(to_value(data)); - return *this; - } - - template - InsertModel& operator()(const std::string& c, const T& data) { - return insert(c, data); - } - - InsertModel& into(const std::string& table_name) { - _table_name = table_name; - return *this; - } - - InsertModel& replace(bool var) { - _replace = var; - return *this; - } - - virtual const std::string& str() override { - _sql.clear(); - std::string v_ss; - - if (_replace) { - _sql.append("insert or replace into "); - }else { - _sql.append("insert into "); - } - - _sql.append(_table_name); - _sql.append("("); - v_ss.append(" values("); - size_t size = _columns.size(); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_columns[i]); - _sql.append(", "); - v_ss.append(_values[i]); - v_ss.append(", "); - } else { - _sql.append(_columns[i]); - _sql.append(")"); - v_ss.append(_values[i]); - v_ss.append(")"); - } - } - _sql.append(v_ss); - return _sql; - } - - InsertModel& reset() { - _table_name.clear(); - _columns.clear(); - _values.clear(); - return *this; - } - - friend inline std::ostream& operator<< (std::ostream& out, InsertModel& mod) { - out< _columns; - std::vector _values; -}; - -template <> -inline InsertModel& InsertModel::insert(const std::string& c, const std::nullptr_t&) { - _columns.push_back(c); - _values.push_back("null"); - return *this; -} - - -class UpdateModel : public SqlModel -{ -public: - UpdateModel() {} - virtual ~UpdateModel() {} - - UpdateModel& update(const std::string& table_name) { - _table_name = table_name; - return *this; - } - - template - UpdateModel& set(const std::string& c, const T& data) { - std::string str(c); - str.append(" = "); - str.append(to_value(data)); - _set_columns.push_back(str); - return *this; - } - - template - UpdateModel& operator()(const std::string& c, const T& data) { - return set(c, data); - } - - UpdateModel& where(const std::string& condition) { - _where_condition.push_back(condition); - return *this; - } - - UpdateModel& where(column& condition) { - _where_condition.push_back(condition.str()); - return *this; - } - - virtual const std::string& str() override { - _sql.clear(); - _sql.append("update "); - _sql.append(_table_name); - _sql.append(" set "); - size_t size = _set_columns.size(); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_set_columns[i]); - _sql.append(", "); - } else { - _sql.append(_set_columns[i]); - } - } - size = _where_condition.size(); - if(size > 0) { - _sql.append(" where "); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_where_condition[i]); - _sql.append(" "); - } else { - _sql.append(_where_condition[i]); - } - } - } - return _sql; - } - - UpdateModel& reset() { - _table_name.clear(); - _set_columns.clear(); - _where_condition.clear(); - return *this; - } - friend inline std::ostream& operator<< (std::ostream& out, UpdateModel& mod) { - out< _set_columns; - std::string _table_name; - std::vector _where_condition; -}; - -template <> -inline UpdateModel& UpdateModel::set(const std::string& c, const std::nullptr_t&) { - std::string str(c); - str.append(" = null"); - _set_columns.push_back(str); - return *this; -} - - -class DeleteModel : public SqlModel -{ -public: - DeleteModel() {} - virtual ~DeleteModel() {} - - DeleteModel& _delete() { - return *this; - } - - template - DeleteModel& from(const std::string& table_name, Args&&... tables) { - if(_table_name.empty()) { - _table_name = table_name; - } else { - _table_name.append(", "); - _table_name.append(table_name); - } - from(tables...); - return *this; - } - - // for recursion - DeleteModel& from() { - return *this; - } - - DeleteModel& where(const std::string& condition) { - _where_condition.push_back(condition); - return *this; - } - - DeleteModel& where(column& condition) { - _where_condition.push_back(condition.str()); - return *this; - } - - virtual const std::string& str() override { - _sql.clear(); - _sql.append("delete from "); - _sql.append(_table_name); - size_t size = _where_condition.size(); - if(size > 0) { - _sql.append(" where "); - for(size_t i = 0; i < size; ++i) { - if(i < size - 1) { - _sql.append(_where_condition[i]); - _sql.append(" "); - } else { - _sql.append(_where_condition[i]); - } - } - } - return _sql; - } - - DeleteModel& reset() { - _table_name.clear(); - _where_condition.clear(); - return *this; - } - friend inline std::ostream& operator<< (std::ostream& out, DeleteModel& mod) { - out< _where_condition; -}; - -} diff --git a/test/test.cpp b/test/test.cpp index 20d78dd..cf07944 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,7 +1,12 @@ #include #include -#include "sql.h" +#include "col.hpp" +#include "select_model.hpp" +#include "update_model.hpp" +#include "delete_model.hpp" +#include "insert_model.hpp" +#include "adapter.hpp" /* @@ -17,49 +22,100 @@ create table if not exists user ( */ -using namespace sql; +using namespace boosql; -int main() +void test_select_join() { - InsertModel i; - i.insert("score", 100) - ("name", std::string("six")) - ("age", (unsigned char)20) - ("address", "beijing") - ("create_time", nullptr) - .into("user"); - std::cout< 60 and (column("age") >= 20 or column("address").is_not_null())) - .group_by("age") - .having(column("age") > 10) - .order_by("age desc") - .limit(10) - .offset(1); - std::cout< 60) and ((age >= 20) or (address is not null)) group by age having age > 10 order by age desc limit 10 offset 1 - - std::vector a = {1, 2, 3}; - UpdateModel u; - u.update("user") - .set("name", "ddc") - ("age", 18) - ("score", nullptr) - ("address", "beijing") - .where(column("id").in(a)); - std::cout< +#include "col.hpp" +#include + +namespace boosql { + +class update_model : public model +{ +public: + update_model() {} + update_model(adapter * a) : model(a) {} + update_model(const update_model & m) : model(m) + { + _columns = m._columns; + _table_name = m._table_name; + } + update_model(adapter * a, const std::string & table_name) + : model(a, table_name) + {} + update_model(const std::string & table_name) + : model(table_name) + {} + + update_model& update(const std::string& table_name) { + _table_name = table_name; + return *this; + } + + template + update_model& set(const std::string& c, const T& data) { + _columns.push_back(col(c)("=").val(data)); + return *this; + } + + template + update_model& operator()(const std::string& c, const T& data) { + return set(c, data); + } + + update_model& and_where(const std::string & condition) + { + model::and_where(condition); + return *this; + } + + update_model& and_where(const col & condition) + { + model::and_where(condition); + return *this; + } + + update_model& or_where(const std::string & condition) + { + model::or_where(condition); + return *this; + } + + update_model& or_where(const col & condition) + { + model::or_where(condition); + return *this; + } + + update_model& quote(std::function callback) + { + model::quote(callback, *this); + return *this; + } + + update_model& where(const std::string& condition) { + model::where(condition); + return *this; + } + + update_model& where(const col& condition) { + model::where(condition); + return *this; + } + + const std::string& str() override + { + _sql.clear(); + _sql.append("UPDATE "); + _sql.append(_adapter->quote_field(_table_name)); + _sql.append(" SET "); + size_t size = _columns.size(); + for(size_t i = 0; i < size; ++i) { + _sql.append(_columns[i].str(_adapter, table_name())); + if(i < size - 1) { + _sql.append(", "); + } + } + append_where(); + + return _sql; + } + + const std::string& str(std::vector & params) override + { + _sql.clear(); + _sql.append("UPDATE "); + _sql.append(_adapter->quote_field(_table_name)); + _sql.append(" SET "); + size_t size = _columns.size(); + for(size_t i = 0; i < size; ++i) { + _sql.append(_columns[i].str(_adapter, table_name(), params)); + if(i < size - 1) { + _sql.append(", "); + } + } + append_where(); + + return _sql; + } + + update_model& reset() { + model::reset(); + _columns.clear(); + return *this; + } + friend inline std::ostream& operator << (std::ostream& out, update_model& mod) { + out << mod.str(); + return out; + } + +protected: + std::vector _columns; + std::string _table_name; +}; + +} \ No newline at end of file