view 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 source
#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