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
|
INC = -Ibuild -Isource/include -I/usr/include/zdb -Igoogletest/googletest/include
|
||||||
LIBS = -lcairo -lqrencode -lzdb -lpthread
|
LIB = -Lbuild -Lgoogletest/googletest
|
||||||
|
UNITTESTLIBS = -lzdb -lpthread -lgtest_main -lgtest
|
||||||
|
SOLIBS = -lzdb -lpthread
|
||||||
|
|
||||||
CXXSOURCE = $(wildcard source/*.cc)
|
CXXSOURCE = $(wildcard source/*.cc)
|
||||||
CSOURCE = $(wildcard source/*.c)
|
CSOURCE = $(wildcard source/*.c)
|
||||||
|
@ -10,46 +12,104 @@ COBJ = $(CSOURCE:source/%.c=build/%.o)
|
||||||
CXXOBJ = $(CXXSOURCE:source/%.cc=build/%.o)
|
CXXOBJ = $(CXXSOURCE:source/%.cc=build/%.o)
|
||||||
OBJ = $(COBJ) $(CXXOBJ)
|
OBJ = $(COBJ) $(CXXOBJ)
|
||||||
|
|
||||||
CDEPEND = $(CSOURCE:source/%.c=build/%.c.d)
|
UNITTEST_CXXSOURCE = $(wildcard unittest/*.cc)
|
||||||
CXXDEPEND = $(CXXSOURCE:source/%.cc=build/%.cc.d)
|
UNITTEST_CXXOBJ = $(UNITTEST_CXXSOURCE:unittest/%.cc=build/unittest/%.o)
|
||||||
DEPEND = $(CDEPEND) $(CXXDEPEND)
|
UNITTEST_TARGETS = $(UNITTEST_CXXOBJ:%.o=%)
|
||||||
.PRECIOUS: $(DEPEND) $(OBJ)
|
UNITTEST_LOGS = $(UNITTEST_TARGETS:%=%.xml)
|
||||||
.SECONDARY: $(DEPEND) $(OBJ)
|
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
|
CXXDEPEND = $(CXXSOURCE:source/%.cc=build/%.cc.d) \
|
||||||
CXX = g++
|
$(UNITTEST_CXXSOURCE:unittest/%.cc=build/unittest/%.cc.d)
|
||||||
LD = $(CXX)
|
HEADERDEPEND = $(HEADERS:source/include/%.hh=build/%.hh.d)
|
||||||
CXXFLAGS = -std=c++14 -g -fPIC
|
DEPEND = $(CXXDEPEND) $(HEADERDEPEND)
|
||||||
SOFLAGS = -shared
|
|
||||||
|
|
||||||
.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)
|
depend: $(DEPEND)
|
||||||
|
|
||||||
clean:
|
clean: clean_testresults
|
||||||
rm -f build/*.o
|
rm -f build/*.o
|
||||||
|
rm -f build/unittest/*.o
|
||||||
rm -f $(TARGETS)
|
rm -f $(TARGETS)
|
||||||
|
rm -f $(UNITTEST_LOGS)
|
||||||
|
rm -f build/unittest/*.db
|
||||||
|
rm -f $(PRECOMPILED_HEADERS)
|
||||||
|
|
||||||
|
clean_testresults:
|
||||||
|
rm -f $(UNITTEST_TARGETS)
|
||||||
|
|
||||||
mrproper: clean
|
mrproper: clean
|
||||||
rm -f build/*.d
|
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
|
build/%.cc.d: source/%.cc
|
||||||
$(CXX) -M $< -o $@ $(INC) $(CXXFLAGS)
|
$(CXX) -MM $< -o $@ $(INC) $(CXXFLAGS)
|
||||||
|
|
||||||
build/%.c.d: source/%.c
|
build/%.o: source/%.cc build/%.cc.d $(PRECOMPILED_HEADERS)
|
||||||
$(CXX) -M $< -o $@ $(INC) $(CXXFLAGS)
|
|
||||||
|
|
||||||
build/%.o: source/%.cc build/%.cc.d
|
|
||||||
$(CXX) -c $< -o $@ $(INC) $(CXXFLAGS)
|
$(CXX) -c $< -o $@ $(INC) $(CXXFLAGS)
|
||||||
|
|
||||||
build/%.o: source/%.c build/%.c.d
|
|
||||||
$(CC) -c $< -o $@ $(INC) $(CFLAGS)
|
|
||||||
|
|
||||||
build/%.so: $(OBJ)
|
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/*.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 "category.hh"
|
||||||
#include "session.hh"
|
#include "dbobject.hh"
|
||||||
|
#include "item.hh"
|
||||||
|
#include "dbsession.hh"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <zdb.h>
|
#include <zdb.h>
|
||||||
// refer to libzdb docs
|
// refer to libzdb docs
|
||||||
|
@ -12,36 +14,53 @@ namespace inventory {
|
||||||
namespace datamodel {
|
namespace datamodel {
|
||||||
|
|
||||||
const char *Category::sc_table = "categories";
|
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) {
|
const struct DBRecordElementDesc Category::desc_desc = {
|
||||||
Category ret;
|
"description",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
TRY {
|
const struct DBRecordElementDesc Category::symbol_desc = {
|
||||||
ret.id = ResultSet_getIntByName(r, "id");
|
"symbol",
|
||||||
ret.parent = ResultSet_getIntByName(r, "parent");
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
const char *pname = ResultSet_getStringByName(r, "name");
|
void Category::create_tables(db::DBSession &session) {
|
||||||
if (pname != NULL)
|
const char *create_table =
|
||||||
ret.name = pname;
|
"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");
|
db::DBOperations::execute<void *>(session, NULL, create_table,
|
||||||
if (pdescription != NULL)
|
[](void *, PreparedStatement_T s) {});
|
||||||
ret.description = pdescription;
|
|
||||||
|
|
||||||
int symbol_size;
|
DBHierarchicalObject<Category>::create_sub_view(session);
|
||||||
// returns void* by default, and std::copy needs type size info
|
DBHierarchicalObject<Category>::create_sup_view(session);
|
||||||
const char *psymbol =
|
DBAssocHierarchicalObject<Item, Category>::create_membership_table(session);
|
||||||
(const char *)(ResultSet_getBlobByName(r, "symbol", &symbol_size));
|
|
||||||
if (psymbol != NULL) {
|
const char *category_view =
|
||||||
ret.symbol.resize(symbol_size);
|
"CREATE VIEW category_view AS SELECT * FROM category_sub \n\
|
||||||
std::copy(psymbol, psymbol + symbol_size, std::back_inserter(ret.symbol));
|
JOIN categories ON categories.id = category_sub.id;";
|
||||||
}
|
|
||||||
} CATCH(SQLException) {
|
db::DBOperations::execute<void *>(session, NULL, category_view,
|
||||||
|
[](void *, PreparedStatement_T s) {});
|
||||||
}
|
}
|
||||||
END_TRY;
|
|
||||||
|
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 <zdb.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
@ -9,6 +9,16 @@ DBSession::DBSession(const char *url)
|
||||||
: m_url(url), m_pool(NULL) {
|
: 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() {
|
void DBSession::create_connpool() {
|
||||||
if (m_pool == NULL) {
|
if (m_pool == NULL) {
|
||||||
URL_T url = URL_new(m_url.c_str());
|
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() {
|
Connection_T DBSession::get_connection() {
|
||||||
create_connpool();
|
create_connpool();
|
||||||
Connection_T conn = ConnectionPool_getConnection(m_pool);
|
Connection_T conn = ConnectionPool_getConnection(m_pool);
|
||||||
|
@ -25,5 +43,9 @@ Connection_T DBSession::get_connection() {
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DBSession::return_connection(Connection_T conn) {
|
||||||
|
ConnectionPool_returnConnection(m_pool, conn);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,44 @@
|
||||||
#ifndef INVENTORY_CATEGORY_HH_
|
#ifndef INVENTORY_CATEGORY_HH_
|
||||||
#define INVENTORY_CATEGORY_HH_
|
#define INVENTORY_CATEGORY_HH_
|
||||||
#include <string>
|
#include "dbcontainer.hh"
|
||||||
#include <vector>
|
#include "dbobjectvec.hh"
|
||||||
#include <zdb.h>
|
#include "dbobjectptr.hh"
|
||||||
|
#include "dbobject.hh"
|
||||||
|
#include "dbfield.hh"
|
||||||
|
#include "dbassocobject.hh"
|
||||||
|
|
||||||
namespace inventory {
|
namespace inventory {
|
||||||
|
namespace db {
|
||||||
|
class DBSession;
|
||||||
|
}
|
||||||
|
|
||||||
namespace datamodel {
|
namespace datamodel {
|
||||||
class Category {
|
class Item;
|
||||||
|
class Category : public DBAssocHierarchicalObject<Item, Category> {
|
||||||
public:
|
public:
|
||||||
int id;
|
static const char *sc_table;
|
||||||
int parent;
|
static const char *sc_view;
|
||||||
std::string name;
|
static const char *sc_membership_table;
|
||||||
std::string description;
|
static const char *sc_dbclass;
|
||||||
std::vector<char> symbol;
|
|
||||||
|
|
||||||
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
|
#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_
|
#define INVENTORY_GROUP_HH_
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "category.hh"
|
||||||
|
|
||||||
namespace inventory {
|
namespace inventory {
|
||||||
namespace datamodel {
|
namespace datamodel {
|
||||||
|
|
|
@ -1,31 +1,67 @@
|
||||||
#ifndef INVENTORY_ITEM_HH_
|
#ifndef INVENTORY_ITEM_HH_
|
||||||
#define INVENTORY_ITEM_HH_
|
#define INVENTORY_ITEM_HH_
|
||||||
#include "session.hh"
|
#include "dbobjectptr.hh"
|
||||||
#include <zdb.h>
|
#include "dbcontainer.hh"
|
||||||
#include <string>
|
#include "dbsession.hh"
|
||||||
#include <vector>
|
#include "dbobject.hh"
|
||||||
|
#include "dbfield.hh"
|
||||||
|
#include "cxx-semantics.hh"
|
||||||
|
|
||||||
namespace inventory {
|
namespace inventory {
|
||||||
namespace datamodel {
|
namespace datamodel {
|
||||||
class Item {
|
|
||||||
|
class Item;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class DBRecordDesc<Item> : semantics::non_constructible {
|
||||||
public:
|
public:
|
||||||
// raw data in table order
|
static const struct DBRecordElementDesc desc_desc;
|
||||||
// no need for deeper abstraction here
|
static const struct DBRecordElementDesc time_added_desc;
|
||||||
int id;
|
static const struct DBRecordElementDesc time_spoiled_desc;
|
||||||
int parent;
|
static const struct DBRecordElementDesc cash_worth_desc;
|
||||||
std::string name;
|
static const struct DBRecordElementDesc currency_desc;
|
||||||
std::string description;
|
static const struct DBRecordElementDesc long_axis_desc;
|
||||||
unsigned int time_added;
|
static const struct DBRecordElementDesc short_axis_desc;
|
||||||
unsigned int time_spoiled;
|
static const struct DBRecordElementDesc height_desc;
|
||||||
std::string cash_worth;
|
static const struct DBRecordElementDesc mass_desc;
|
||||||
int currency_id;
|
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
|
DBString<Item> description;
|
||||||
static Item from_query(ResultSet_T);
|
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
|
#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 "item.hh"
|
||||||
#include "session.hh"
|
#include "category.hh"
|
||||||
|
#include "dboperations.hh"
|
||||||
|
#include "dbsession.hh"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <zdb.h>
|
#include <zdb.h>
|
||||||
|
@ -10,38 +14,155 @@ namespace inventory {
|
||||||
namespace datamodel {
|
namespace datamodel {
|
||||||
|
|
||||||
const char *Item::sc_table = "items";
|
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) {
|
const struct DBRecordElementDesc DBRecordDesc<Item>::desc_desc = {
|
||||||
Item ret;
|
"description",
|
||||||
|
"Description",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
TRY {
|
const struct DBRecordElementDesc DBRecordDesc<Item>::time_added_desc = {
|
||||||
ret.id = ResultSet_getIntByName(r, "id");
|
"time_added",
|
||||||
ret.parent = ResultSet_getIntByName(r, "parent");
|
"Time added",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
// SQL NULL case
|
const struct DBRecordElementDesc DBRecordDesc<Item>::time_spoiled_desc = {
|
||||||
const char *pname = ResultSet_getStringByName(r, "name");
|
"time_spoiled",
|
||||||
if (pname != NULL)
|
"Time spoiled",
|
||||||
ret.name = pname;
|
""
|
||||||
|
};
|
||||||
|
|
||||||
const char *pdescription = ResultSet_getStringByName(r, "description");
|
const struct DBRecordElementDesc DBRecordDesc<Item>::cash_worth_desc = {
|
||||||
if (pdescription != NULL)
|
"cash_worth",
|
||||||
ret.description = pdescription;
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
ret.time_added = ResultSet_getIntByName(r, "time_added");
|
const struct DBRecordElementDesc DBRecordDesc<Item>::currency_desc = {
|
||||||
ret.time_spoiled = ResultSet_getIntByName(r, "time_spoiled");
|
"currency_id",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
const char *pcashworth = ResultSet_getStringByName(r, "cash_worth");
|
const struct DBRecordElementDesc DBRecordDesc<Item>::long_axis_desc = {
|
||||||
if (pcashworth != NULL)
|
"long_axis",
|
||||||
ret.cash_worth = pcashworth;
|
"",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
ret.currency_id = ResultSet_getIntByName(r, "currency_id");
|
const struct DBRecordElementDesc DBRecordDesc<Item>::short_axis_desc = {
|
||||||
} CATCH(SQLException) {
|
"short_axis",
|
||||||
|
"",
|
||||||
}
|
""
|
||||||
END_TRY;
|
};
|
||||||
|
|
||||||
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