Initial implementation
parent
880f447196
commit
dd11464124
|
@ -0,0 +1,5 @@
|
|||
*.swo
|
||||
*.swp
|
||||
*.db-journal
|
||||
*.gdb_history
|
||||
*.peda-session*
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "googletest"]
|
||||
path = googletest
|
||||
url = https://github.com/google/googletest.git
|
110
Makefile
110
Makefile
|
@ -1,5 +1,7 @@
|
|||
INC = -Isource/include -I/usr/include/zdb
|
||||
LIBS = -lcairo -lqrencode -lzdb -lpthread
|
||||
INC = -Ibuild -Isource/include -I/usr/include/zdb -Igoogletest/googletest/include
|
||||
LIB = -Lbuild -Lgoogletest/googletest
|
||||
UNITTESTLIBS = -lzdb -lpthread -lgtest_main -lgtest
|
||||
SOLIBS = -lzdb -lpthread
|
||||
|
||||
CXXSOURCE = $(wildcard source/*.cc)
|
||||
CSOURCE = $(wildcard source/*.c)
|
||||
|
@ -10,46 +12,104 @@ COBJ = $(CSOURCE:source/%.c=build/%.o)
|
|||
CXXOBJ = $(CXXSOURCE:source/%.cc=build/%.o)
|
||||
OBJ = $(COBJ) $(CXXOBJ)
|
||||
|
||||
CDEPEND = $(CSOURCE:source/%.c=build/%.c.d)
|
||||
CXXDEPEND = $(CXXSOURCE:source/%.cc=build/%.cc.d)
|
||||
DEPEND = $(CDEPEND) $(CXXDEPEND)
|
||||
.PRECIOUS: $(DEPEND) $(OBJ)
|
||||
.SECONDARY: $(DEPEND) $(OBJ)
|
||||
UNITTEST_CXXSOURCE = $(wildcard unittest/*.cc)
|
||||
UNITTEST_CXXOBJ = $(UNITTEST_CXXSOURCE:unittest/%.cc=build/unittest/%.o)
|
||||
UNITTEST_TARGETS = $(UNITTEST_CXXOBJ:%.o=%)
|
||||
UNITTEST_LOGS = $(UNITTEST_TARGETS:%=%.xml)
|
||||
PRECOMPILED_HEADERS = $(HEADERS:source/include/%.hh=build/%.hh.gch)
|
||||
TESTDBS = $(UNITTEST_TARGETS:build/unittest/%=build/unittest/%.db)
|
||||
|
||||
TARGETS = build/libinvdb.so
|
||||
LIBTARGETS = build/libinvdb.so
|
||||
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
LD = $(CXX)
|
||||
CXXFLAGS = -std=c++14 -g -fPIC
|
||||
SOFLAGS = -shared
|
||||
CXXDEPEND = $(CXXSOURCE:source/%.cc=build/%.cc.d) \
|
||||
$(UNITTEST_CXXSOURCE:unittest/%.cc=build/unittest/%.cc.d)
|
||||
HEADERDEPEND = $(HEADERS:source/include/%.hh=build/%.hh.d)
|
||||
DEPEND = $(CXXDEPEND) $(HEADERDEPEND)
|
||||
|
||||
.PHONY: all depend clean mrproper
|
||||
TARGETS = $(LIBTARGETS) $(UNITTEST_TARGETS)
|
||||
|
||||
all: depend $(TARGETS)
|
||||
UNITTEST_LD_LIBRARY_PATH = build
|
||||
|
||||
.PRECIOUS: $(DEPEND) $(OBJ) $(UNITTEST_CXXOBJ) $(UNITTEST_LOGS) \
|
||||
$(PRECOMPILED_HEADERS)
|
||||
.SECONDARY: $(DEPEND) $(OBJ) $(UNITTEST_CXXOBJ) $(UNITTEST_LOGS) \
|
||||
$(PRECOMPILED_HEADERS)
|
||||
|
||||
CC := gcc
|
||||
CXX := g++
|
||||
LD := g++
|
||||
CXXFLAGS := -std=c++14 -g -fPIC -Wno-virtual-move-assign -Wno-format-security
|
||||
SOFLAGS := -shared
|
||||
|
||||
.PHONY: all depend clean mrproper googletest submodules test \
|
||||
clean_testresults directories
|
||||
|
||||
all: directories depend $(TARGETS) test
|
||||
|
||||
depend: $(DEPEND)
|
||||
|
||||
clean:
|
||||
clean: clean_testresults
|
||||
rm -f build/*.o
|
||||
rm -f build/unittest/*.o
|
||||
rm -f $(TARGETS)
|
||||
rm -f $(UNITTEST_LOGS)
|
||||
rm -f build/unittest/*.db
|
||||
rm -f $(PRECOMPILED_HEADERS)
|
||||
|
||||
clean_testresults:
|
||||
rm -f $(UNITTEST_TARGETS)
|
||||
|
||||
mrproper: clean
|
||||
rm -f build/*.d
|
||||
rm -f build/unittest/*.d
|
||||
cd googletest && make clean
|
||||
cd googletest/googletest && make clean
|
||||
|
||||
googletest:
|
||||
git submodule init
|
||||
git submodule update googletest
|
||||
cd googletest && cmake . && make
|
||||
cd googletest/googletest && cmake . && make
|
||||
|
||||
submodules: googletest
|
||||
|
||||
directories:
|
||||
mkdir -p build/unittest
|
||||
mkdir -p googletest
|
||||
|
||||
test: $(UNITTEST_LOGS)
|
||||
|
||||
build/unittest/%.db: data/clean.db
|
||||
cp $< $@
|
||||
|
||||
build/unittest/%.xml: build/unittest/% build/unittest/%.db
|
||||
LD_LIBRARY_PATH=$(UNITTEST_LD_LIBRARY_PATH) $< --gtest_output=\"xml:$@\"\
|
||||
\sqlite://$(CURDIR)/$<.db 2>&1
|
||||
|
||||
build/%.cc.d: source/%.cc
|
||||
$(CXX) -M $< -o $@ $(INC) $(CXXFLAGS)
|
||||
$(CXX) -MM $< -o $@ $(INC) $(CXXFLAGS)
|
||||
|
||||
build/%.c.d: source/%.c
|
||||
$(CXX) -M $< -o $@ $(INC) $(CXXFLAGS)
|
||||
|
||||
build/%.o: source/%.cc build/%.cc.d
|
||||
build/%.o: source/%.cc build/%.cc.d $(PRECOMPILED_HEADERS)
|
||||
$(CXX) -c $< -o $@ $(INC) $(CXXFLAGS)
|
||||
|
||||
build/%.o: source/%.c build/%.c.d
|
||||
$(CC) -c $< -o $@ $(INC) $(CFLAGS)
|
||||
|
||||
build/%.so: $(OBJ)
|
||||
$(LD) $(SOFLAGS) $(OBJ) -o $@
|
||||
$(LD) $(SOFLAGS) $(OBJ) $(SOLIBS) -o $@
|
||||
|
||||
build/unittest/%.cc.d: unittest/%.cc
|
||||
$(CXX) -MM $< -o $@ $(INC) $(CXXFLAGS)
|
||||
|
||||
build/unittest/%.o: unittest/%.cc build/unittest/*.cc.d $(PRECOMPILED_HEADERS)
|
||||
$(CXX) $(INC) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
build/unittest/%: build/unittest/%.o $(LIBTARGETS)
|
||||
$(LD) $(LIB) $< $(LIBTARGETS:build/lib%.so=-l%) \
|
||||
$(UNITTESTLIBS) -o $@
|
||||
|
||||
build/%.hh.d: source/include/%.hh
|
||||
$(CXX) $(INC) -MM $(CXXFLAGS) $< -o $@
|
||||
|
||||
build/%.hh.gch: source/include/%.hh build/%.hh.d
|
||||
$(CXX) $(INC) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
include $(wildcard build/*.d)
|
||||
include $(wildcard build/unittest/*.d)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Subproject commit 82b11b8cfcca464c2ac74b623d04e74452e74f32
|
|
@ -1,8 +1,10 @@
|
|||
#include "dbobjectptr.hh"
|
||||
#include "dbobjectmap.hh"
|
||||
#include "category.hh"
|
||||
#include "session.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "item.hh"
|
||||
#include "dbsession.hh"
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <zdb.h>
|
||||
// refer to libzdb docs
|
||||
|
@ -12,36 +14,53 @@ namespace inventory {
|
|||
namespace datamodel {
|
||||
|
||||
const char *Category::sc_table = "categories";
|
||||
const char *Category::sc_view = "category_view";
|
||||
const char *Category::sc_membership_table = "category_membership";
|
||||
const char *Category::sc_dbclass = "category";
|
||||
|
||||
Category Category::from_query(ResultSet_T r) {
|
||||
Category ret;
|
||||
const struct DBRecordElementDesc Category::desc_desc = {
|
||||
"description",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
TRY {
|
||||
ret.id = ResultSet_getIntByName(r, "id");
|
||||
ret.parent = ResultSet_getIntByName(r, "parent");
|
||||
const struct DBRecordElementDesc Category::symbol_desc = {
|
||||
"symbol",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
const char *pname = ResultSet_getStringByName(r, "name");
|
||||
if (pname != NULL)
|
||||
ret.name = pname;
|
||||
void Category::create_tables(db::DBSession &session) {
|
||||
const char *create_table =
|
||||
"CREATE TABLE \"categories\" ( \n\
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \n\
|
||||
`sup` INTEGER NOT NULL DEFAULT -1, \n\
|
||||
`name` TEXT, \n\
|
||||
`description` TEXT, \n\
|
||||
`symbol` BLOB, \n\
|
||||
FOREIGN KEY(`sup`) REFERENCES categories ( id ) );";
|
||||
|
||||
const char *pdescription = ResultSet_getStringByName(r, "description");
|
||||
if (pdescription != NULL)
|
||||
ret.description = pdescription;
|
||||
db::DBOperations::execute<void *>(session, NULL, create_table,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
|
||||
int symbol_size;
|
||||
// returns void* by default, and std::copy needs type size info
|
||||
const char *psymbol =
|
||||
(const char *)(ResultSet_getBlobByName(r, "symbol", &symbol_size));
|
||||
if (psymbol != NULL) {
|
||||
ret.symbol.resize(symbol_size);
|
||||
std::copy(psymbol, psymbol + symbol_size, std::back_inserter(ret.symbol));
|
||||
}
|
||||
} CATCH(SQLException) {
|
||||
|
||||
}
|
||||
END_TRY;
|
||||
DBHierarchicalObject<Category>::create_sub_view(session);
|
||||
DBHierarchicalObject<Category>::create_sup_view(session);
|
||||
DBAssocHierarchicalObject<Item, Category>::create_membership_table(session);
|
||||
|
||||
const char *category_view =
|
||||
"CREATE VIEW category_view AS SELECT * FROM category_sub \n\
|
||||
JOIN categories ON categories.id = category_sub.id;";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, category_view,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
void Category::validate_tables(db::DBSession &session) {
|
||||
|
||||
}
|
||||
|
||||
void Category::remove_tables(db::DBSession &session) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include "dbfield.hh"
|
||||
#include "dbobject.hh"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <zdb.h>
|
||||
// refer to libzdb docs
|
||||
#include <Exception.h>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#include "dbobject.hh"
|
||||
#include "dbfield.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dboperations.hh"
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
const struct DBRecordElementDesc
|
||||
DBRecordDescTempl<DBObject>::id_desc = {
|
||||
"id",
|
||||
"ID",
|
||||
"Unique identification number"
|
||||
};
|
||||
|
||||
const struct DBRecordElementDesc
|
||||
DBRecordDescTempl<DBNamedObject>::name_desc = {
|
||||
"name",
|
||||
"Name",
|
||||
""
|
||||
};
|
||||
|
||||
const struct DBRecordElementDesc
|
||||
DBRecordDescTempl<DBHierarchicalObject>::sup_desc = {
|
||||
"sup",
|
||||
"Stored in",
|
||||
""
|
||||
};
|
||||
|
||||
const struct DBRecordElementDesc
|
||||
DBRecordDescTempl<DBHierarchicalObject>::nsub_desc = {
|
||||
"sub",
|
||||
"Children count",
|
||||
""
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "session.hh"
|
||||
#include "dbsession.hh"
|
||||
#include <zdb.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
@ -9,6 +9,16 @@ DBSession::DBSession(const char *url)
|
|||
: m_url(url), m_pool(NULL) {
|
||||
}
|
||||
|
||||
DBSession::~DBSession() {
|
||||
destroy_connpool();
|
||||
}
|
||||
|
||||
void DBSession::reset(const char *url) {
|
||||
destroy_connpool();
|
||||
m_url = url;
|
||||
create_connpool();
|
||||
}
|
||||
|
||||
void DBSession::create_connpool() {
|
||||
if (m_pool == NULL) {
|
||||
URL_T url = URL_new(m_url.c_str());
|
||||
|
@ -17,6 +27,14 @@ void DBSession::create_connpool() {
|
|||
}
|
||||
}
|
||||
|
||||
void DBSession::destroy_connpool() {
|
||||
if (m_pool == NULL)
|
||||
return;
|
||||
|
||||
ConnectionPool_stop(m_pool);
|
||||
ConnectionPool_free(&m_pool);
|
||||
}
|
||||
|
||||
Connection_T DBSession::get_connection() {
|
||||
create_connpool();
|
||||
Connection_T conn = ConnectionPool_getConnection(m_pool);
|
||||
|
@ -25,5 +43,9 @@ Connection_T DBSession::get_connection() {
|
|||
return conn;
|
||||
}
|
||||
|
||||
void DBSession::return_connection(Connection_T conn) {
|
||||
ConnectionPool_returnConnection(m_pool, conn);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,44 @@
|
|||
#ifndef INVENTORY_CATEGORY_HH_
|
||||
#define INVENTORY_CATEGORY_HH_
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <zdb.h>
|
||||
#include "dbcontainer.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "dbfield.hh"
|
||||
#include "dbassocobject.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
class DBSession;
|
||||
}
|
||||
|
||||
namespace datamodel {
|
||||
class Category {
|
||||
class Item;
|
||||
class Category : public DBAssocHierarchicalObject<Item, Category> {
|
||||
public:
|
||||
int id;
|
||||
int parent;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::vector<char> symbol;
|
||||
static const char *sc_table;
|
||||
static const char *sc_view;
|
||||
static const char *sc_membership_table;
|
||||
static const char *sc_dbclass;
|
||||
|
||||
static const char *sc_table;
|
||||
DBString<Category> description;
|
||||
DBBlob<Category> symbol;
|
||||
|
||||
Category()
|
||||
: description(this, &desc_desc),
|
||||
symbol(this, &symbol_desc) {}
|
||||
|
||||
static Category from_query(ResultSet_T);
|
||||
static void create_tables(db::DBSession &session);
|
||||
static void validate_tables(db::DBSession &session);
|
||||
static void remove_tables(db::DBSession &session);
|
||||
|
||||
private:
|
||||
static const struct DBRecordElementDesc desc_desc;
|
||||
static const struct DBRecordElementDesc symbol_desc;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "category_impl.hh"
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef INVENTORY_CATEGORY_IMPL_HH_
|
||||
#define INVENTORY_CATEGORY_IMPL_HH_
|
||||
#include "dbassocobject_impl.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef INVENTORY_CXX_SEMANTICS_HH_
|
||||
#define INVENTORY_CXX_SEMANTICS_HH_
|
||||
#include <cstddef>
|
||||
|
||||
namespace inventory {
|
||||
namespace semantics {
|
||||
|
||||
class non_constructible {
|
||||
non_constructible() {}
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class singleton {
|
||||
public:
|
||||
static Derived &instance() {
|
||||
if (s_instance == NULL)
|
||||
s_instance = new Derived;
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
singleton() {}
|
||||
|
||||
private:
|
||||
static Derived *s_instance;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
class visitor {
|
||||
public:
|
||||
virtual void apply(Args... args) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "cxx-semantics_impl.hh"
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INVENTORY_CXX_SEMANTICS_IMPL_HH_
|
||||
#define INVENTORY_CXX_SEMANTICS_IMPL_HH_
|
||||
#include "cxx-semantics.hh"
|
||||
#include <cstddef>
|
||||
|
||||
namespace inventory {
|
||||
namespace semantics {
|
||||
|
||||
template<class Derived>
|
||||
Derived *singleton<Derived>::s_instance = NULL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef INVENTORY_DBASSOCHIERARCHY_HH_
|
||||
#define INVENTORY_DBASSOCHIERARCHY_HH_
|
||||
#include "dbcontainer.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbobject.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Object, class Derived>
|
||||
class DBAssocObject : public DBNamedObject<Derived> {
|
||||
public:
|
||||
template<template<class> class Container = datamodel::DBObjectVector>
|
||||
datamodel::DBContainerPtr<Object, Container> get_objects(
|
||||
db::DBSession &session,
|
||||
datamodel::DBObjectInserter<Object, Container> inserter =
|
||||
datamodel::DBObjectInserterImpl<Object, Container>::inserter,
|
||||
datamodel::DBContainerPtr<Object, Container> pcontainer = {},
|
||||
int offset = 0, int limit = -1) const;
|
||||
|
||||
template<template<class> class Container = datamodel::DBObjectVector>
|
||||
static datamodel::DBContainerPtr<Derived, Container> get_assoc(
|
||||
db::DBSession &session, const Object& object,
|
||||
datamodel::DBObjectInserter<Derived, Container> inserter =
|
||||
datamodel::DBObjectInserterImpl<Derived, Container>::inserter,
|
||||
datamodel::DBContainerPtr<Derived, Container> pcontainer = {},
|
||||
int offset = 0, int limit = -1);
|
||||
|
||||
protected:
|
||||
static void create_membership_table(db::DBSession &session);
|
||||
};
|
||||
|
||||
template<class Object, class Derived>
|
||||
class DBAssocHierarchicalObject : public DBAssocObject<Object, Derived>,
|
||||
public DBHierarchicalObject<Derived> {
|
||||
public:
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef INVENTORY_DBASSOCHIERARCHY_IMPL_HH_
|
||||
#define INVENTORY_DBASSOCHIERARCHY_IMPL_HH_
|
||||
#include "category.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "dboperations.hh"
|
||||
#include "dbsession.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Object, class Derived>
|
||||
template<template<class> class Container>
|
||||
DBContainerPtr<Object, Container> DBAssocObject<Object, Derived>::get_objects(
|
||||
db::DBSession &session,
|
||||
DBObjectInserter<Object, Container> inserter,
|
||||
DBContainerPtr<Object, Container> pcontainer, int offset, int limit) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Object::sc_view +
|
||||
std::string(" AS object JOIN ") + Derived::sc_membership_table +
|
||||
std::string(" AS membr") +
|
||||
std::string(" WHERE object.id = membr.object_id AND") +
|
||||
std::string(" membr.assoc_id = ?") +
|
||||
std::string(" ORDER BY object.name ASC LIMIT ? OFFSET ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[this](void *, PreparedStatement_T s, int offset, int limit) {
|
||||
PreparedStatement_setInt(s, 1, (int)(DBObject<Derived>::id));
|
||||
PreparedStatement_setInt(s, 2, limit);
|
||||
PreparedStatement_setInt(s, 3, offset);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_all<Object, void *, Container>(session, NULL,
|
||||
prepared_stmt.c_str(), stmt_cons, inserter, pcontainer, offset, limit);
|
||||
}
|
||||
|
||||
template<class Object, class Derived>
|
||||
template<template<class> class Container>
|
||||
DBContainerPtr<Derived, Container> DBAssocObject<Object, Derived>::get_assoc(
|
||||
db::DBSession &session, const Object& object,
|
||||
DBObjectInserter<Derived, Container> inserter,
|
||||
DBContainerPtr<Derived, Container> pcontainer, int offset, int limit) {
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Derived::sc_view +
|
||||
std::string(" AS assoc JOIN ") + Derived::sc_membership_table +
|
||||
std::string(" AS membr") +
|
||||
std::string(" WHERE assoc.id = membr.object_id AND") +
|
||||
std::string(" membr.assoc_id = ?") +
|
||||
std::string(" ORDER BY assoc.name ASC LIMIT ? OFFSET ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[object](void *, PreparedStatement_T s, int offset, int limit) {
|
||||
PreparedStatement_setInt(s, 1, (int)(object.id));
|
||||
PreparedStatement_setInt(s, 2, limit);
|
||||
PreparedStatement_setInt(s, 3, offset);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_all<Derived, void *, Container>(session, NULL,
|
||||
prepared_stmt.c_str(), stmt_cons, inserter, pcontainer, offset, limit);
|
||||
}
|
||||
|
||||
template<class Object, class Derived>
|
||||
void DBAssocObject<Object, Derived>::create_membership_table(
|
||||
db::DBSession &session) {
|
||||
std::string membership_table =
|
||||
std::string("CREATE TABLE \"") + Derived::sc_dbclass + "_membership\" ( \n\
|
||||
`object_id` INTEGER NOT NULL, \n\
|
||||
`assoc_id` INTEGER NOT NULL, \n\
|
||||
FOREIGN KEY(`object_id`) REFERENCES " + Object::sc_table + " ( id ), \n\
|
||||
FOREIGN KEY(`assoc_id`) REFERENCES " + Derived::sc_table + "( id ) );";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, membership_table.c_str(),
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "dbobject_impl.hh"
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef INVENTORY_DBCONTAINER_HH_
|
||||
#define INVENTORY_DBCONTAINER_HH_
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbobjectmap.hh"
|
||||
#include "dbobjectptr.hh"
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class T, template<class> class Container>
|
||||
using DBObjectInserter =
|
||||
std::function<void(datamodel::DBObjectPtr<T>, Container<T> &)>;
|
||||
|
||||
template<class T, template<class> class Container>
|
||||
class DBObjectInserterImpl {};
|
||||
|
||||
template<class T>
|
||||
class DBObjectInserterImpl<T, DBObjectVector> {
|
||||
public:
|
||||
static void inserter(DBObjectPtr<T> el, DBObjectVector<T> &c) {
|
||||
c.push_back(el);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class DBObjectInserterImpl<T, DBObjectMap> {
|
||||
public:
|
||||
static void inserter(DBObjectPtr<T> el, DBObjectMap<T> &c) {
|
||||
c.insert(std::make_pair((int)(el->id), el));
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, template<class> class Container = DBObjectVector>
|
||||
using DBContainerPtr = std::shared_ptr<Container<T>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INVENTORY_DBCONTAINERPTR_HH_
|
||||
#define INVENTORY_DBCONTAINERPTR_HH_
|
||||
#include "dbobjectvec.hh"
|
||||
#include <memory>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class T, template<class> class Container>
|
||||
using DBContainerPtr = std::shared_ptr<Container<T>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,134 @@
|
|||
#ifndef INVENTORY_DBFIELD_HH
|
||||
#define INVENTORY_DBFIELD_HH
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <zdb.h>
|
||||
#include "cxx-semantics.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Derived>
|
||||
class DBObject;
|
||||
|
||||
enum class DBRecordElementType {
|
||||
UNKNOWN,
|
||||
INTEGER,
|
||||
UNSIGNED_INTEGER,
|
||||
TEXT,
|
||||
BLOB
|
||||
};
|
||||
|
||||
struct DBRecordElementDesc {
|
||||
const char *column_name;
|
||||
const char *name;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
// specialized for each class in DBObject tree
|
||||
template<template<class> class Object>
|
||||
class DBRecordDescTempl : semantics::non_constructible {};
|
||||
|
||||
template<class Object>
|
||||
class DBRecordDesc : semantics::non_constructible {};
|
||||
|
||||
template<class Derived>
|
||||
class DBRecordElement {
|
||||
public:
|
||||
DBRecordElement(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
const struct DBRecordElementDesc *element_description() {
|
||||
return mcp_elem_desc;
|
||||
}
|
||||
|
||||
DBRecordElementType type() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
virtual void from_query(ResultSet_T) {};
|
||||
virtual void insert_value(PreparedStatement_T stmt, int pos) const {};
|
||||
|
||||
protected:
|
||||
const DBObject<Derived> * const m_parent;
|
||||
const struct DBRecordElementDesc * const mcp_elem_desc;
|
||||
|
||||
DBRecordElementType m_type;
|
||||
};
|
||||
|
||||
template<class Data, class Derived>
|
||||
class DBField : public DBRecordElement<Derived> {
|
||||
public:
|
||||
DBField(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
DBField operator=(const DBField<Data, Derived> &assigned) {
|
||||
m_value = assigned.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data operator=(const Data &assigned) {
|
||||
return m_value = assigned;
|
||||
}
|
||||
|
||||
operator Data() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
virtual void from_query(ResultSet_T r) {};
|
||||
virtual void insert_value(PreparedStatement_T stmt, int pos) const {};
|
||||
|
||||
protected:
|
||||
Data m_value;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBInt : public DBField<int, Derived> {
|
||||
public:
|
||||
DBInt(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
using DBField<int, Derived>::operator =;
|
||||
void from_query(ResultSet_T r);
|
||||
void insert_value(PreparedStatement_T stmt, int pos) const;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBUnsignedInt : public DBField<unsigned int, Derived> {
|
||||
public:
|
||||
DBUnsignedInt(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
using DBField<unsigned int, Derived>::operator =;
|
||||
void from_query(ResultSet_T r);
|
||||
void insert_value(PreparedStatement_T stmt, int pos) const;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBString : public DBField<std::string, Derived> {
|
||||
public:
|
||||
DBString(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
using DBField<std::string, Derived>::operator =;
|
||||
void from_query(ResultSet_T r);
|
||||
void insert_value(PreparedStatement_T stmt, int pos) const;
|
||||
|
||||
const char *c_str() const;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBBlob : public DBField<std::vector<char>, Derived> {
|
||||
public:
|
||||
DBBlob(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc);
|
||||
|
||||
using DBField<std::vector<char>, Derived>::operator =;
|
||||
void from_query(ResultSet_T r);
|
||||
void insert_value(PreparedStatement_T stmt, int pos) const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,126 @@
|
|||
#ifndef INVENTORY_DBFIELD_IMPL_HH_
|
||||
#define INVENTORY_DBFIELD_IMPL_HH_
|
||||
#include "dbobject.hh"
|
||||
#include "dbobject_impl.hh"
|
||||
#include "dbfield.hh"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Data, class Derived>
|
||||
DBField<Data, Derived>::DBField(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: DBRecordElement<Derived>(parent, elem_desc) {}
|
||||
|
||||
template<class Derived>
|
||||
DBInt<Derived>::DBInt(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: DBField<int, Derived>(parent, elem_desc) {
|
||||
// neither clang nor gcc would recognize m_value
|
||||
// w/o explicit class path, not sure why
|
||||
DBField<int, Derived>::m_value = -1;
|
||||
DBRecordElement<Derived>::m_type = DBRecordElementType::INTEGER;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBUnsignedInt<Derived>::DBUnsignedInt(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: DBField<unsigned int, Derived>(parent, elem_desc) {
|
||||
DBField<unsigned int, Derived>::m_value = 0;
|
||||
DBRecordElement<Derived>::m_type = DBRecordElementType::UNSIGNED_INTEGER;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBString<Derived>::DBString(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: DBField<std::string, Derived>(parent, elem_desc) {
|
||||
DBRecordElement<Derived>::m_type = DBRecordElementType::TEXT;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBBlob<Derived>::DBBlob(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: DBField<std::vector<char>, Derived>(parent, elem_desc) {
|
||||
DBRecordElement<Derived>::m_type = DBRecordElementType::BLOB;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
const char *DBString<Derived>::c_str() const {
|
||||
return DBField<std::string, Derived>::m_value.c_str();
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBRecordElement<Derived>::DBRecordElement(const DBObject<Derived> *parent,
|
||||
const struct DBRecordElementDesc *elem_desc)
|
||||
: m_parent(parent), mcp_elem_desc(elem_desc) {
|
||||
if (parent != NULL)
|
||||
parent->register_element(this);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBInt<Derived>::from_query(ResultSet_T r) {
|
||||
DBField<int, Derived>::m_value =
|
||||
ResultSet_getIntByName(r,
|
||||
DBRecordElement<Derived>::mcp_elem_desc->column_name);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBInt<Derived>::insert_value(PreparedStatement_T stmt, int pos) const {
|
||||
PreparedStatement_setInt(stmt, pos, DBField<int, Derived>::m_value);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBUnsignedInt<Derived>::from_query(ResultSet_T r) {
|
||||
DBField<unsigned int, Derived>::m_value =
|
||||
ResultSet_getIntByName(r,
|
||||
DBRecordElement<Derived>::mcp_elem_desc->column_name);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBUnsignedInt<Derived>::insert_value(PreparedStatement_T stmt, int pos) const {
|
||||
PreparedStatement_setInt(stmt, pos, DBField<unsigned int, Derived>::m_value);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBString<Derived>::from_query(ResultSet_T r) {
|
||||
const char * pname =
|
||||
ResultSet_getStringByName(r,
|
||||
DBRecordElement<Derived>::mcp_elem_desc->column_name);
|
||||
|
||||
if (pname != NULL)
|
||||
DBField<std::string, Derived>::m_value = pname;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBString<Derived>::insert_value(PreparedStatement_T stmt, int pos) const {
|
||||
PreparedStatement_setString(stmt, pos,
|
||||
DBField<std::string, Derived>::m_value.c_str());
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBBlob<Derived>::from_query(ResultSet_T r) {
|
||||
int symbol_size;
|
||||
// returns void* by default, and std::copy needs type size info
|
||||
const char *psymbol = (const char *)(ResultSet_getBlobByName(r,
|
||||
DBRecordElement<Derived>::mcp_elem_desc->column_name,
|
||||
&symbol_size));
|
||||
if (psymbol != NULL) {
|
||||
DBField<std::vector<char>, Derived>::m_value.resize(symbol_size);
|
||||
std::copy(psymbol, psymbol + symbol_size,
|
||||
std::back_inserter(DBField<std::vector<char>, Derived>::m_value));
|
||||
}
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBBlob<Derived>::insert_value(PreparedStatement_T stmt, int pos) const {
|
||||
PreparedStatement_setBlob(stmt, pos,
|
||||
&DBField<std::vector<char>, Derived>::m_value.front(),
|
||||
DBField<std::vector<char>, Derived>::m_value.size());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,164 @@
|
|||
#ifndef INVENTORY_DBOBJECT_HH_
|
||||
#define INVENTORY_DBOBJECT_HH_
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <zdb.h>
|
||||
#include "dbcontainer.hh"
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbcontainerptr.hh"
|
||||
#include "dbfield.hh"
|
||||
#include "cxx-semantics.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
class DBSession;
|
||||
}
|
||||
|
||||
namespace datamodel {
|
||||
template<class Derived>
|
||||
class DBObject;
|
||||
|
||||
template<>
|
||||
class DBRecordDescTempl<DBObject> : semantics::non_constructible {
|
||||
public:
|
||||
static const struct DBRecordElementDesc id_desc;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBObject {
|
||||
template<class T>
|
||||
friend class DBRecordElement;
|
||||
|
||||
public:
|
||||
typedef std::map<std::string, DBRecordElement<Derived> *> RecordElementMap;
|
||||
|
||||
protected:
|
||||
void register_element(DBRecordElement<Derived> *el) const;
|
||||
|
||||
// must be initialized before table elements
|
||||
mutable RecordElementMap m_record_elements;
|
||||
|
||||
public:
|
||||
enum class Id {
|
||||
ROOT = -1,
|
||||
UNDEFINED = -2
|
||||
};
|
||||
|
||||
DBInt<Derived> id;
|
||||
|
||||
DBObject();
|
||||
virtual ~DBObject() {}
|
||||
|
||||
static datamodel::DBObjectPtr<Derived> get(db::DBSession &session, int id);
|
||||
|
||||
template<template<class> class Container = datamodel::DBObjectVector>
|
||||
static datamodel::DBContainerPtr<Derived, Container> get_all(
|
||||
db::DBSession &session,
|
||||
datamodel::DBObjectInserter<Derived, Container> inserter =
|
||||
datamodel::DBObjectInserterImpl<Derived, Container>::inserter,
|
||||
datamodel::DBContainerPtr<Derived, Container> pcontainer = {},
|
||||
int offset = 0, int limit = -1);
|
||||
|
||||
// Table fields are initialized only in DBObject class,
|
||||
// but there may be some additional information to be
|
||||
// taken care of lower in the hierarchy, like children
|
||||
// count in DBHierarchicalObject.
|
||||
|
||||
// Implementations still must call DBObject function.
|
||||
virtual void from_query(ResultSet_T);
|
||||
|
||||
void insert(db::DBSession &sess) const;
|
||||
void update(db::DBSession &sess) const;
|
||||
void remove(db::DBSession &sess) const;
|
||||
void refresh(db::DBSession &sess);
|
||||
|
||||
DBRecordElement<Derived> *operator[](const char *column_name) {
|
||||
return m_record_elements[column_name];
|
||||
}
|
||||
|
||||
const RecordElementMap &record_element_map() {
|
||||
return m_record_elements;
|
||||
}
|
||||
|
||||
std::string prepared_statement_columns() const;
|
||||
std::string prepared_statement_value_placeholders() const;
|
||||
|
||||
bool is_root() const {
|
||||
return id == (int)(Id::ROOT);
|
||||
}
|
||||
|
||||
private:
|
||||
void insert_values(PreparedStatement_T s) const;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBNamedObject;
|
||||
|
||||
template<>
|
||||
class DBRecordDescTempl<DBNamedObject> : semantics::non_constructible {
|
||||
public:
|
||||
static const struct DBRecordElementDesc name_desc;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBNamedObject : public virtual DBObject<Derived> {
|
||||
public:
|
||||
DBString<Derived> name;
|
||||
|
||||
DBNamedObject();
|
||||
virtual ~DBNamedObject() {}
|
||||
|
||||
static datamodel::DBObjectPtr<Derived> get(db::DBSession &session,
|
||||
const char *name);
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBHierarchicalObject;
|
||||
|
||||
template<>
|
||||
class DBRecordDescTempl<DBHierarchicalObject> : semantics::non_constructible {
|
||||
public:
|
||||
static const struct DBRecordElementDesc sup_desc;
|
||||
static const struct DBRecordElementDesc nsub_desc;
|
||||
};
|
||||
|
||||
template<class Derived>
|
||||
class DBHierarchicalObject : public virtual DBObject<Derived> {
|
||||
public:
|
||||
DBInt<Derived> sup;
|
||||
|
||||
// generated, not part of the table
|
||||
DBInt<Derived> nsub;
|
||||
|
||||
DBHierarchicalObject();
|
||||
virtual ~DBHierarchicalObject() {}
|
||||
|
||||
template<template<class> class Container = datamodel::DBObjectVector>
|
||||
static datamodel::DBContainerPtr<Derived, Container> get_children(
|
||||
db::DBSession &session, int id,
|
||||
datamodel::DBObjectInserter<Derived, Container> inserter =
|
||||
datamodel::DBObjectInserterImpl<Derived, Container>::inserter,
|
||||
datamodel::DBContainerPtr<Derived, Container> pcontainer = {},
|
||||
int offset = 0, int limit = -1);
|
||||
|
||||
template<template<class> class Container = datamodel::DBObjectVector>
|
||||
datamodel::DBContainerPtr<Derived, Container> get_children(
|
||||
db::DBSession &session,
|
||||
datamodel::DBObjectInserter<Derived, Container> inserter =
|
||||
datamodel::DBObjectInserterImpl<Derived, Container>::inserter,
|
||||
datamodel::DBContainerPtr<Derived, Container> pcontainer = {},
|
||||
int offset = 0, int limit = -1);
|
||||
|
||||
datamodel::DBObjectPtr<Derived> get_parent(db::DBSession &session);
|
||||
|
||||
virtual void from_query(ResultSet_T);
|
||||
|
||||
protected:
|
||||
static void create_sub_view(db::DBSession &session);
|
||||
static void create_sup_view(db::DBSession &session);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,280 @@
|
|||
#ifndef INVENTORY_DBOBJECT_IMPL_HH_
|
||||
#define INVENTORY_DBOBJECT_IMPL_HH_
|
||||
#include <string>
|
||||
#include "dbfield.hh"
|
||||
#include "dbfield_impl.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "dboperations.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Derived>
|
||||
DBObject<Derived>::DBObject()
|
||||
: m_record_elements(),
|
||||
// used in SQL statements, left unregistered
|
||||
id(NULL, &DBRecordDescTempl<DBObject>::id_desc) {
|
||||
id = (int)(DBObject<Derived>::Id::UNDEFINED);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBNamedObject<Derived>::DBNamedObject()
|
||||
: name(this, &DBRecordDescTempl<DBNamedObject>::name_desc) {}
|
||||
|
||||
template<class Derived>
|
||||
DBHierarchicalObject<Derived>::DBHierarchicalObject()
|
||||
: sup(this, &DBRecordDescTempl<DBHierarchicalObject>::sup_desc),
|
||||
nsub(NULL, &DBRecordDescTempl<DBHierarchicalObject>::nsub_desc) {
|
||||
sup = (int)(DBHierarchicalObject<Derived>::Id::UNDEFINED);
|
||||
nsub = -1;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::register_element(DBRecordElement<Derived> *el) const {
|
||||
m_record_elements.insert(
|
||||
std::make_pair(
|
||||
std::string(el->element_description()->column_name), el
|
||||
));
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::from_query(ResultSet_T r) {
|
||||
TRY {
|
||||
id = ResultSet_getIntByName(r,
|
||||
DBRecordDescTempl<DBObject>::id_desc.column_name);
|
||||
for (const auto &el : m_record_elements)
|
||||
(el.second)->from_query(r);
|
||||
} CATCH(SQLException) {
|
||||
|
||||
}
|
||||
END_TRY;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBHierarchicalObject<Derived>::from_query(ResultSet_T r) {
|
||||
nsub = ResultSet_getIntByName(r,
|
||||
DBRecordDescTempl<DBHierarchicalObject>::nsub_desc.column_name);
|
||||
DBObject<Derived>::from_query(r);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
std::string DBObject<Derived>::prepared_statement_columns() const {
|
||||
std::string ret;
|
||||
|
||||
for (const auto &el : m_record_elements)
|
||||
ret += el.first + ", ";
|
||||
|
||||
ret.pop_back();
|
||||
ret.pop_back();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
std::string DBObject<Derived>::prepared_statement_value_placeholders() const {
|
||||
std::string ret;
|
||||
|
||||
for (const auto &el : m_record_elements)
|
||||
ret += "?, ";
|
||||
|
||||
ret.pop_back();
|
||||
ret.pop_back();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::insert(db::DBSession &session) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("INSERT INTO ") +
|
||||
Derived::sc_table + std::string("(") +
|
||||
prepared_statement_columns() +
|
||||
std::string(") VALUES(") +
|
||||
prepared_statement_value_placeholders() +
|
||||
std::string(")");
|
||||
|
||||
auto stmt_cons =
|
||||
[this](void *, PreparedStatement_T s) {
|
||||
insert_values(s);
|
||||
};
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, prepared_stmt.c_str(),
|
||||
stmt_cons);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::update(db::DBSession &session) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("UPDATE ") +
|
||||
Derived::sc_table +
|
||||
std::string(" SET (") +
|
||||
prepared_statement_columns() +
|
||||
std::string(") = (") +
|
||||
prepared_statement_value_placeholders() +
|
||||
std::string(") WHERE id = ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[this](void *, PreparedStatement_T s) {
|
||||
insert_values(s);
|
||||
};
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, prepared_stmt.c_str(),
|
||||
stmt_cons);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::remove(db::DBSession &session) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("DELETE FROM ") +
|
||||
Derived::sc_table +
|
||||
std::string("WHERE item_id = ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[this](void *, PreparedStatement_T s) {
|
||||
PreparedStatement_setInt(s, 1, (int)(id));
|
||||
};
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, prepared_stmt.c_str(),
|
||||
stmt_cons);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::refresh(db::DBSession &session) {
|
||||
*this = *DBObject<Derived>::get(session, id);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBObject<Derived>::insert_values(PreparedStatement_T s) const {
|
||||
int pos = 1;
|
||||
for (const auto &el : m_record_elements) {
|
||||
el.second->insert_value(s, pos++);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBObjectPtr<Derived> DBObject<Derived>::get(db::DBSession &session, int id) {
|
||||
if (id < 0)
|
||||
return DBObjectPtr<Derived>(); // TODO
|
||||
|
||||
// I want to use underlying database's prepared
|
||||
// statement system to sanititze inputs, but it's
|
||||
// not possible to specify tables this way.
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Derived::sc_view +
|
||||
std::string(" WHERE id=?");
|
||||
|
||||
auto stmt_cons =
|
||||
[](int id, PreparedStatement_T s) {
|
||||
PreparedStatement_setInt(s, 1, id);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_single<Derived, int>(session, id,
|
||||
prepared_stmt.c_str(), stmt_cons);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBObjectPtr<Derived> DBNamedObject<Derived>::
|
||||
get(db::DBSession &session, const char *name) {
|
||||
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Derived::sc_view +
|
||||
std::string(" WHERE name=?");
|
||||
|
||||
auto stmt_cons =
|
||||
[](const char *name, PreparedStatement_T s) {
|
||||
PreparedStatement_setString(s, 1, name);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_single<Derived, const char *>(session,
|
||||
name, prepared_stmt.c_str(), stmt_cons);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
template<template<class> class Container>
|
||||
DBContainerPtr<Derived, Container> DBObject<Derived>::get_all(
|
||||
db::DBSession &session,
|
||||
DBObjectInserter<Derived, Container> inserter,
|
||||
DBContainerPtr<Derived, Container> pcontainer, int offset, int limit) {
|
||||
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Derived::sc_view +
|
||||
std::string(" ORDER BY name ASC LIMIT ? OFFSET ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[](void *, PreparedStatement_T s, int offset, int limit) {
|
||||
PreparedStatement_setInt(s, 1, limit);
|
||||
PreparedStatement_setInt(s, 2, offset);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_all<Derived, void *, Container>(session, NULL,
|
||||
prepared_stmt.c_str(), stmt_cons, inserter, pcontainer, offset, limit);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
template<template<class> class Container>
|
||||
DBContainerPtr<Derived, Container> DBHierarchicalObject<Derived>::get_children(
|
||||
db::DBSession &session, int id,
|
||||
DBObjectInserter<Derived, Container> inserter,
|
||||
DBContainerPtr<Derived, Container> pcontainer, int offset, int limit) {
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
Derived::sc_view +
|
||||
std::string(" WHERE parent = ?") +
|
||||
std::string(" ORDER BY name ASC LIMIT ? OFFSET ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[](int id, PreparedStatement_T s, int offset, int limit) {
|
||||
PreparedStatement_setInt(s, 1, id);
|
||||
PreparedStatement_setInt(s, 2, limit);
|
||||
PreparedStatement_setInt(s, 3, offset);
|
||||
};
|
||||
|
||||
return db::DBOperations::get_all<Derived, int, Container>(session, id,
|
||||
prepared_stmt.c_str(), stmt_cons, inserter, pcontainer,
|
||||
offset, limit);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
template<template<class> class Container>
|
||||
DBContainerPtr<Derived, Container> DBHierarchicalObject<Derived>::get_children(
|
||||
db::DBSession &session,
|
||||
DBObjectInserter<Derived, Container> inserter,
|
||||
DBContainerPtr<Derived, Container> pcontainer, int offset, int limit) {
|
||||
return get_children<Container>(session, (int)(DBObject<Derived>::id), inserter,
|
||||
pcontainer, offset, limit);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
DBObjectPtr<Derived> DBHierarchicalObject<Derived>::get_parent(
|
||||
db::DBSession &session) {
|
||||
return get(session, sup);
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBHierarchicalObject<Derived>::create_sub_view(db::DBSession &session) {
|
||||
std::string sub_view = std::string("CREATE VIEW ") + Derived::sc_dbclass + "_sub AS \n\
|
||||
SELECT id, 0 AS sub FROM " + Derived::sc_table + " WHERE id NOT IN (SELECT id FROM " + Derived::sc_dbclass + "_sup) \n\
|
||||
UNION \n\
|
||||
SELECT id, sub FROM " + Derived::sc_dbclass + "_sup;";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, sub_view.c_str(),
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
template<class Derived>
|
||||
void DBHierarchicalObject<Derived>::create_sup_view(db::DBSession &session) {
|
||||
std::string sup_view = std::string("CREATE VIEW ") + Derived::sc_dbclass + "_sup \
|
||||
AS SELECT sup AS id, COUNT(*) AS sub FROM " + Derived::sc_table + " GROUP BY sup;";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, sup_view.c_str(),
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INVENTORY_OBJECT_MAP_
|
||||
#define INVENTORY_OBJECT_MAP_
|
||||
#include <map>
|
||||
#include "dbobjectptr.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class T>
|
||||
using DBObjectMap = std::map<int, DBObjectPtr<T>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef INVENTORY_DBOBJECTPTR_HH
|
||||
#include <memory>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
template <class T>
|
||||
using DBObjectPtr = std::shared_ptr<T>;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef INVENTORY_OBJECT_TREE_HH_
|
||||
#define INVENTORY_OBJECT_TREE_HH_
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbobjecttreeptr.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "cxx-semantics.hh"
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
class DBSession;
|
||||
}
|
||||
|
||||
namespace datamodel {
|
||||
|
||||
template <class Object>
|
||||
class DBObjectTree : public DBObjectTreePtrBase<Object> {
|
||||
public:
|
||||
template <class T>
|
||||
using SubtreeContainer = std::vector<DBObjectTreePtr<T>>;
|
||||
|
||||
typedef DBContainerPtr<Object, SubtreeContainer>
|
||||
SubtreeContainerPtr;
|
||||
|
||||
class SubtreeVisitor :
|
||||
public semantics::visitor<DBObjectPtr<Object>, int, int> {};
|
||||
|
||||
// visibility outside of DBObjectTree
|
||||
typedef SubtreeVisitor Visitor;
|
||||
|
||||
DBObjectTree() {
|
||||
m_object.reset(new Object);
|
||||
m_object->id = (int)(Object::Id::ROOT);
|
||||
}
|
||||
|
||||
DBObjectTree(DBObjectPtr<Object> obj)
|
||||
: m_object(obj) {}
|
||||
|
||||
DBObjectTree(DBObjectPtr<Object> obj, DBObjectTreePtr<Object> suptree)
|
||||
: m_object(obj), m_suptree(suptree) {}
|
||||
|
||||
DBObjectPtr<Object> object() {
|
||||
return m_object;
|
||||
}
|
||||
|
||||
SubtreeContainerPtr subtree() {
|
||||
return m_subtree;
|
||||
}
|
||||
|
||||
void fetch_object(db::DBSession &);
|
||||
void fetch_subtree(db::DBSession &);
|
||||
|
||||
void recurse_subtrees(db::DBSession &session, Visitor &visitor,
|
||||
int max_level = -1, int level = 0);
|
||||
|
||||
private:
|
||||
DBObjectPtr<Object> m_object;
|
||||
|
||||
DBObjectTreePtr<Object> m_suptree;
|
||||
SubtreeContainerPtr m_subtree;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef INVENTORY_OBJECT_TREE_IMPL_HH_
|
||||
#define INVENTORY_OBJECT_TREE_IMPL_HH_
|
||||
#include "dbobjecttree.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "dbcontainerptr.hh"
|
||||
#include <vector>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template<class Object>
|
||||
void DBObjectTree<Object>::fetch_object(db::DBSession &session) {
|
||||
m_object->refresh(session);
|
||||
}
|
||||
|
||||
template<class Object>
|
||||
void DBObjectTree<Object>::fetch_subtree(db::DBSession &session) {
|
||||
if (!m_object)
|
||||
fetch_object(session);
|
||||
|
||||
if (!m_subtree)
|
||||
m_subtree.reset(new SubtreeContainer<Object>);
|
||||
|
||||
auto inserter =
|
||||
[this](DBObjectPtr<Object> el, SubtreeContainer<Object> &c) {
|
||||
DBObjectTreePtr<Object> subtree(new DBObjectTree<Object>(el,
|
||||
DBObjectTree<Object>::shared_from_this()));
|
||||
c.push_back(subtree);
|
||||
};
|
||||
|
||||
(*m_object).template get_children<SubtreeContainer>(session,
|
||||
inserter, m_subtree);
|
||||
}
|
||||
|
||||
template<class Object>
|
||||
void DBObjectTree<Object>::recurse_subtrees(db::DBSession &session,
|
||||
DBObjectTree<Object>::Visitor &visitor, int max_level,
|
||||
int level) {
|
||||
if (!m_subtree)
|
||||
fetch_subtree(session);
|
||||
|
||||
visitor.apply(m_object, max_level, level);
|
||||
|
||||
if (max_level != -1 && level >= max_level)
|
||||
return;
|
||||
|
||||
if (m_subtree) {
|
||||
for (DBObjectTreePtr<Object> subtree : *m_subtree) {
|
||||
subtree->recurse_subtrees(session, visitor, max_level, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef INVENTORY_DBOBJECTTREEPTR_HH_
|
||||
#define INVENTORY_DBOBJECTTREEPTR_HH_
|
||||
#include <memory>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template <class T>
|
||||
class DBObjectTree;
|
||||
|
||||
template <class T>
|
||||
using DBObjectTreePtr = std::shared_ptr<DBObjectTree<T>>;
|
||||
|
||||
template <class T>
|
||||
using DBObjectTreePtrBase = std::enable_shared_from_this<DBObjectTree<T>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INVENTORY_DBOBJECTVEC_HH_
|
||||
#define INVENTORY_DBOBJECTVEC_HH_
|
||||
#include "dbobjectptr.hh"
|
||||
#include <vector>
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
||||
template <class T>
|
||||
using DBObjectVector = std::vector<DBObjectPtr<T>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef INVENTORY_DBOPERATIONS_HH
|
||||
#define INVENTORY_DBOPERATIONS_HH
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <zdb.h>
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbcontainer.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
template<class Derived>
|
||||
class DBObject;
|
||||
}
|
||||
|
||||
namespace db {
|
||||
|
||||
class DBSession;
|
||||
|
||||
class DBOperations {
|
||||
DBOperations() {}
|
||||
|
||||
public:
|
||||
template<class K>
|
||||
using StatementConstructorExecute = std::function<void(K, PreparedStatement_T)>;
|
||||
|
||||
template<class K>
|
||||
using StatementConstructorSingle = std::function<void(K, PreparedStatement_T)>;
|
||||
|
||||
// + offset, limit
|
||||
template<class K>
|
||||
using StatementConstructor = std::function<void(K, PreparedStatement_T, int, int)>;
|
||||
|
||||
// only a statement constructor argument class here, operation is the same
|
||||
// for whole database object hierarchy
|
||||
template<class K>
|
||||
static void execute(
|
||||
DBSession &session, const K &stmt_arg, const char *prepared_stmt,
|
||||
StatementConstructorExecute<K> cons);
|
||||
|
||||
template<class T, class K>
|
||||
static datamodel::DBObjectPtr<T> get_single(
|
||||
DBSession &session, const K &stmt_arg, const char *prepared_stmt,
|
||||
StatementConstructorSingle<K> cons);
|
||||
|
||||
template<class T, class K,
|
||||
template<class> class Container = datamodel::DBObjectVector>
|
||||
static datamodel::DBContainerPtr<T, Container> get_all(
|
||||
DBSession& session, const K &stmt_arg,
|
||||
const char *prepared_stmt, StatementConstructor<K> cons,
|
||||
datamodel::DBObjectInserter<T, Container> inserter,
|
||||
datamodel::DBContainerPtr<T, Container> pcontainer,
|
||||
int offset, int limit);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef INVENTORY_DBOPERATIONS_IMPL_HH_
|
||||
#define INVENTORY_DBOPERATIONS_IMPL_HH_
|
||||
#include "dboperations.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "log.hh"
|
||||
|
||||
#include <zdb.h>
|
||||
// refer to libzdb docs
|
||||
#include <Exception.h>
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
|
||||
template<class K>
|
||||
void DBOperations::execute(
|
||||
DBSession &session, const K &stmt_arg, const char *prepared_stmt,
|
||||
StatementConstructorSingle<K> cons) {
|
||||
LIBINVDB_LOG(Loglevel::DEBUG, std::string("[QUERY] ") + prepared_stmt);
|
||||
|
||||
Connection_T conn = session.get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn, prepared_stmt);
|
||||
|
||||
cons(stmt_arg, s);
|
||||
|
||||
TRY {
|
||||
PreparedStatement_execute(s);
|
||||
} CATCH(SQLException) {
|
||||
// TODO exception wrapper
|
||||
}
|
||||
END_TRY;
|
||||
|
||||
session.return_connection(conn);
|
||||
}
|
||||
|
||||
template<class T, class K>
|
||||
datamodel::DBObjectPtr<T> DBOperations::get_single(
|
||||
DBSession &session, const K &stmt_arg, const char *prepared_stmt,
|
||||
StatementConstructorSingle<K> cons) {
|
||||
LIBINVDB_LOG(Loglevel::DEBUG, std::string("[QUERY] ") + prepared_stmt);
|
||||
|
||||
Connection_T conn = session.get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn, prepared_stmt);
|
||||
|
||||
cons(stmt_arg, s);
|
||||
|
||||
ResultSet_T r = PreparedStatement_executeQuery(s);
|
||||
ResultSet_next(r); // with unique key, only one item is expected
|
||||
if (r == NULL)
|
||||
throw std::runtime_error("No such item");
|
||||
|
||||
datamodel::DBObjectPtr<T> ret(new T);
|
||||
ret->from_query(r);
|
||||
session.return_connection(conn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class T, class K, template<class> class Container>
|
||||
datamodel::DBContainerPtr<T, Container> DBOperations::get_all(
|
||||
DBSession &session, const K &stmt_arg,
|
||||
const char *prepared_stmt, StatementConstructor<K> cons,
|
||||
datamodel::DBObjectInserter<T, Container> inserter,
|
||||
datamodel::DBContainerPtr<T, Container> pcontainer,
|
||||
int offset, int limit) {
|
||||
LIBINVDB_LOG(Loglevel::DEBUG, std::string("[QUERY] ") + prepared_stmt);
|
||||
|
||||
if (!pcontainer)
|
||||
pcontainer.reset(new Container<T>);
|
||||
|
||||
Connection_T conn = session.get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn,
|
||||
prepared_stmt);
|
||||
|
||||
cons(stmt_arg, s, offset, limit);
|
||||
|
||||
ResultSet_T r = PreparedStatement_executeQuery(s);
|
||||
while (ResultSet_next(r)) {
|
||||
datamodel::DBObjectPtr<T> el(new T);
|
||||
el->from_query(r);
|
||||
inserter(el, *pcontainer);
|
||||
}
|
||||
|
||||
session.return_connection(conn);
|
||||
return pcontainer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "dbobject_impl.hh"
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef INVENTORY_DBSESSION_HH_
|
||||
#define INVENTORY_DBSESSION_HH_
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <zdb.h>
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "dboperations.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
|
||||
class DBSession {
|
||||
public:
|
||||
DBSession() {}
|
||||
DBSession(const char *url);
|
||||
~DBSession();
|
||||
|
||||
Connection_T get_connection();
|
||||
void return_connection(Connection_T);
|
||||
|
||||
void reset(const char *url);
|
||||
|
||||
private:
|
||||
void create_connpool();
|
||||
void destroy_connpool();
|
||||
|
||||
std::string m_url;
|
||||
ConnectionPool_T m_pool;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "dboperations_impl.hh"
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#define INVENTORY_GROUP_HH_
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "category.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
|
|
|
@ -1,31 +1,67 @@
|
|||
#ifndef INVENTORY_ITEM_HH_
|
||||
#define INVENTORY_ITEM_HH_
|
||||
#include "session.hh"
|
||||
#include <zdb.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "dbobjectptr.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbobject.hh"
|
||||
#include "dbfield.hh"
|
||||
#include "cxx-semantics.hh"
|
||||
|
||||
namespace inventory {
|
||||
namespace datamodel {
|
||||
class Item {
|
||||
|
||||
class Item;
|
||||
|
||||
template<>
|
||||
class DBRecordDesc<Item> : semantics::non_constructible {
|
||||
public:
|
||||
// raw data in table order
|
||||
// no need for deeper abstraction here
|
||||
int id;
|
||||
int parent;
|
||||
std::string name;
|
||||
std::string description;
|
||||
unsigned int time_added;
|
||||
unsigned int time_spoiled;
|
||||
std::string cash_worth;
|
||||
int currency_id;
|
||||
static const struct DBRecordElementDesc desc_desc;
|
||||
static const struct DBRecordElementDesc time_added_desc;
|
||||
static const struct DBRecordElementDesc time_spoiled_desc;
|
||||
static const struct DBRecordElementDesc cash_worth_desc;
|
||||
static const struct DBRecordElementDesc currency_desc;
|
||||
static const struct DBRecordElementDesc long_axis_desc;
|
||||
static const struct DBRecordElementDesc short_axis_desc;
|
||||
static const struct DBRecordElementDesc height_desc;
|
||||
static const struct DBRecordElementDesc mass_desc;
|
||||
static const struct DBRecordElementDesc url_desc;
|
||||
};
|
||||
|
||||
static const char *sc_table;
|
||||
class Item : public DBNamedObject<Item>, public DBHierarchicalObject<Item> {
|
||||
public:
|
||||
static const char *sc_table;
|
||||
static const char *sc_view;
|
||||
static const char *sc_dbclass;
|
||||
|
||||
// these may throw, can't do it in copy constructor
|
||||
static Item from_query(ResultSet_T);
|
||||
DBString<Item> description;
|
||||
DBUnsignedInt<Item> time_added;
|
||||
DBUnsignedInt<Item> time_spoiled;
|
||||
DBInt<Item> cash_worth;
|
||||
DBInt<Item> currency_id;
|
||||
DBInt<Item> long_axis;
|
||||
DBInt<Item> short_axis;
|
||||
DBInt<Item> height;
|
||||
DBInt<Item> mass;
|
||||
DBString<Item> url;
|
||||
|
||||
Item()
|
||||
: description(this, &DBRecordDesc<Item>::desc_desc),
|
||||
time_added(this, &DBRecordDesc<Item>::time_added_desc),
|
||||
time_spoiled(this, &DBRecordDesc<Item>::time_spoiled_desc),
|
||||
cash_worth(this, &DBRecordDesc<Item>::cash_worth_desc),
|
||||
currency_id(this, &DBRecordDesc<Item>::currency_desc),
|
||||
long_axis(this, &DBRecordDesc<Item>::long_axis_desc),
|
||||
short_axis(this, &DBRecordDesc<Item>::short_axis_desc),
|
||||
height(this, &DBRecordDesc<Item>::height_desc),
|
||||
mass(this, &DBRecordDesc<Item>::mass_desc),
|
||||
url(this, &DBRecordDesc<Item>::url_desc) {}
|
||||
|
||||
static void create_tables(db::DBSession &session);
|
||||
static void validate_tables(db::DBSession &session);
|
||||
static void remove_tables(db::DBSession &session);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef INVENTORY_ITEM_IMPL_HH_
|
||||
#define INVENTORY_ITEM_IMPL_HH_
|
||||
#include "item.hh"
|
||||
#include "dbobject_impl.hh"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef INVENTORY_LOG_HH_
|
||||
#define INVENTORY_LOG_HH_
|
||||
#include "logstream.hh"
|
||||
#include "cxx-semantics.hh"
|
||||
#include <memory>
|
||||
|
||||
#define LIBINVDB_LOG(lvl, str) Log::instance()(lvl, "[" + \
|
||||
std::string(__func__) + " @ " + std::string(__FILE__) + ":" + \
|
||||
std::to_string(__LINE__) + "] " + std::string(str))
|
||||
|
||||
namespace inventory {
|
||||
|
||||
class Log : public semantics::singleton<Log> {
|
||||
public:
|
||||
Log()
|
||||
: m_loglevel(Loglevel::WARNING) {}
|
||||
|
||||
void set_stream(std::shared_ptr<Logstream> stream) {
|
||||
mp_logstream = stream;
|
||||
}
|
||||
|
||||
void set_loglevel(Loglevel lvl) {
|
||||
m_loglevel = lvl;
|
||||
}
|
||||
|
||||
void operator()(Loglevel lvl, const std::string &msg) {
|
||||
if ((int)(lvl) <= (int)(m_loglevel))
|
||||
(*mp_logstream)(lvl, msg);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Logstream> mp_logstream;
|
||||
|
||||
Loglevel m_loglevel;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef INVENTORY_LOGSTREAM_HH_
|
||||
#define INVENTORY_LOGSTREAM_HH_
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace inventory {
|
||||
|
||||
enum class Loglevel {
|
||||
CRITICAL,
|
||||
WARNING,
|
||||
NOTICE,
|
||||
INFO,
|
||||
DEBUG,
|
||||
DEBUG2
|
||||
};
|
||||
|
||||
class Logstream {
|
||||
public:
|
||||
virtual void operator()(Loglevel lvl, const std::string &msg) = 0;
|
||||
};
|
||||
|
||||
class StdLogstream : public Logstream {
|
||||
public:
|
||||
virtual void operator()(Loglevel lvl, const std::string &msg);
|
||||
|
||||
private:
|
||||
static const char *sc_loglevel[];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,118 +0,0 @@
|
|||
#ifndef INVENTORY_DBSESSION_HH
|
||||
#define INVENTORY_DBSESSION_HH
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <zdb.h>
|
||||
|
||||
namespace inventory {
|
||||
namespace db {
|
||||
|
||||
class DBSession {
|
||||
public:
|
||||
DBSession(const char *url);
|
||||
|
||||
Connection_T get_connection();
|
||||
|
||||
// database accessors for use w/ datamodel types
|
||||
template<class T>
|
||||
T get(int id);
|
||||
|
||||
template<class T>
|
||||
T get(const char *name);
|
||||
|
||||
template<class T>
|
||||
std::vector<T> get_all(int offset = 0, int limit = -1);
|
||||
|
||||
template<class T>
|
||||
T get_parent(const T &);
|
||||
|
||||
private:
|
||||
void create_connpool();
|
||||
|
||||
const std::string m_url;
|
||||
ConnectionPool_T m_pool;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
T DBSession::get(int id) {
|
||||
// We want to use underlying database's prepared
|
||||
// statement system to sanititze inputs, but it's
|
||||
// not possible to specify tables this way.
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
T::sc_table +
|
||||
std::string(" WHERE id=?");
|
||||
|
||||
Connection_T conn = get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn,
|
||||
prepared_stmt.c_str());
|
||||
|
||||
PreparedStatement_setInt(s, 1, id);
|
||||
|
||||
ResultSet_T r = PreparedStatement_executeQuery(s);
|
||||
ResultSet_next(r); // with unique key, only one item is expected
|
||||
if (r == NULL)
|
||||
throw std::runtime_error("No such item");
|
||||
|
||||
T &&ret = T::from_query(r);
|
||||
Connection_close(conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T DBSession::get(const char *name) {
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
T::sc_table +
|
||||
std::string(" WHERE name=?");
|
||||
|
||||
Connection_T conn = get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn,
|
||||
prepared_stmt.c_str());
|
||||
|
||||
PreparedStatement_setString(s, 1, name);
|
||||
|
||||
ResultSet_T r = PreparedStatement_executeQuery(s);
|
||||
ResultSet_next(r);
|
||||
if (r == NULL)
|
||||
throw std::runtime_error("No such item");
|
||||
|
||||
T &&ret = T::from_query(r);
|
||||
Connection_close(conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::vector<T> DBSession::get_all(int offset, int limit) {
|
||||
const std::string prepared_stmt =
|
||||
std::string("SELECT * FROM ") +
|
||||
T::sc_table +
|
||||
std::string(" ORDER BY name ASC LIMIT ? OFFSET ?");
|
||||
|
||||
Connection_T conn = get_connection();
|
||||
PreparedStatement_T s = Connection_prepareStatement(conn,
|
||||
prepared_stmt.c_str());
|
||||
|
||||
PreparedStatement_setInt(s, 1, limit);
|
||||
PreparedStatement_setInt(s, 2, offset);
|
||||
|
||||
ResultSet_T r = PreparedStatement_executeQuery(s);
|
||||
|
||||
std::vector<T> ret;
|
||||
while (ResultSet_next(r)) {
|
||||
T &&it = T::from_query(r);
|
||||
ret.push_back(it);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T DBSession::get_parent(const T &t) {
|
||||
return (T&&)(get(t.id));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
169
source/item.cc
169
source/item.cc
|
@ -1,5 +1,9 @@
|
|||
#include "dbcontainer.hh"
|
||||
#include "dbobjectptr.hh"
|
||||
#include "item.hh"
|
||||
#include "session.hh"
|
||||
#include "category.hh"
|
||||
#include "dboperations.hh"
|
||||
#include "dbsession.hh"
|
||||
#include <stdexcept>
|
||||
|
||||
#include <zdb.h>
|
||||
|
@ -10,38 +14,155 @@ namespace inventory {
|
|||
namespace datamodel {
|
||||
|
||||
const char *Item::sc_table = "items";
|
||||
const char *Item::sc_view = "item_view";
|
||||
const char *Item::sc_dbclass = "item";
|
||||
|
||||
Item Item::from_query(ResultSet_T r) {
|
||||
Item ret;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::desc_desc = {
|
||||
"description",
|
||||
"Description",
|
||||
""
|
||||
};
|
||||
|
||||
TRY {
|
||||
ret.id = ResultSet_getIntByName(r, "id");
|
||||
ret.parent = ResultSet_getIntByName(r, "parent");
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::time_added_desc = {
|
||||
"time_added",
|
||||
"Time added",
|
||||
""
|
||||
};
|
||||
|
||||
// SQL NULL case
|
||||
const char *pname = ResultSet_getStringByName(r, "name");
|
||||
if (pname != NULL)
|
||||
ret.name = pname;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::time_spoiled_desc = {
|
||||
"time_spoiled",
|
||||
"Time spoiled",
|
||||
""
|
||||
};
|
||||
|
||||
const char *pdescription = ResultSet_getStringByName(r, "description");
|
||||
if (pdescription != NULL)
|
||||
ret.description = pdescription;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::cash_worth_desc = {
|
||||
"cash_worth",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
ret.time_added = ResultSet_getIntByName(r, "time_added");
|
||||
ret.time_spoiled = ResultSet_getIntByName(r, "time_spoiled");
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::currency_desc = {
|
||||
"currency_id",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
const char *pcashworth = ResultSet_getStringByName(r, "cash_worth");
|
||||
if (pcashworth != NULL)
|
||||
ret.cash_worth = pcashworth;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::long_axis_desc = {
|
||||
"long_axis",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
ret.currency_id = ResultSet_getIntByName(r, "currency_id");
|
||||
} CATCH(SQLException) {
|
||||
|
||||
}
|
||||
END_TRY;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::short_axis_desc = {
|
||||
"short_axis",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
return ret;
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::height_desc = {
|
||||
"height",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::mass_desc = {
|
||||
"mass",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
const struct DBRecordElementDesc DBRecordDesc<Item>::url_desc = {
|
||||
"url",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
void Item::create_tables(db::DBSession &session) {
|
||||
const char *create_table =
|
||||
"CREATE TABLE \"items\" ( \n\
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, \n\
|
||||
`name` TEXT NOT NULL, \n\
|
||||
`sup` INTEGER NOT NULL DEFAULT -1, \n\
|
||||
`description` TEXT, \n\
|
||||
`time_added` INTEGER DEFAULT -1, \n\
|
||||
`time_spoiled` INTEGER DEFAULT -1, \n\
|
||||
`cash_worth` INTEGER DEFAULT -1, \n\
|
||||
`currency_id` INTEGER DEFAULT -1, \n\
|
||||
`long_axis` INTEGER DEFAULT -1, \n\
|
||||
`short_axis` INTEGER DEFAULT -1, \n\
|
||||
`height` INTEGER DEFAULT -1, \n\
|
||||
`mass` INTEGER DEFAULT -1, \n\
|
||||
`url` TEXT, \n\
|
||||
FOREIGN KEY(`sup`) REFERENCES items ( id ) );";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, create_table,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
|
||||
DBHierarchicalObject<Item>::create_sub_view(session);
|
||||
DBHierarchicalObject<Item>::create_sup_view(session);
|
||||
|
||||
const char *item_view =
|
||||
"CREATE VIEW item_view AS SELECT * FROM item_sub JOIN items ON items.id = item_sub.id;";
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, item_view,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
void Item::validate_tables(db::DBSession &session) {
|
||||
|
||||
}
|
||||
|
||||
void Item::remove_tables(db::DBSession &session) {
|
||||
const char *drop_table = "DROP TABLE items";
|
||||
db::DBOperations::execute<void *>(session, NULL, drop_table,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
|
||||
const char *drop_item_sub = "DROP VIEW item_sub";
|
||||
db::DBOperations::execute<void *>(session, NULL, drop_item_sub,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
|
||||
const char *drop_item_sup = "DROP VIEW item_sup";
|
||||
db::DBOperations::execute<void *>(session, NULL, drop_item_sup,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
|
||||
const char *drop_item_view = "DROP VIEW item_view;";
|
||||
db::DBOperations::execute<void *>(session, NULL, drop_item_view,
|
||||
[](void *, PreparedStatement_T s) {});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void Item::add_category(db::DBSession &session, const Category& category) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("INSERT INTO ") +
|
||||
Category::sc_membership_table +
|
||||
std::string(" (item_id, category_id) VALUES (?, ?)");
|
||||
|
||||
auto stmt_cons =
|
||||
[this, category](void *, PreparedStatement_T s) {
|
||||
PreparedStatement_setInt(s, 1, id);
|
||||
PreparedStatement_setInt(s, 2, category.id);
|
||||
};
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, prepared_stmt.c_str(),
|
||||
stmt_cons);
|
||||
}
|
||||
|
||||
void Item::remove_category(db::DBSession &session, const Category& category) const {
|
||||
const std::string prepared_stmt =
|
||||
std::string("DELETE FROM ") +
|
||||
Category::sc_membership_table +
|
||||
std::string("WHERE item_id = ?");
|
||||
|
||||
auto stmt_cons =
|
||||
[this](void *, PreparedStatement_T s) {
|
||||
PreparedStatement_setInt(s, 1, id);
|
||||
};
|
||||
|
||||
|
||||
db::DBOperations::execute<void *>(session, NULL, prepared_stmt.c_str(),
|
||||
stmt_cons);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#include "log.hh"
|
||||
#include "logstream.hh"
|
||||
#include <memory>
|
||||
|
||||
namespace inventory {
|
||||
|
||||
std::shared_ptr<Logstream> Log::mp_logstream(new StdLogstream);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#include "logstream.hh"
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
namespace inventory {
|
||||
|
||||
const char *StdLogstream::sc_loglevel[] = {
|
||||
"CRITICAL",
|
||||
"WARNING",
|
||||
"NOTICE",
|
||||
"INFO",
|
||||
"DEBUG"
|
||||
};
|
||||
|
||||
void StdLogstream::operator()(Loglevel lvl, const std::string &msg) {
|
||||
std::cerr << "[" << sc_loglevel[(int)(lvl)] << "] " << msg << std::endl;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#include "category.hh"
|
||||
#include "category_impl.hh"
|
||||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DBSession session(argv[1]);
|
||||
|
||||
return 0;
|
||||
|
||||
std::cout << "Categories' items" << std::endl;
|
||||
|
||||
DBContainerPtr<Category> cats = Category::get_all(session);
|
||||
for (DBObjectPtr<Category> cat : *cats) {
|
||||
std::cout << (std::string)(cat->name) << std::endl;
|
||||
DBContainerPtr<Item> items = cat->get_objects(session);
|
||||
for (DBObjectPtr<Item> it : *items) {
|
||||
std::cout << "\t" << (std::string)(it->name) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Items' categories" << std::endl;
|
||||
|
||||
DBContainerPtr<Item> items = Item::get_all(session);
|
||||
for (DBObjectPtr<Item> item : *items) {
|
||||
std::cout << (std::string)(item->name) << std::endl;
|
||||
DBContainerPtr<Category> cats = Category::get_assoc(session, *item);
|
||||
for (DBObjectPtr<Category> cat : *cats) {
|
||||
std::cout << "\t" << (std::string)(cat->name) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#include "dbobjectptr.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DBSession session(argv[1]);
|
||||
|
||||
return 0;
|
||||
|
||||
{
|
||||
DBContainerPtr<Item> items = Item::get_all(session);
|
||||
for (DBObjectPtr<Item> &it : *items) {
|
||||
cout << it->name.c_str() << "\t" << it->nsub << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#include "dbobjectptr.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbcontainer.hh"
|
||||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DBSession session(argv[1]);
|
||||
|
||||
return 0;
|
||||
|
||||
std::cout << "Items via vector: " << std::endl;
|
||||
{
|
||||
DBContainerPtr<Item> items = Item::get_all(session);
|
||||
for (DBObjectPtr<Item> &it : *items) {
|
||||
cout << it->name.c_str() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Items via map: " << std::endl;
|
||||
{
|
||||
DBContainerPtr<Item, DBObjectMap> items
|
||||
= Item::get_all<DBObjectMap>(session);
|
||||
for (auto &map_pair : *items) {
|
||||
DBObjectPtr<Item> it = map_pair.second;
|
||||
cout << it->name.c_str() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbobjecttree.hh"
|
||||
#include "dbobjecttree_impl.hh"
|
||||
#include "dbobjecttreeptr.hh"
|
||||
#include "category.hh"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
|
||||
std::string g_test_db_url;
|
||||
|
||||
class DBInitTest : public ::testing::Test {
|
||||
public:
|
||||
DBInitTest()
|
||||
: m_session(g_test_db_url.c_str()) {}
|
||||
|
||||
virtual void setUp() {}
|
||||
virtual void tearDown() {}
|
||||
|
||||
protected:
|
||||
DBSession m_session;
|
||||
};
|
||||
|
||||
TEST_F(DBInitTest, ItemInit) {
|
||||
Item::create_tables(m_session);
|
||||
}
|
||||
|
||||
TEST_F(DBInitTest, CategoryInit) {
|
||||
Category::create_tables(m_session);
|
||||
}
|
||||
|
||||
TEST_F(DBInitTest, ItemInsert) {
|
||||
Item root;
|
||||
root.name = "Root object";
|
||||
root.insert(m_session);
|
||||
}
|
||||
|
||||
TEST_F(DBInitTest, ItemRetrieve) {
|
||||
DBContainerPtr<Item> items = Item::get_all(m_session);
|
||||
ASSERT_STREQ((*items)[0]->name.c_str(), "Root object");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
g_test_db_url = argv[1];
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include "category.hh"
|
||||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DBSession session(argv[1]);
|
||||
|
||||
return 0;
|
||||
|
||||
Item i;
|
||||
i.name = "Specimen 1";
|
||||
i.mass = 200;
|
||||
i.url = "http://digikey.com";
|
||||
|
||||
cout << "Inserting item\n";
|
||||
|
||||
i.insert(session);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include "dbobject.hh"
|
||||
#include "dbobject_impl.hh"
|
||||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Item item;
|
||||
cout << item.prepared_statement_columns() << endl;
|
||||
cout << item.prepared_statement_value_placeholders() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#include "item.hh"
|
||||
#include "item_impl.hh"
|
||||
#include "dbsession.hh"
|
||||
#include "dbobjectvec.hh"
|
||||
#include "dbobjecttree.hh"
|
||||
#include "dbobjecttree_impl.hh"
|
||||
#include "dbobjecttreeptr.hh"
|
||||
#include "category.hh"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace inventory;
|
||||
using namespace datamodel;
|
||||
using namespace inventory::db;
|
||||
|
||||
std::string g_test_db_url;
|
||||
|
||||
template<class Object>
|
||||
class TreeTest : public ::testing::Test {
|
||||
protected:
|
||||
class Visitor : public DBObjectTree<Object>::SubtreeVisitor {
|
||||
public:
|
||||
virtual void apply(DBObjectPtr<Object> item, int max_lvl, int lvl) {
|
||||
indent(lvl);
|
||||
if (!item->is_root())
|
||||
std::cout << (std::string)(item->name) << std::endl;
|
||||
}
|
||||
|
||||
protected:
|
||||
void indent(int level) {
|
||||
for (int i = 0; i < level; i++)
|
||||
std::cout << "\t";
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Object TestedDBObject;
|
||||
|
||||
TreeTest()
|
||||
: m_session(g_test_db_url.c_str()) {}
|
||||
|
||||
virtual void setUp() {}
|
||||
virtual void tearDown() {}
|
||||
|
||||
protected:
|
||||
DBSession m_session;
|
||||
};
|
||||
|
||||
typedef TreeTest<Item> ItemTest;
|
||||
|
||||
TEST_F(ItemTest, ItemTreeTest) {
|
||||
Visitor visitor;
|
||||
DBObjectTreePtr<TestedDBObject> tree(new DBObjectTree<TestedDBObject>);
|
||||
tree->recurse_subtrees(m_session, visitor);
|
||||
EXPECT_EQ(0, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
g_test_db_url = argv[1];
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Reference in New Issue