diff cppdb/cppdb.h @ 0:a4671277546c tip

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cppdb/cppdb.h	Tue Aug 17 11:19:54 2021 +0200
@@ -0,0 +1,688 @@
+#ifndef CPPDB_H
+#define CPPDB_H
+
+#include <string>
+#include <common.h>
+#include <tuple>
+#include <functional>
+#include <type_traits>
+#include <algorithm>
+
+#define _O(x) x
+
+// helper macros
+#define CONCAT_IMPL( x, y ) x##y
+#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
+
+// basic value retrieval for various column types
+struct value_renderer
+{
+    value_renderer() = default;
+	virtual ~value_renderer() = default;
+    virtual std::string render(const std::string& t) const { return t; }
+
+    virtual std::string value() const = 0;
+    virtual std::string crypted(bool) = 0;
+    virtual value_renderer& decrypted(bool) = 0;
+
+};
+
+/**
+* Class representing a column
+*/
+class Column
+{
+public:
+    virtual ~Column() = default;
+    virtual std::string name() const = 0;
+    virtual std::string realname() const = 0;
+    virtual std::string type() const = 0;
+    virtual const value_renderer* get_value_renderer() const  = 0;
+    virtual std::string extra_modifiers() const = 0;
+    virtual std::string crypt_value(const std::string&) const = 0;
+    virtual std::string decrypt_value(const std::string&) const = 0;
+    virtual Column* clone() const = 0;
+};
+
+class BasicColumn : public Column
+{
+public:
+    virtual std::string name() const { return "" ;}
+    virtual std::string type() const  { return "" ;}
+    virtual std::string extra_modifiers() const { return "";}
+};
+
+class NotAColumn : public Column
+{
+public:
+    virtual std::string name() const = 0;
+    virtual std::string realname() const = 0;
+    virtual std::string type() const { return ""; }
+    virtual const value_renderer* get_value_renderer() const { return nullptr; }
+    virtual std::string extra_modifiers() const { return ""; }
+    virtual std::string crypt_value(const std::string&) const { return ""; }
+    virtual std::string decrypt_value(const std::string&) const { return ""; }
+    virtual Column* clone() const { return nullptr; }
+};
+
+class DescAsColumn : public NotAColumn
+{
+public:
+    typedef DescAsColumn type;
+    virtual std::string name() const { return "DESC"; }
+    virtual std::string realname() const { return "DESC"; }
+};
+
+class AscAsColumn : public NotAColumn
+{
+public:
+    typedef AscAsColumn type;
+    virtual std::string name() const { return "ASC"; }
+    virtual std::string realname() const { return "ASC"; }
+};
+
+#define DESC ,DescAsColumn()
+#define ASC  ,AscAsColumn()
+
+class BasicTable
+{
+public:
+    virtual std::string name() const = 0;
+    virtual bool is_crypted() const = 0;
+    virtual ~BasicTable() = default;
+};
+
+struct Functor
+{
+    Functor(std::string& rstr) : s(rstr) {}
+    std::string& s;
+
+    template<typename T>
+    void operator()(T& t)
+    {
+        std::string g = unafrog::utils::to_string(t);
+        this->s = g;
+    }
+};
+
+struct GetNameFunctor
+{
+    GetNameFunctor(std::string& rstr) : s(rstr) {}
+    std::string& s;
+
+    template<typename T>
+    void operator()(T& t)
+    {
+        std::string g = t.name();
+        this->s = g;
+    }
+};
+
+
+static const char* insert_into = "INSERT OR IGNORE INTO ";
+
+// columnset stuff for insert operations
+class Columnset
+{	
+    size_t colcount;
+	std::vector<std::string> colnames;
+    std::vector<const Column*> cols;
+    std::vector<const Column*> mcols;
+
+    std::vector<const value_renderer*> valrends;
+    const BasicTable& t;
+    mutable std::string values_s;
+    mutable std::string s;
+
+public:
+    template<class ...Cs>
+    Columnset(const BasicTable& bt, Cs&... inputs) : colcount(sizeof...(Cs)), cols{&inputs...}, t(bt), values_s(""),
+		s(insert_into + t.name() + "(")
+    {
+		for (auto c : cols)
+		{
+			colnames.push_back(c->name());
+            valrends.push_back(c->get_value_renderer());
+            mcols.push_back(c->clone());
+		}
+	}
+
+    virtual ~Columnset()
+    {
+        for (auto c : mcols)
+        {
+            delete c;
+        }
+        for (auto c : valrends)
+        {
+            delete c;
+        }
+    }
+
+	template <class ...Vals>
+    std::string insert(const char* c, Vals... v)
+    {
+        return std::string(c),  + valset(v...);
+    }
+
+    template <class ...Vals>
+    std::string insert(Vals... v) const
+    {
+		auto r = valset(v...);
+        for(size_t i=0; i<colcount; i++)
+        {
+            std::string cn = colnames.at(i);
+            s += cn.substr(cn.find('.') + 1);
+
+            // the value from the template arguments, stored as a tuple.
+            std::string returned;
+            Functor a(returned);
+            for_index(i, r, a);
+			std::string crypted = mcols.at(i)->crypt_value(returned);
+            values_s += valrends.at(i)->render(crypted);
+            if(i < cols.size() - 1)
+            {
+                s+= ", ";
+                values_s += ", ";
+            }
+        }
+        s += ") VALUES (" + values_s + ")";
+        return s;
+    }
+
+    std::string prepare_insert() const
+    {
+        std::string s = std::string(insert_into) + t.name() + "(";
+        std::string values_s = "";
+
+        for(size_t i=0; i<colcount; i++)
+        {
+            std::string cn = mcols[i]->name();
+            s += cn.substr(cn.find('.') + 1);
+            values_s += ":v" + std::to_string(i + 1);
+
+            if(i < cols.size() - 1)
+            {
+                s+= ", ";
+                values_s += ", ";
+            }
+        }
+        s += _O(") VALUES (") + values_s + ")";
+        return s;
+    }
+
+};
+
+/**
+ * Class representing a Table
+ */
+class Table : public BasicTable
+{
+    mutable std::vector<std::string> foreign_keys;
+    std::vector<std::string> inited_foreign_keys;
+    std::vector<const Column*> columns;
+    std::string resolve_foreign_key(const std::string& s) const;
+
+protected:
+    bool init_foreign_key(const std::string& s);
+
+public:
+    virtual std::string verify() const;
+    virtual std::string create() const;
+
+    virtual std::string name() const = 0;
+    virtual std::string realname() const = 0;
+    virtual bool is_crypted() const = 0;
+
+    void addColumn(const Column* c) {columns.push_back(c);}
+};
+
+class Condition
+{
+public:
+    Condition(const std::string& s);
+    std::string cond() const;
+private:
+    std::string cstr;
+};
+
+std::string operator && (const Condition& c1, const Condition& c2);
+std::string operator || (const Condition& c1, const Condition& c2);
+typedef std::string (*PT)(const Condition& c1, const Condition& c2);
+
+#define AND &&
+#define OR ||
+
+template<typename TF>
+std::string write_debug_output( TF const& f) {
+    std::stringstream ss;
+    ss << f.get_modifier();
+    return ss.str();
+}
+
+struct modifier_retriever {
+    template<typename TF, typename ... TR>
+    std::string write( TF const& f, TR const& ... rest ) {
+        std::string full = write_debug_output( f );
+        full += " ";
+        full += write( rest... );
+        return full;
+    }
+    template<typename TF>
+    std::string write( TF const& f ) {
+        return write_debug_output( f );
+    }
+    std::string write() {
+        return "";
+    }
+};
+
+namespace cppdb
+{
+std::string crypt_db_name(const std::string& in);
+std::string crypt_db_value(const std::string& in, bool do_crypt);
+std::string decrypt_db_value(const std::string& in, bool do_decrypt);
+unsigned long crypt_number(unsigned long in, bool do_crypt);
+unsigned long decrypt_number(unsigned long in, bool do_crypt);
+}
+
+#define TABLE_WRAPPER(Name,Crypt) namespace {                       \
+    class Table##Name : public Table                                \
+    {                                                               \
+        mutable std::string crypted_name = "";                      \
+    public:                                                         \
+        static std::string tablename;                               \
+        Table##Name() {}                                            \
+        bool is_crypted() const {return Crypt;}                     \
+        template<class ...Cs>                                       \
+        const Columnset operator() (Cs... cols) const               \
+        { return Columnset(*this, cols...);}                        \
+        virtual std::string name() const                            \
+        {                                                           \
+            if(Crypt)                                               \
+            {                                                       \
+                if(crypted_name.empty())                            \
+                    crypted_name = cppdb::crypt_db_name(tablename); \
+                return crypted_name;                                \
+            }                                                       \
+            else return tablename;                                  \
+        }                                                           \
+        virtual std::string realname() const {return tablename; }
+
+#define TABLE(Name) TABLE_WRAPPER(Name,0)
+#define CRYP_TABLE(Name) TABLE_WRAPPER(Name,1)
+
+// Will create the actual "Name" object of type Table##Name
+#define ENDTABLE(Name) };                                                                       \
+     std::string Table##Name::tablename = #Name;                                            \
+     const Table##Name& Name = Table##Name();                                                   \
+     bool MACRO_CONCAT(reg_tab_, Name) = cppdb_warehouse::instance().add_table(&Name);          \
+     }
+
+#define DEF_OPERATOR(OP,sqlop,Type)                                                                     \
+    Condition operator OP (const Type::type& t) const                                                   \
+    { return Condition( "(" + name() + #sqlop + unafrog::utils::to_string(t) + ")"); }                                  \
+    Condition operator OP (const Column& t) const                                                       \
+    { return Condition( "(" + name() + #sqlop + t.name() + ")"); }                                      \
+
+
+#define COLUMN_WRAPPER(Name, Type, Crypt, ...)                                                          \
+    class Column##Name : public BasicColumn                                                             \
+    {                                                                                                   \
+        Table& parent;                                                                                  \
+        mutable std::string crypted_name = "";                                                          \
+        std::string extra = modifier_retriever().write(__VA_ARGS__);                                    \
+    public:                                                                                             \
+        Column##Name (Table& p) : parent(p) {p.addColumn(this);}                                        \
+            virtual std::string name() const                                                            \
+            {                                                                                           \
+                if (Crypt || parent.is_crypted())                                                       \
+                {                                                                                       \
+                    if(crypted_name.empty())                                                            \
+                    {                                                                                   \
+                        crypted_name = parent.name() + "." + cppdb::crypt_db_name(#Name);           \
+                    }                                                                                   \
+                    return crypted_name;                                                                \
+                }                                                                                       \
+                else return parent.name() + "." + #Name;                                            \
+            }                                                                                           \
+            virtual std::string realname() const { return #Name; }                                  \
+            DEF_OPERATOR(==, =, Type)                                                                   \
+            Condition operator == (const std::string& t) const                                          \
+            { return Condition( "(" + name() + " = '" + t + "')"); }                                    \
+            DEF_OPERATOR(!=, <>, Type)                                                                  \
+            DEF_OPERATOR(<=, <=, Type)                                                                  \
+            DEF_OPERATOR(>=, >=, Type)                                                                  \
+            DEF_OPERATOR(<, <, Type)                                                                    \
+            DEF_OPERATOR(>, >, Type)                                                                    \
+            std::string type() const { return Type().get_type(); }                                      \
+            std::string extra_modifiers() const { return extra; }                                       \
+            const value_renderer* get_value_renderer() const { return new Type(); }                     \
+            std::string crypt_value(const std::string& value) const                                     \
+            {                                                                                           \
+                return Type(value).crypted(Crypt || parent.is_crypted());                               \
+            }                                                                                           \
+            std::string crypt_value(const Type::type& value) const                                      \
+            {                                                                                           \
+                return Type(value).crypted(Crypt || parent.is_crypted());                               \
+            }                                                                                           \
+            std::string decrypt_value(const std::string& value) const                                   \
+            {                                                                                           \
+                return Type(value).decrypted(Crypt || parent.is_crypted()).value();                     \
+            }                                                                                           \
+            Column* clone() const { Column* c = new Column##Name(parent); dynamic_cast<Column##Name*>(c)->crypted_name = crypted_name; dynamic_cast<Column##Name*>(c)->extra = extra; return c;}\
+    };                                                                                                  \
+    Column##Name Name = Column##Name(*this);                                                            \
+    bool MACRO_CONCAT(reg_col_, Name) = cppdb_warehouse::instance().add_column(&Name)
+
+
+#define COLUMN(Name, Type, ...) COLUMN_WRAPPER(Name, Type, 0, __VA_ARGS__)
+#define CRYP_COLUMN(Name, Type, ...) COLUMN_WRAPPER(Name, Type, 1,  __VA_ARGS__)
+
+// Foreign Key stuff
+#define FOREIGN_KEY(X) bool MACRO_CONCAT(g, __COUNTER__ ) = init_foreign_key(std::string(#X))
+
+// Order by
+
+template<typename T, typename U>
+struct is_same : std::false_type { };
+
+template<typename T>
+struct is_same<T, T> : std::true_type { };
+
+template<typename T, typename U>
+constexpr bool eqTypes() { return is_same<T, U>::value; }
+
+std::string orderby_helper(const Column& c);
+
+template <class... args>
+std::string orderby_helper(args... a)
+{
+
+    std::size_t cnt = sizeof...(args);
+    auto r = std::tuple<args...>(a...);
+    std::vector<std::string> columns;
+
+    for(std::size_t i=0; i<cnt; i++)
+    {
+        std::string returned;
+        GetNameFunctor a(returned);
+        for_index(i, r, a);
+        columns.push_back(returned);
+    }
+    std::string result = "";
+    for(size_t i = 0; i<columns.size(); i++)
+    {
+        result += columns.at(i);
+        if(i < columns.size() - 1)
+        {
+            if(columns.at(i + 1) != "ASC" && columns.at(i + 1) != "DESC")
+            {
+                result += ", ";
+            }
+            else
+            {
+                result += " ";
+            }
+        }
+    }
+    return result;
+}
+
+template <class... all>
+std::string ORDER_BY(all... a)
+{
+    std::string s = " ORDER BY ";
+    std::string g = orderby_helper(a...);
+    return s + g;
+}
+
+// Where
+
+std::string where_helper(const Condition& c);
+
+template <class C = Condition, class... args>
+std::string where_helper(const Condition& c, args... a)
+{
+    std::string s = c.cond();
+    s += from(a...);
+    return s;
+}
+
+template <class... all>
+std::string WHERE(all... a)
+{
+    std::string s = "WHERE ";
+    std::string g = where_helper(a...);
+    return s + g;
+}
+
+// From
+
+std::string from_helper(const Table& t);
+
+template <class T = Table, class... args>
+std::string from_helper(const Table& t, args... a)
+{
+    std::string s = t.name() + ", ";
+    s += from_helper(a...);
+    return s;
+}
+
+template <class... all>
+std::string FROM(all... a)
+{
+    std::string s = "FROM ";
+    std::string g = from_helper(a...);
+    return s + g + " ";
+}
+
+// Select
+
+std::string select_helper(const Column& c);
+
+template <class C = Column, class... args>
+std::string select_helper(const Column& c, args... a)
+{
+    std::string s = c.name() + ", ";
+    s += select_helper(a...);
+    return s;
+}
+
+
+template <class... all>
+std::string SELECT(all... a)
+{
+    std::string s = "SELECT ";
+    std::string g = select_helper(a...);
+    return s + g + " ";
+}
+
+// Delete
+
+#define DELETE "DELETE "
+
+// Update
+
+std::string UPDATE(const Table& tab);
+
+std::string set_helper(int total_size, const Column& c);
+
+template <class C = Column, class... args>
+std::string set_helper(std::size_t total_size, const Column& c, args... a)
+{
+    std::string s = c.realname() + "=:v";
+    std::stringstream ss;
+    ss << total_size - sizeof...(a);
+    s += ss.str() + ", ";
+    s += set_helper(total_size, a...);
+    return s;
+}
+
+template <class... all>
+std::string SET(all... a)
+{
+    std::string s = " SET ";
+    std::string g = set_helper(sizeof...(a), a...);
+    return s + g ;
+}
+
+// varchar support
+template<size_t SIZE> class varchar : public value_renderer
+{
+public:
+    // this has to be "const char*" otherwise overloading upon const std::string& is not possible in Column classes
+    typedef const char* type;
+
+    varchar() : s(SIZE) {}
+    varchar(const std::string& v) : s(SIZE), mvalue(v) {}
+    operator std::string () const
+    {
+        return _O("VARCHAR(") + std::to_string(s) + ")";
+    }
+
+    std::string get_type() const
+    {
+        return operator std::string();
+    }
+
+    virtual std::string render(const std::string& t) const {return "\"" + t + "\"" ; }
+
+    virtual std::string crypted(bool crypt) {return cppdb::crypt_db_value(mvalue, crypt); }
+    virtual value_renderer& decrypted(bool crypt) { mvalue = cppdb::decrypt_db_value(mvalue, crypt); return *this;}
+    std::string value() const {return mvalue; }
+private:
+    size_t s;
+    std::string mvalue = "";
+};
+
+template <size_t SIZE>
+std::basic_ostream<char>& operator << (std::basic_ostream<char>& os, varchar<SIZE> vc)
+{
+    os << vc.operator std::string();
+    return os;
+}
+
+#define VARCHAR(S) varchar<S>
+
+// text support
+class text: public value_renderer
+{
+public:
+    // this has to be "const char*" otherwise overloading upon const std::string& is not possible in Column classes
+    typedef const char* type;
+
+    text() = default;
+    text(const std::string& v) : mvalue(v) {}
+    operator std::string () const
+    {
+        return _O("TEXT");
+    }
+
+    std::string get_type() const
+    {
+        return operator std::string();
+    }
+
+    virtual std::string render(const std::string& t) const {return "\"" + t + "\"" ; }
+
+    virtual std::string crypted(bool crypt) {return cppdb::crypt_db_value(mvalue, crypt); }
+    virtual value_renderer& decrypted(bool crypt) { mvalue = cppdb::decrypt_db_value(mvalue, crypt); return *this;}
+    std::string value() const {return mvalue; }
+private:
+    std::string mvalue = "";
+};
+std::basic_ostream<char>& operator << (std::basic_ostream<char>& os, const text& tx);
+
+#define TEXT text
+
+// other types support
+template <class T> class simple_type : public value_renderer
+{
+public:
+    typedef T type;
+    simple_type() = default;
+    simple_type(T t) : mt(t) {}
+private:
+    T mt;
+};
+
+template <> class simple_type<time_t> : public value_renderer
+{
+public:
+    typedef int type;
+    simple_type() = default;
+    simple_type(time_t t) : mt(t) {}
+    simple_type(const std::string& sv) : mt(stol(sv)) {}
+    std::string get_type() const {return _O("TIMESTAMP"); }
+    virtual std::string crypted(bool crypt) {return std::to_string(cppdb::crypt_number(mt, crypt));}
+    virtual value_renderer& decrypted(bool crypt) { mt = cppdb::decrypt_number(mt, crypt); return *this; }
+    std::string value() const {return std::to_string(mt); }
+private:
+    time_t mt = 0;
+};
+
+template <> class simple_type<int> : public value_renderer
+{
+public:
+    typedef int type;
+    simple_type() = default;
+    simple_type(int t) : mt(t) {}
+    simple_type(const std::string& sv) : mt(stoi(sv)) {}
+    std::string get_type() const {return _O("INTEGER"); }
+    virtual std::string crypted(bool crypt) {return std::to_string(cppdb::crypt_number(mt, crypt));}
+    virtual value_renderer& decrypted(bool crypt) { mt = static_cast<int>(cppdb::decrypt_number(mt, crypt)); return *this; }
+    std::string value() const {return std::to_string(mt); }
+private:
+    int mt = 0;
+};
+
+#define INTEGER simple_type<int>
+#define TIMESTAMP simple_type<time_t>
+
+struct primary_key { const std::string modifier() {return _O("PRIMARY KEY"); } };
+struct not_null { const std::string modifier() {return _O("NOT NULL"); } };
+
+template<class D>
+struct defaults
+{
+    defaults(const D& d) : md(d) {}
+    std::string get_modifier() const
+    {
+        std::string res = _O("DEFAULT ");
+        std::stringstream ss; ss << md ; return res + ss.str();
+    }
+private:
+    D md;
+};
+
+template <class T> class modifier_templ
+{
+public:
+    modifier_templ(){}
+    std::string get_modifier() const {return T().modifier();}
+};
+
+static const modifier_templ<primary_key> pk;
+static const modifier_templ<not_null> nn;
+
+#define PRIMARY_KEY pk
+#define NOT_NULL nn
+#define CURRENT_TIMESTAMP _O("CURRENT_TIMESTAMP")
+#define DEFAULT(X) defaults<decltype(X)>(X)
+
+/* The actual warehouse keeping all the tables, columns and other elements */
+class cppdb_warehouse
+{
+public:
+    static cppdb_warehouse& instance();
+    bool add_column(const Column* c);
+    bool add_table(const Table* t);
+    const Table* table(const std::string& tabname);
+    const Column* column(const std::string& colname);
+private:
+    std::vector<const Column*> columns;
+    std::vector<const Table*> tables;
+};
+
+#endif // CPPDB