Mercurial > malikania
changeset 29:99792c6c8b06
Server: add initial postgresql account management, #475
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 26 May 2016 07:32:05 +0200 |
parents | 80736513d699 |
children | a1e80d991968 |
files | CMakeLists.txt cmake/MalikaniaOptions.cmake cmake/MalikaniaVersion.cmake cmake/internal/sysconfig-tests.h database/CMakeLists.txt database/backend/postgresql/CMakeLists.txt database/backend/postgresql/account.cpp database/postgresql/CMakeLists.txt database/postgresql/script/init.sql database/postgresql/src/account.cpp database/postgresql/src/driver.cpp database/postgresql/src/driver.h libserver/CMakeLists.txt libserver/malikania/account.h libserver/malikania/dao-account.cpp libserver/malikania/dao-account.h libserver/malikania/database.cpp libserver/malikania/database.h libserver/malikania/dynlib.h server/CMakeLists.txt server/main.cpp tests/libserver/CMakeLists.txt tests/libserver/dao-account/CMakeLists.txt tests/libserver/dao-account/main.cpp |
diffstat | 24 files changed, 1497 insertions(+), 193 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ b/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -28,9 +28,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) +include(cmake/MalikaniaVersion.cmake) include(cmake/MalikaniaOptions.cmake) include(cmake/MalikaniaFunctions.cmake) -include(cmake/MalikaniaVersion.cmake) include(cmake/MalikaniaSystem.cmake) configure_file( @@ -45,6 +45,7 @@ find_package(OpenSSL REQUIRED) add_subdirectory(extern) +add_subdirectory(database) add_subdirectory(docs) add_subdirectory(libcommon) add_subdirectory(libclient)
--- a/cmake/MalikaniaOptions.cmake Tue Apr 19 21:13:23 2016 +0200 +++ b/cmake/MalikaniaOptions.cmake Thu May 26 07:32:05 2016 +0200 @@ -43,6 +43,31 @@ set(WITH_DOXYGEN Off) endif () +set(WITH_DRIVERDIR "lib/malikania/${MALIKANIA_VERSION}" + CACHE STRING "Database driver directory") +set(WITH_BINDIR "libexec/malikania/${MALIKANIA_VERSION}" + CACHE STRING "Front end directory") + +if (IS_ABSOLUTE ${WITH_DRIVERDIR}) + message(FATAL_ERROR "WITH_DRIVERDIR must not be absolute") +elseif (IS_ABSOLUTE ${WITH_BINDIR}) + message(FATAL_ERROR "WITH_BINDIR must not be absolute") +endif () + +# +# Options for unit tests +# ------------------------------------------------------------------- +# + +set(WITH_TEST_PGSQL_HOST "localhost" + CACHE STRING "Hostname for PostgreSQL driver") +set(WITH_TEST_PGSQL_PORT "5432" + CACHE STRING "Port for PostgreSQL driver") +set(WITH_TEST_PGSQL_DATABASE "malikaniadb" + CACHE STRING "Database for PostgreSQL driver") +set(WITH_TEST_PGSQL_USER "malikania" + CACHE STRING "Username for PostgreSQL driver") + # # Targets to build # ------------------------------------------------------------------- @@ -61,3 +86,8 @@ else () message(FATAL_ERROR "Unknown backend selected: ${WITH_BACKEND}") endif () + +configure_file( + ${malikania_SOURCE_DIR}/cmake/internal/sysconfig-tests.h + ${malikania_BINARY_DIR}/sysconfig-tests.h +)
--- a/cmake/MalikaniaVersion.cmake Tue Apr 19 21:13:23 2016 +0200 +++ b/cmake/MalikaniaVersion.cmake Thu May 26 07:32:05 2016 +0200 @@ -19,3 +19,4 @@ set(MALIKANIA_VERSION_MAJOR 0) set(MALIKANIA_VERSION_MINOR 1) set(MALIKANIA_VERSION_PATCH 0) +set(MALIKANIA_VERSION ${MALIKANIA_VERSION_MAJOR}.${MALIKANIA_VERSION_MINOR}.${MALIKANIA_VERSION_PATCH})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/internal/sysconfig-tests.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,13 @@ +#ifndef MALIKANIA_SYSCONFIG_TESTS_H +#define MALIKANIA_SYSCONFIG_TESTS_H + +/* + * PostgreSQL configuration. + */ + +#define WITH_TEST_PGSQL_HOST "@WITH_TEST_PGSQL_HOST@" +#define WITH_TEST_PGSQL_PORT "@WITH_TEST_PGSQL_PORT@" +#define WITH_TEST_PGSQL_DATABASE "@WITH_TEST_PGSQL_DATABASE@" +#define WITH_TEST_PGSQL_USER "@WITH_TEST_PGSQL_USER@" + +#endif // !MALIKANIA_SYSCONFIG_TESTS_H
--- a/database/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ b/database/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -18,4 +18,4 @@ project(database) -add_subdirectory(backend/postgresql) +add_subdirectory(postgresql)
--- a/database/backend/postgresql/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for malikania -# -# Copyright (c) 2013, 2014, 2015 Malikania Authors -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -add_library( - malikania-db-postgresql - MODULE - account.cpp -)
--- a/database/backend/postgresql/account.cpp Tue Apr 19 21:13:23 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#include <string> - -class Database; -class Account; - -extern "C" { - -void account_create(Database &database, Account &account) -{ -} - -void account_update(Database &database, Account &account) -{ -} - -void account_delete(Database &database, Account &account) -{ -} - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/database/postgresql/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,39 @@ +# +# CMakeLists.txt -- CMake build system for malikania +# +# Copyright (c) 2013, 2014, 2015 Malikania Authors +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +find_package(PostgreSQL REQUIRED) + +add_library( + mlk-driver-postgresql + MODULE + script/init.sql + src/account.cpp + src/driver.cpp + src/driver.h +) + +target_link_libraries(mlk-driver-postgresql libmlk-server ${PostgreSQL_LIBRARIES}) +target_include_directories(mlk-driver-postgresql PRIVATE ${PostgreSQL_INCLUDE_DIRS}) +target_compile_options(mlk-driver-postgresql PRIVATE -Wno-return-type-c-linkage) +set_target_properties( + mlk-driver-postgresql + PROPERTIES + PREFIX "" + OUTPUT_NAME pgsql + LIBRARY_OUTPUT_DIRECTORY ${malikania_BINARY_DIR}/fakeroot/${WITH_DRIVERDIR} +)
--- a/database/postgresql/script/init.sql Tue Apr 19 21:13:23 2016 +0200 +++ b/database/postgresql/script/init.sql Thu May 26 07:32:05 2016 +0200 @@ -1,115 +1,139 @@ -CREATE TABLE IF NOT EXISTS mk_account ( - ac_id serial, - ac_name varchar[32], - ac_email varchar[128], - ac_firstname varchar[32], - ac_lastname varchar[32], - ac_salt varchar[8], - ac_password varchar[256], - ac_joindate date, - CONSTRAINT pk_ac_id PRIMARY KEY(ac_id) -); - -CREATE TABLE IF NOT EXISTS mk_log_history ( - log_id serial, - log_ac_id serial, - CONSTRAINT pk_log_id PRIMARY KEY(log_id), - CONSTRAINT fk_log_ac_id FOREIGN KEY(log_ac_id) REFERENCES mk_account(ac_id) -); - -CREATE TABLE IF NOT EXISTS mk_character ( - ch_id serial, - ch_ac_id serial, - ch_class varchar[32], - ch_createdate date, - CONSTRAINT pk_ch_id PRIMARY KEY(ch_id), - CONSTRAINT fk_ch_ac_id FOREIGN KEY(ch_ac_id) REFERENCES mk_account(ac_id) -); - -CREATE TABLE IF NOT EXISTS mk_log_history_item ( - lhi_id serial, - lhi_log_id serial, - lhi_date time, - CONSTRAINT pk_lhi_id PRIMARY KEY(lhi_id), - CONSTRAINT fk_lhi_log_id FOREIGN KEY(lhi_log_id) REFERENCES mk_log_history(log_id) -); - -CREATE TABLE IF NOT EXISTS mk_status ( - st_id serial, - st_ch_id serial, - st_hp int, - st_mp int, - CONSTRAINT pk_st_id PRIMARY KEY(st_id), - CONSTRAINT fk_st_ch_id FOREIGN KEY(st_ch_id) REFERENCES mk_character(ch_id) -); - -CREATE TABLE IF NOT EXISTS mk_points ( - pt_id serial, - pt_st_id serial, - pt_avail smallint, - pt_force smallint, - pt_defense smallint, - pt_agility smallint, - CONSTRAINT pk_pt_id PRIMARY KEY(pt_id), - CONSTRAINT fk_st_id FOREIGN KEY(pt_st_id) REFERENCES mk_status(st_id) -); - -CREATE TABLE IF NOT EXISTS mk_build ( - bd_id serial, - bd_ac_id serial, - bd_position int, - bd_title varchar[32], - bd_password varchar[25], - bd_kind varchar[32], - CONSTRAINT pk_bd_id PRIMARY KEY(bd_id), - CONSTRAINT fk_bd_ac_id FOREIGN KEY(bd_ac_id) REFERENCES mk_account(ac_id) -); - -CREATE TABLE IF NOT EXISTS mk_chest ( - cht_id serial, - cht_bd_id serial, - cht_name varchar[32], - CONSTRAINT pk_cht_id PRIMARY KEY(cht_id), - CONSTRAINT fk_cht_bd_id FOREIGN KEY(cht_bd_id) REFERENCES mk_build(bd_id) -); - -CREATE TABLE IF NOT EXISTS mk_inventory ( - inv_id serial, - inv_ch_id serial, - CONSTRAINT pk_inv_id PRIMARY KEY(inv_id), - CONSTRAINT fk_inv_ch_id FOREIGN KEY(inv_ch_id) REFERENCES mk_character(ch_id) -); - -CREATE TABLE IF NOT EXISTS mk_artefact ( - atf_id serial, - atf_ch_id serial, - atf_name varchar[32], - CONSTRAINT pk_atf_id PRIMARY KEY(atf_id), - CONSTRAINT fk_atf_ch_id FOREIGN KEY(atf_ch_id) REFERENCES mk_character(ch_id) -); - -CREATE TABLE IF NOT EXISTS mk_quest ( - qs_id serial, - qs_ch_id serial, - qs_name varchar[32], - CONSTRAINT pk_qs_id PRIMARY KEY(qs_id), - CONSTRAINT fk_qs_ch_id FOREIGN KEY(qs_ch_id) REFERENCES mk_character(ch_id) -); - -CREATE TABLE IF NOT EXISTS mk_inventory_object ( - invo_id serial, - invo_inv_id serial, - invo_name varchar[32], - invo_count smallint, - CONSTRAINT pk_invo_id PRIMARY KEY(invo_id), - CONSTRAINT fk_invo_inv_id FOREIGN KEY(invo_inv_id) REFERENCES mk_inventory(inv_id) -); - -CREATE TABLE IF NOT EXISTS mk_quest_property ( - qp_id serial, - qp_qs_id serial, - qp_name varchar[32], - qp_value text, - CONSTRAINT pk_qp_id PRIMARY KEY(qp_id), - CONSTRAINT fk_qp_qs_id FOREIGN KEY(qp_qs_id) REFERENCES mk_quest(qs_id) -); \ No newline at end of file +-- +-- init.sql -- initialize PostgreSQL database +-- +-- Copyright (c) 2013-2016 Malikania Authors +-- +-- Permission to use, copy, modify, and/or distribute this software for any +-- purpose with or without fee is hereby granted, provided that the above +-- copyright notice and this permission notice appear in all copies. +-- +-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-- + +create table if not exists mk_info( + if_id varchar(32), + if_value varchar(256), + CONSTRAINT pk_if_id PRIMARY KEY(if_id) +); + +create table if not exists mk_account( + ac_id serial, + ac_name varchar(32), + ac_email varchar(128), + ac_firstname varchar(32), + ac_lastname varchar(32), + ac_salt varchar(8), + ac_password varchar(256), + ac_joindate date, + CONSTRAINT pk_ac_id PRIMARY KEY(ac_id) +); + +create table if not exists mk_log_history( + log_id serial, + log_ac_id serial, + CONSTRAINT pk_log_id PRIMARY KEY(log_id), + CONSTRAINT fk_log_ac_id FOREIGN KEY(log_ac_id) REFERENCES mk_account(ac_id) +); + +create table if not exists mk_character( + ch_id serial, + ch_ac_id serial, + ch_class varchar(32), + ch_createdate date, + CONSTRAINT pk_ch_id PRIMARY KEY(ch_id), + CONSTRAINT fk_ch_ac_id FOREIGN KEY(ch_ac_id) REFERENCES mk_account(ac_id) +); + +create table if not exists mk_log_history_item( + lhi_id serial, + lhi_log_id serial, + lhi_date time, + CONSTRAINT pk_lhi_id PRIMARY KEY(lhi_id), + CONSTRAINT fk_lhi_log_id FOREIGN KEY(lhi_log_id) REFERENCES mk_log_history(log_id) +); + +create table if not exists mk_status( + st_id serial, + st_ch_id serial, + st_hp int, + st_mp int, + CONSTRAINT pk_st_id PRIMARY KEY(st_id), + CONSTRAINT fk_st_ch_id FOREIGN KEY(st_ch_id) REFERENCES mk_character(ch_id) +); + +create table if not exists mk_points( + pt_id serial, + pt_st_id serial, + pt_avail smallint, + pt_force smallint, + pt_defense smallint, + pt_agility smallint, + CONSTRAINT pk_pt_id PRIMARY KEY(pt_id), + CONSTRAINT fk_st_id FOREIGN KEY(pt_st_id) REFERENCES mk_status(st_id) +); + +create table if not exists mk_build( + bd_id serial, + bd_ac_id serial, + bd_position int, + bd_title varchar(32), + bd_password varchar(25), + bd_kind varchar(32), + CONSTRAINT pk_bd_id PRIMARY KEY(bd_id), + CONSTRAINT fk_bd_ac_id FOREIGN KEY(bd_ac_id) REFERENCES mk_account(ac_id) +); + +create table if not exists mk_chest( + cht_id serial, + cht_bd_id serial, + cht_name varchar(32), + CONSTRAINT pk_cht_id PRIMARY KEY(cht_id), + CONSTRAINT fk_cht_bd_id FOREIGN KEY(cht_bd_id) REFERENCES mk_build(bd_id) +); + +create table if not exists mk_inventory( + inv_id serial, + inv_ch_id serial, + CONSTRAINT pk_inv_id PRIMARY KEY(inv_id), + CONSTRAINT fk_inv_ch_id FOREIGN KEY(inv_ch_id) REFERENCES mk_character(ch_id) +); + +create table if not exists mk_artefact( + atf_id serial, + atf_ch_id serial, + atf_name varchar(32), + CONSTRAINT pk_atf_id PRIMARY KEY(atf_id), + CONSTRAINT fk_atf_ch_id FOREIGN KEY(atf_ch_id) REFERENCES mk_character(ch_id) +); + +create table if not exists mk_quest( + qs_id serial, + qs_ch_id serial, + qs_name varchar(32), + CONSTRAINT pk_qs_id PRIMARY KEY(qs_id), + CONSTRAINT fk_qs_ch_id FOREIGN KEY(qs_ch_id) REFERENCES mk_character(ch_id) +); + +create table if not exists mk_inventory_object( + invo_id serial, + invo_inv_id serial, + invo_name varchar(32), + invo_count smallint, + CONSTRAINT pk_invo_id PRIMARY KEY(invo_id), + CONSTRAINT fk_invo_inv_id FOREIGN KEY(invo_inv_id) REFERENCES mk_inventory(inv_id) +); + +create table if not exists mk_quest_property( + qp_id serial, + qp_qs_id serial, + qp_name varchar(32), + qp_value text, + CONSTRAINT pk_qp_id PRIMARY KEY(qp_id), + CONSTRAINT fk_qp_qs_id FOREIGN KEY(qp_qs_id) REFERENCES mk_quest(qs_id) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/database/postgresql/src/account.cpp Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,130 @@ +/* + * account.cpp -- account management + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <sstream> +#include <stdexcept> +#include <vector> + +#include <malikania/dynlib.h> +#include <malikania/account.h> + +#include "driver.h" + +using namespace malikania; + +extern "C" { + +namespace { + +Account toAccount(PGresult *result, int i) +{ + assert(i < PQntuples(result)); + + Account account; + + account.setId(static_cast<std::uint64_t>(std::stoll(PQgetvalue(result, i, 0)))); + account.setName(PQgetvalue(result, i, 1)); + account.setEmail(PQgetvalue(result, i, 2)); + account.setFirstName(PQgetvalue(result, i, 3)); + account.setLastName(PQgetvalue(result, i, 4)); + account.setPassword(PQgetvalue(result, i, 6)); + + return account; +} + +} // !namespace + +DYNLIB_EXPORT void malikania_account_create(Account &account) +{ + std::ostringstream oss; + + oss << "insert into mk_account(ac_name, ac_email, ac_firstname, ac_lastname, ac_password) values ("; + oss << pgsql::escape(account.name()) << ", "; + oss << pgsql::escape(account.email()) << ", "; + oss << pgsql::escape(account.firstName()) << ", "; + oss << pgsql::escape(account.lastName()) << ", "; + oss << pgsql::escape(account.password()) << ") returning ac_id"; + + std::shared_ptr<PGresult> result = pgsql::exec(oss.str()); + + account.setId(static_cast<std::uint64_t>(std::stoll(PQgetvalue(result.get(), 0, 0)))); +} + +DYNLIB_EXPORT void malikania_account_update(Account &account) +{ + std::ostringstream oss; + + oss << "update mk_account set "; + oss << "ac_name = " << pgsql::escape(account.name()) << ", "; + oss << "ac_email = " << pgsql::escape(account.email()) << ", "; + oss << "ac_firstname = " << pgsql::escape(account.firstName()) << ", "; + oss << "ac_lastname = " << pgsql::escape(account.lastName()) << ", "; + oss << "ac_password = " << pgsql::escape(account.password()) << " "; + oss << "where ac_id = " << account.id(); + + pgsql::exec(oss.str()); +} + +DYNLIB_EXPORT void malikania_account_remove(const Account &account) +{ + std::ostringstream oss; + + oss << "delete from mk_account where ac_id = " << account.id(); + + pgsql::exec(oss.str()); +} + +DYNLIB_EXPORT Account malikania_account_get(std::uint64_t id) +{ + std::ostringstream oss; + + oss << "select * from mk_account where ac_id = " << id; + + std::shared_ptr<PGresult> result = pgsql::exec(oss.str()); + + if (PQntuples(result.get()) != 1) + throw std::out_of_range("account not found"); + + return toAccount(result.get(), 0); +} + +DYNLIB_EXPORT std::vector<malikania::Account> malikania_account_list() +{ + std::vector<Account> accounts; + std::shared_ptr<PGresult> result = pgsql::exec("select * from mk_account"); + + for (int i = 0; i < PQntuples(result.get()); ++i) + accounts.push_back(toAccount(result.get(), i)); + + return accounts; +} + +DYNLIB_EXPORT std::uint64_t malikania_account_count() +{ + std::shared_ptr<PGresult> result = pgsql::exec("select count(*) from mk_account"); + + return static_cast<std::uint64_t>(std::stoll(PQgetvalue(result.get(), 0, 0))); +} + +DYNLIB_EXPORT void malikania_account_clear() +{ + pgsql::exec("delete from mk_account"); +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/database/postgresql/src/driver.cpp Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,113 @@ +/* + * driver.cpp -- PostgreSQL database driver + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sstream> +#include <string> +#include <unordered_map> + +#include <malikania/dynlib.h> + +#include "driver.h" + +namespace pgsql { + +PGconn *connection{nullptr}; + +std::shared_ptr<PGresult> exec(const std::string &sql) +{ + std::shared_ptr<PGresult> ptr(PQexec(connection, sql.c_str()), PQclear); + + if (PQresultStatus(ptr.get()) != PGRES_TUPLES_OK && PQresultStatus(ptr.get()) != PGRES_COMMAND_OK) + throw std::runtime_error(PQresultErrorMessage(ptr.get())); + + return ptr; +} + +std::string escape(const std::string &input) +{ + auto text = PQescapeLiteral(connection, input.c_str(), input.length()); + + if (!text) + throw std::runtime_error(PQerrorMessage(connection)); + + std::string result(text); + + PQfreemem(text); + + return result; +} + +} // !pgsql + +namespace { + +std::string parameters(const std::unordered_map<std::string, std::string> ¶ms) +{ + std::ostringstream oss; + std::unordered_map<std::string, std::string>::const_iterator it; + + // Host. + if ((it = params.find("host")) == params.end()) + throw std::runtime_error("missing 'host' parameter"); + + oss << "host = " << it->second << " "; + + // Database. + if ((it = params.find("database")) == params.end()) + throw std::runtime_error("missing 'database' parameter"); + + oss << "dbname = " << it->second << " "; + + // User. + if ((it = params.find("user")) == params.end()) + throw std::runtime_error("missing 'user' parameter"); + + oss << "user = " << it->second << " "; + + // Port (optional). + if ((it = params.find("port")) != params.end()) + oss << "port = " << it->second << " "; + + // Password (optional). + if ((it = params.find("password")) != params.end()) + oss << "password = " << it->second << " "; + + return oss.str(); +} + +} // !namespace + +extern "C" { + +DYNLIB_EXPORT void malikania_driver_load(const std::unordered_map<std::string, std::string> ¶ms) +{ + pgsql::connection = PQconnectdb(parameters(params).c_str()); + + if (!pgsql::connection) + throw std::runtime_error(PQerrorMessage(pgsql::connection)); +} + +DYNLIB_EXPORT void malikania_driver_unload() +{ + if (pgsql::connection) { + PQfinish(pgsql::connection); + pgsql::connection = nullptr; + } +} + +} // !C
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/database/postgresql/src/driver.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,37 @@ +/* + * driver.h -- PostgreSQL database driver + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_POSTGRESQL_DRIVER_H +#define MALIKANIA_POSTGRESQL_DRIVER_H + +#include <memory> +#include <string> + +#include <libpq-fe.h> + +namespace pgsql { + +extern PGconn *connection; + +std::shared_ptr<PGresult> exec(const std::string &sql); + +std::string escape(const std::string &input); + +} // !pgsql + +#endif // !MALIKANIA_POSTGRESQL_DRIVER_H
--- a/libserver/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ b/libserver/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -16,24 +16,26 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -# set( -# HEADERS -# ${CMAKE_CURRENT_SOURCE_DIR}/malikania/Server.h -# ${CMAKE_CURRENT_SOURCE_DIR}/malikania/ServerApp.h -# ) -# -# set( -# SOURCES -# ${CMAKE_CURRENT_SOURCE_DIR}/malikania/Server.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/malikania/ServerApp.cpp -# ) -# -# malikania_create_library( -# PROJECT libserver -# TARGET libserver -# SOURCES ${HEADERS} ${SOURCES} -# PUBLIC_INCLUDES -# ${CMAKE_CURRENT_SOURCE_DIR} -# LIBRARIES -# libcommon -# ) +project(libmlk-server) + +set( + HEADERS + ${libmlk-server_SOURCE_DIR}/malikania/account.h + ${libmlk-server_SOURCE_DIR}/malikania/dao-account.h + ${libmlk-server_SOURCE_DIR}/malikania/database.h + ${libmlk-server_SOURCE_DIR}/malikania/dynlib.h +) + +set( + SOURCES + ${libmlk-server_SOURCE_DIR}/malikania/dao-account.cpp + ${libmlk-server_SOURCE_DIR}/malikania/database.cpp +) + +malikania_create_library( + TARGET libmlk-server + SOURCES ${HEADERS} ${SOURCES} + LIBRARIES libcommon +) + +target_include_directories(libmlk-server PUBLIC ${libmlk-server_SOURCE_DIR})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/account.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,180 @@ +/* + * account.h -- account model + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_ACCOUNT_H +#define MALIKANIA_ACCOUNT_H + +/** + * \file account.h + * \brief Account model. + */ + +#include <cstdint> +#include <string> + +namespace malikania { + +/** + * \brief Account model + */ +class Account { +private: + std::uint64_t m_id{0}; + std::string m_name; + std::string m_email; + std::string m_firstName; + std::string m_lastName; + std::string m_password; + +public: + /** + * Get the id. + * + * \return the id + */ + inline std::uint64_t id() const noexcept + { + return m_id; + } + + /** + * Set the id. + * + * \param id the id + */ + inline void setId(std::uint64_t id) noexcept + { + m_id = id; + } + + /** + * Get the account name. + * + * \return the name + */ + inline const std::string &name() const noexcept + { + return m_name; + } + + /** + * Set the account name. + * + * \param name the name + */ + inline void setName(std::string name) noexcept + { + m_name = std::move(name); + } + + /** + * Get the email. + * + * \return the email address + */ + inline const std::string &email() const noexcept + { + return m_email; + } + + /** + * Set the email. + * + * \param email the email address + */ + inline void setEmail(std::string email) noexcept + { + m_email = std::move(email); + } + + /** + * Get the first name. + * + * \return the first name + */ + inline const std::string &firstName() const noexcept + { + return m_firstName; + } + + /** + * Set the first name. + * + * \param firstName the first name + */ + inline void setFirstName(std::string firstName) noexcept + { + m_firstName = std::move(firstName); + } + + /** + * Get the last name. + * + * \return the last name + */ + inline const std::string &lastName() const noexcept + { + return m_lastName; + } + + /** + * Set the last name. + * + * \param lastName the last name + */ + inline void setLastName(std::string lastName) noexcept + { + m_lastName = std::move(lastName); + } + + /** + * Get the password. + * + * \return the password + */ + inline const std::string &password() const noexcept + { + return m_password; + } + + /** + * Set the password. + * + * \param password the password + */ + inline void setPassword(std::string password) noexcept + { + m_password = std::move(password); + } +}; + +inline bool operator==(const Account &ac1, const Account &ac2) noexcept +{ + return ac1.id() == ac2.id() && ac1.name() == ac2.name() && ac1.email() == ac2.email() && + ac1.firstName() == ac2.firstName() && ac1.lastName() == ac2.lastName() && + ac1.password() == ac2.password(); +} + +inline bool operator!=(const Account &ac1, const Account &ac2) noexcept +{ + return !(ac1 == ac2); +} + +} // !malikania + +#endif // !MALIKANIA_ACCOUNT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/dao-account.cpp Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,68 @@ +/* + * dao-account.cpp -- database account management + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "account.h" +#include "dao-account.h" +#include "database.h" + +namespace malikania { + +using Create = void (*)(Account &); +using Remove = void (*)(const Account &); +using Update = void (*)(Account &); +using Get = Account (*)(std::uint64_t); +using List = std::vector<Account> (*)(); +using Count = std::uint64_t (*)(); +using Clear = void (*)(); + +void AccountDao::create(Account &account) +{ + m_database.handle().sym<Create>("malikania_account_create")(account); +} + +void AccountDao::remove(const Account &account) +{ + m_database.handle().sym<Remove>("malikania_account_remove")(account); +} + +void AccountDao::update(Account &account) +{ + m_database.handle().sym<Update>("malikania_account_update")(account); +} + +Account AccountDao::get(uint64_t id) +{ + return m_database.handle().sym<Get>("malikania_account_get")(id); +} + +std::vector<Account> AccountDao::list() +{ + return m_database.handle().sym<List>("malikania_account_list")(); +} + +std::uint64_t AccountDao::count() +{ + return m_database.handle().sym<Count>("malikania_account_count")(); +} + +void AccountDao::clear() +{ + m_database.handle().sym<Clear>("malikania_account_clear")(); +} + +} // !malikania
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/dao-account.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,113 @@ +/* + * dao-account.h -- database account management + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_DAO_ACCOUNT_H +#define MALIKANIA_DAO_ACCOUNT_H + +/** + * \file dao-account.h + * \brief Database account management. + */ + +#include <cstdint> +#include <vector> + +namespace malikania { + +class Database; +class Account; + +/** + * \brief Account DAO. + */ +class AccountDao { +private: + Database &m_database; + +public: + /** + * Constructor. + * + * \param database the database + */ + inline AccountDao(Database &database) noexcept + : m_database(database) + { + } + + /** + * Create the given account. + * + * The object will be modified in place. + * + * \param account the account to add + * \throw std::exception on errors + */ + void create(Account &account); + + /** + * Remove the given account, all data that references this account is deleted too. + * + * \param account the account + * \throw std::exception on errors + */ + void remove(const Account &account); + + /** + * Update account only, does not recurse into objects that references the account. + * + * \param account the account + * \throw std::exception on errors + */ + void update(Account &account); + + /** + * Get an account. + * + * \param id the account id + * \return the account + * \throw std::exception if not found + */ + Account get(std::uint64_t id); + + /** + * Get the list of account. + * + * \throw std::exception on errors + */ + std::vector<Account> list(); + + /** + * Get the number of accounts. + * + * \return the number of account. + */ + std::uint64_t count(); + + /** + * Remove all accounts recursively. + * + * \throw std::exception on errors + * \warning use with care + */ + void clear(); +}; + +} // !malikania + +#endif // !MALIKANIA_DAO_ACCOUNT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/database.cpp Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,39 @@ +/* + * database.cpp -- generic database loader + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "database.h" + +namespace malikania { + +using Load = void (*)(const std::unordered_map<std::string, std::string> &); +using Unload = void (*)(); + +// TODO: is path is relative, search for it + +Database::Database(const std::string &path, const std::unordered_map<std::string, std::string> ¶ms) + : m_dso(path) +{ + m_dso.sym<Load>("malikania_driver_load")(params); +} + +Database::~Database() +{ + m_dso.sym<Unload>("malikania_driver_unload")(); +} + +} // !malikania
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/database.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,69 @@ +/* + * database.h -- generic database loader + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_DATABASE_H +#define MALIKANIA_DATABASE_H + +/** + * \file database.h + * \brief Generic database loader. + */ + +#include <string> +#include <unordered_map> + +#include "dynlib.h" + +namespace malikania { + +/** + * \brief Generic database. + */ +class Database { +private: + Dynlib m_dso; + +public: + /** + * Load the database driver dynamically. + * + * The type is the canonical file name (e.g. "pgsql"). + * + * \param type the database name + * \param params the parameters to pass to the driver + * \throw std::exception on errors + */ + Database(const std::string &type, const std::unordered_map<std::string, std::string> ¶ms); + + /** + * Close the database. + */ + ~Database(); + + /** + * Get the associated dynlib handle. + */ + inline Dynlib &handle() noexcept + { + return m_dso; + } +}; + +} // !malikania + +#endif // !MALIKANIA_DATABASE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/dynlib.h Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,338 @@ +/* + * dynlib.hpp -- portable shared library loader + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DYNLIB_HPP +#define DYNLIB_HPP + +/** + * \file dynlib.hpp + * \brief Portable shared library loader. + * \author David Demelier <markand@malikania.fr> + */ + +/** + * \page Dynlib Dynlib + * \brief Portable shared library loader. + * + * The dynlib module let you open shared libraries dynamically at runtime. + * + * ## Operating system support + * + * | System | Support | Remarks | + * |---------|---------|--------------------| + * | Apple | Ok | | + * | FreeBSD | Ok | | + * | Linux | Ok | Needs -ldl library | + * | Windows | Ok | | + * + * ## How to export symbols + * + * When you want to dynamically load symbols from your shared library, make sure they are in a `extern "C"` block, if + * not they will be [mangled][name-mangling]. + * + * Note, this does not mean that you can't write C++ code, it just mean that you can't use namespaces and function + * overloading. + * + * Example of **plugin.cpp**: + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * extern "C" { + * + * DYNLIB_EXPORT void plugin_load() + * { + * std::cout << "Loading plugin" << std::endl; + * } + * + * DYNLIB_EXPORT void plugin_unload() + * { + * std::cout << "Unloading plugin" << std::endl; + * } + * + * } + * ```` + * + * The \ref DYNLIB_EXPORT macro is necessary on some platforms to be sure that symbol will be visible. Make sure you always + * add it before any function. + * + * To compile, see your compiler documentation or build system. For gcc you can use the following: + * + * ```` + * gcc -std=c++14 -shared plugin.cpp -o plugin.so + * ```` + * + * ## How to load the library + * + * The dynlib module will search for the library in various places, thus you can use relative paths names but be sure + * that the library can be found. Otherwise, just use an absolute path to the file. + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * int main() + * { + * try { + * Dynlib dso("./plugin" DYNLIB_SUFFIX); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * + * return 0; + * } + * ```` + * + * ## How to load symbol + * + * The last part is symbol loading, you muse use raw C function pointer and the Dynlib::sym function. + * + * ````cpp + * #include <iostream> + * + * #include "dynlib.hpp" + * + * using PluginLoad = void (*)(); + * using PluginUnload = void (*)(); + * + * int main() + * { + * try { + * Dynlib dso("./plugin" DYNLIB_SUFFIX); + * + * dso.sym<PluginLoad>("plugin_load")(); + * dso.sym<PluginUnload>("plugin_unload")(); + * } catch (const std::exception &ex) { + * std::cerr << ex.what() << std::endl; + * } + * + * return 0; + * } + * ```` + * + * [name-mangling]: https://en.wikipedia.org/wiki/Name_mangling + */ + +#include <stdexcept> +#include <string> + +#if defined(_WIN32) +# include <Windows.h> +#else +# include <dlfcn.h> +#endif + +/** + * \brief Export the symbol. + * + * This is required on some platforms and you should put it before your function signature. + * + * \code{.cpp} + * extern "C" { + * + * DYNLIB_EXPORT void my_function() + * { + * } + * + * } + * \endcode + */ +#if defined(_WIN32) +# define DYNLIB_EXPORT __declspec(dllexport) +#else +# define DYNLIB_EXPORT +#endif + +/** + * \brief Usual suffix for the library. + * + * This macro expands to the suffix convention for this platform. + * + * \code{.cpp} + * Dynlib library("./myplugin" DYNLIB_SUFFIX); + * \endcode + * + * \note Don't use the suffix expanded value shown in Doxygen as it may be wrong. + */ +#if defined(_WIN32) +# define DYNLIB_SUFFIX ".dll" +#elif defined(__APPLE__) +# define DYNLIB_SUFFIX ".dylib" +#else +# define DYNLIB_SUFFIX ".so" +#endif + +namespace malikania { + +/** + * \class Dynlib + * \brief Load a dynamic module. + * + * This class is a portable wrapper to load shared libraries on supported systems. + */ +class Dynlib { +private: +#if defined(_WIN32) + using Handle = HMODULE; + using Sym = FARPROC; +#else + using Handle = void *; + using Sym = void *; +#endif + +public: + /** + * \brief Policy for symbol resolution. + */ + enum Policy { + Immediately, //!< load symbols immediately + Lazy //!< load symbols when needed + }; + +private: + Handle m_handle; + + Dynlib(const Dynlib &) = delete; + Dynlib &operator=(const Dynlib &) = delete; + + Dynlib(Dynlib &&) = delete; + Dynlib &operator=(Dynlib &&) = delete; + +#if defined(_WIN32) + std::string error() + { + LPSTR error = nullptr; + std::string errmsg; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, NULL); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; + } +#endif + +public: + /** + * Constructor to load a shared module. + * + * \param path the absolute path + * \param policy the policy to load + * \throw std::runtime_error on error + */ + Dynlib(const std::string &path, Policy policy = Immediately); + + /** + * Close the library automatically. + */ + ~Dynlib(); + + /** + * Get a symbol from the library. + * + * On some platforms the symbol must be manually exported. + * + * \param name the symbol + * \return the symbol + * \throw std::runtime_error on error + * \see DYNLIB_EXPORT + */ + template <typename T> + T sym(const std::string &name); +}; + +#if defined(_WIN32) + +/* + * Windows implementation + * ------------------------------------------------------------------ + */ + +inline Dynlib::Dynlib(const std::string &path, Policy) +{ + m_handle = LoadLibraryA(path.c_str()); + + if (m_handle == nullptr) + throw std::runtime_error(error()); +} + +inline Dynlib::~Dynlib() +{ + FreeLibrary(m_handle); + m_handle = nullptr; +} + +template <typename T> +T Dynlib::sym(const std::string &name) +{ + Sym sym = GetProcAddress(m_handle, name.c_str()); + + if (sym == nullptr) + throw std::runtime_error(error()); + + return reinterpret_cast<T>(sym); +} + +#else + +/* + * Unix implementation + * ------------------------------------------------------------------ + */ + +inline Dynlib::Dynlib(const std::string &path, Policy policy) +{ + m_handle = dlopen(path.c_str(), policy == Immediately ? RTLD_NOW : RTLD_LAZY); + + if (m_handle == nullptr) + throw std::runtime_error(dlerror()); +} + +inline Dynlib::~Dynlib() +{ + dlclose(m_handle); + m_handle = nullptr; +} + +template <typename T> +T Dynlib::sym(const std::string &name) +{ + Sym sym = dlsym(m_handle, name.c_str()); + + if (sym == nullptr) + throw std::runtime_error(dlerror()); + + return reinterpret_cast<T>(sym); +} + +#endif + +} // !malikania + +#endif // !DYNLIB_HPP
--- a/server/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ b/server/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -16,9 +16,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -# project(server) -# -# add_executable(server main.cpp) -# -# target_include_directories(server PRIVATE ${Mongo_INCLUDE_DIRS}) -# target_link_libraries(server libserver ${Mongo_LIBRARIES}) +project(mlk-server) + +add_executable(mlk-server main.cpp) +target_link_libraries(mlk-server libmlk-server)
--- a/server/main.cpp Tue Apr 19 21:13:23 2016 +0200 +++ b/server/main.cpp Thu May 26 07:32:05 2016 +0200 @@ -18,17 +18,38 @@ #include <iostream> -#include <malikania/Game.h> -#include <malikania/ResourcesLocator.h> -#include <malikania/ResourcesLoader.h> +#include "malikania/account.h" +#include "malikania/dao-account.h" +#include "malikania/database.h" using namespace malikania; int main(int, char **) { - ResourcesLocatorDirectory locator("/home/markand/mygame"); - ResourcesLoader loader(locator); - Game game = loader.loadGame(); + try { + Database database("./pgsql", { + { "user", "kingdom" }, + { "host", "localhost" }, + { "database", "kingdomdb" } + }); + + AccountDao act(database); + Account account; + + account.setId(2); + + act.remove(account); + + for (const Account &ac : act.list()) { + std::cout << "id : " << ac.id() << std::endl; + std::cout << "name : " << ac.name() << std::endl; + std::cout << "email : " << ac.email() << std::endl; + std::cout << "firstname : " << ac.firstName() << std::endl; + std::cout << "lastname : " << ac.lastName() << std::endl; + } + } catch (const std::exception &ex) { + std::cerr << ex.what() << std::endl; + } return 0; }
--- a/tests/libserver/CMakeLists.txt Tue Apr 19 21:13:23 2016 +0200 +++ b/tests/libserver/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -17,3 +17,4 @@ # add_subdirectory(id) +add_subdirectory(dao-account)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/libserver/dao-account/CMakeLists.txt Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,25 @@ +# +# CMakeLists.txt -- CMake build system for malikania +# +# Copyright (c) 2013-2016 Malikania Authors +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +malikania_create_test( + NAME dao-account + LIBRARIES libmlk-server + SOURCES main.cpp +) + +target_compile_definitions(test-dao-account PRIVATE DRIVERDIR=\"${CMAKE_BINARY_DIR}/fakeroot/${WITH_DRIVERDIR}/\")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/libserver/dao-account/main.cpp Thu May 26 07:32:05 2016 +0200 @@ -0,0 +1,105 @@ +/* + * main.cpp -- test AccountDao + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> + +#include <gtest/gtest.h> + +#include <sysconfig-tests.h> + +#include <malikania/account.h> +#include <malikania/dao-account.h> +#include <malikania/database.h> + +using namespace malikania; + +class TestAccountDao : public testing::Test { +protected: + Database m_database; + AccountDao m_dao; + +public: + TestAccountDao() + : m_database(DRIVERDIR "pgsql.so", { + { "host", WITH_TEST_PGSQL_HOST }, + { "port", WITH_TEST_PGSQL_PORT }, + { "user", WITH_TEST_PGSQL_USER }, + { "database", WITH_TEST_PGSQL_DATABASE } + }) + , m_dao(m_database) + { + m_dao.clear(); + + assert(m_dao.count() == 0U); + } +}; + +TEST_F(TestAccountDao, create) +{ + try { + Account ac; + + ac.setName("jean"); + ac.setEmail("jean@christophe.fr"); + ac.setFirstName("Jean"); + ac.setLastName("Christophe"); + ac.setPassword("raw"); + + m_dao.create(ac); + + ASSERT_EQ(1U, m_dao.count()); + ASSERT_EQ(ac, m_dao.list()[0]); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(TestAccountDao, update) +{ + try { + Account ac; + + ac.setName("jean"); + ac.setEmail("jean@christophe.fr"); + ac.setFirstName("Jean"); + ac.setLastName("Christophe"); + ac.setPassword("raw"); + + m_dao.create(ac); + + ac.setEmail("benoit@christophe.fr"); + ac.setFirstName("Benoit"); + + m_dao.update(ac); + + Account ac2 = m_dao.get(ac.id()); + + ASSERT_EQ("jean", ac2.name()); + ASSERT_EQ("benoit@christophe.fr", ac2.email()); + ASSERT_EQ("Benoit", ac2.firstName()); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}