Start adding sqlite3 support

This commit is contained in:
antao 2018-12-26 18:37:11 +08:00
parent bd5cd65c6a
commit 161a964238
9 changed files with 466 additions and 50 deletions

View File

@ -90,6 +90,15 @@ if(MYSQL_FOUND)
endif()
endif()
#Find sqlite3.
find_package (SQLite3)
if (SQLITE3_FOUND)
include_directories(${SQLITE3_INCLUDE_DIRS})
link_libraries(${SQLITE3_LIBRARIES})
aux_source_directory(${PROJECT_SOURCE_DIR}/orm_lib/src/sqlite3_impl DIR_SRCS)
set(USE_ORM TRUE)
endif (SQLITE3_FOUND)
message(STATUS ${DIR_SRCS})
if(CMAKE_BUILD_TYPE STREQUAL "")
@ -136,6 +145,13 @@ if(USE_ORM)
else()
file(APPEND "${CONFIG_HEADER}" "#define USE_MYSQL 0\n")
endif()
if(SQLITE3_FOUND)
file(APPEND "${CONFIG_HEADER}" "#define USE_SQLITE3 1\n")
else()
file(APPEND "${CONFIG_HEADER}" "#define USE_SQLITE3 0\n")
endif()
else()
file(APPEND "${CONFIG_HEADER}" "#define USE_ORM 0\n")
endif()

View File

@ -0,0 +1,37 @@
# Copyright (C) 2007-2009 LuaDist.
# Created by Peter Kapec <kapecp@gmail.com>
# Redistribution and use of this file is allowed according to the terms of the MIT license.
# For details see the COPYRIGHT file distributed with LuaDist.
# Note:
# Searching headers and libraries is very simple and is NOT as powerful as scripts
# distributed with CMake, because LuaDist defines directories to search for.
# Everyone is encouraged to contact the author with improvements. Maybe this file
# becomes part of CMake distribution sometimes.
# - Find sqlite3
# Find the native SQLITE3 headers and libraries.
#
# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc.
# SQLITE3_LIBRARIES - List of libraries when using sqlite.
# SQLITE3_FOUND - True if sqlite found.
# Look for the header file.
FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h)
# Look for the library.
FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if all listed variables are TRUE.
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
# Copy the results to the output variables.
IF(SQLITE3_FOUND)
SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY})
SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR})
ELSE(SQLITE3_FOUND)
SET(SQLITE3_LIBRARIES)
SET(SQLITE3_INCLUDE_DIRS)
ENDIF(SQLITE3_FOUND)
MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)

View File

@ -40,7 +40,20 @@ namespace orm
enum class ClientType
{
PostgreSQL = 0,
Mysql
Mysql,
Sqlite3
};
enum Sqlite3Type
{
Sqlite3TypeChar = 0,
Sqlite3TypeShort,
Sqlite3TypeInt,
Sqlite3TypeInt64,
Sqlite3TypeDouble,
Sqlite3TypeText,
Sqlite3TypeBlob,
Sqlite3TypeNull
};
class DbClient;
@ -296,6 +309,28 @@ class SqlBinder
}
#endif
}
else if (_type == ClientType::Sqlite3)
{
_objs.push_back(obj);
_parameters.push_back((char *)obj.get());
_length.push_back(0);
switch (sizeof(T))
{
case 1:
_format.push_back(Sqlite3TypeChar);
break;
case 2:
_format.push_back(Sqlite3TypeShort);
break;
case 4:
_format.push_back(Sqlite3TypeInt);
break;
case 8:
_format.push_back(Sqlite3TypeInt64);
default:
break;
}
}
//LOG_TRACE << "Bind parameter:" << parameter;
return *this;
}

View File

@ -31,57 +31,57 @@ namespace orm
class DbClientImpl : public DbClient, public std::enable_shared_from_this<DbClientImpl>
{
public:
DbClientImpl(const std::string &connInfo, const size_t connNum, ClientType type);
virtual ~DbClientImpl() noexcept;
virtual void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback) override;
virtual std::shared_ptr<Transaction> newTransaction(const std::function<void(bool)> &commitCallback = std::function<void(bool)>()) override;
public:
DbClientImpl(const std::string &connInfo, const size_t connNum, ClientType type);
virtual ~DbClientImpl() noexcept;
virtual void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback) override;
virtual std::shared_ptr<Transaction> newTransaction(const std::function<void(bool)> &commitCallback = std::function<void(bool)>()) override;
private:
void ioLoop();
std::shared_ptr<trantor::EventLoop> _loopPtr;
void execSql(const DbConnectionPtr &conn, const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback);
private:
void ioLoop();
std::shared_ptr<trantor::EventLoop> _loopPtr;
void execSql(const DbConnectionPtr &conn, const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback);
DbConnectionPtr newConnection();
DbConnectionPtr newConnection();
std::unordered_set<DbConnectionPtr> _connections;
std::unordered_set<DbConnectionPtr> _readyConnections;
std::unordered_set<DbConnectionPtr> _busyConnections;
std::string _connInfo;
std::thread _loopThread;
std::mutex _connectionsMutex;
std::condition_variable _condConnectionReady;
size_t _transWaitNum = 0;
std::unordered_set<DbConnectionPtr> _connections;
std::unordered_set<DbConnectionPtr> _readyConnections;
std::unordered_set<DbConnectionPtr> _busyConnections;
std::string _connInfo;
std::thread _loopThread;
std::mutex _connectionsMutex;
std::condition_variable _condConnectionReady;
size_t _transWaitNum = 0;
size_t _connectNum;
bool _stop = false;
size_t _connectNum;
bool _stop = false;
struct SqlCmd
{
std::string _sql;
size_t _paraNum;
std::vector<const char *> _parameters;
std::vector<int> _length;
std::vector<int> _format;
QueryCallback _cb;
ExceptPtrCallback _exceptCb;
};
std::deque<SqlCmd> _sqlCmdBuffer;
std::mutex _bufferMutex;
struct SqlCmd
{
std::string _sql;
size_t _paraNum;
std::vector<const char *> _parameters;
std::vector<int> _length;
std::vector<int> _format;
QueryCallback _cb;
ExceptPtrCallback _exceptCb;
};
std::deque<SqlCmd> _sqlCmdBuffer;
std::mutex _bufferMutex;
void handleNewTask(const DbConnectionPtr &conn);
void handleNewTask(const DbConnectionPtr &conn);
};
} // namespace orm

View File

@ -26,10 +26,11 @@ int main()
*clientPtr << "select * from users where id!=139 order by id" << Mode::Blocking >> [](const Result &r) {
std::cout << "rows:" << r.size() << std::endl;
std::cout << "column num:" << r.columns() << std::endl;
// for (auto row : r)
// {
// std::cout << "user_id=" << row["user_id"].as<std::string>() << std::endl;
// }
for (auto row : r)
{
std::cout << "id=" << row["id"].as<int>() << std::endl;
}
// for (auto row : r)
// {
// for (auto f : row)

View File

@ -0,0 +1,155 @@
/**
*
* Sqlite3Connection.cc
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include "Sqlite3Connection.h"
#include "Sqlite3ResultImpl.h"
using namespace drogon::orm;
std::once_flag Sqlite3Connection::_once;
Sqlite3Connection::Sqlite3Connection(trantor::EventLoop *loop, const std::string &connInfo)
: DbConnection(loop),
_queue("Sqlite")
{
std::call_once(_once, []() {
auto ret = sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
if (ret != SQLITE_OK)
{
LOG_FATAL << sqlite3_errstr(ret);
}
});
auto thisPtr = shared_from_this();
_queue.runTaskInQueue([thisPtr, connInfo]() {
sqlite3 *tmp = nullptr;
auto ret = sqlite3_open(connInfo.data(), &tmp);
thisPtr->_conn = std::shared_ptr<sqlite3>(tmp, [=](sqlite3 *ptr) { sqlite3_close(ptr); });
if (ret != SQLITE_OK)
{
LOG_FATAL << sqlite3_errstr(ret);
thisPtr->_closeCb(thisPtr);
}
else
{
sqlite3_extended_result_codes(tmp, true);
thisPtr->_okCb(thisPtr);
}
});
}
void Sqlite3Connection::execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb)
{
auto thisPtr = shared_from_this();
_queue.runTaskInQueue([=]() {
thisPtr->execSqlInQueue(sql, paraNum, parameters, length, format, rcb, exceptCallback, idleCb);
});
}
void Sqlite3Connection::execSqlInQueue(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb)
{
sqlite3_stmt *stmt = nullptr;
const char *remaining;
auto ret = sqlite3_prepare_v2(_conn.get(), sql.data(), -1, &stmt, &remaining);
if (ret != SQLITE_OK)
{
//FIXME:throw exception here;
return;
}
if (!std::all_of(remaining, sql.data() + sql.size(), [](char ch) { return std::isspace(ch); }))
{
///close stmt
///FIXME
///throw errors::more_statements("Multiple semicolon separated statements are unsupported", sql);
return;
}
for (int i = 0; i < parameters.size(); i++)
{
int bindRet;
switch (format[i])
{
case Sqlite3TypeChar:
bindRet = sqlite3_bind_int(stmt, i + 1, *(char *)parameters[i]);
break;
case Sqlite3TypeShort:
bindRet = sqlite3_bind_int(stmt, i + 1, *(short *)parameters[i]);
break;
case Sqlite3TypeInt:
bindRet = sqlite3_bind_int(stmt, i + 1, *(int32_t *)parameters[i]);
break;
case Sqlite3TypeInt64:
bindRet = sqlite3_bind_int64(stmt, i + 1, *(int64_t *)parameters[i]);
break;
case Sqlite3TypeDouble:
bindRet = sqlite3_bind_double(stmt, i + 1, *(double *)parameters[i]);
break;
case Sqlite3TypeText:
bindRet = sqlite3_bind_text(stmt, i + 1, parameters[i], -1, SQLITE_STATIC);
break;
case Sqlite3TypeBlob:
bindRet = sqlite3_bind_blob(stmt, i + 1, parameters[i], length[i], SQLITE_STATIC);
break;
case Sqlite3TypeNull:
bindRet = sqlite3_bind_null(stmt, i + 1);
break;
}
if (bindRet != SQLITE_OK)
{
//FIXME throw!
return;
}
}
int r;
int columnNum = sqlite3_column_count(stmt);
Sqlite3ResultImpl result(sql);
while ((r = sqlite3_step(stmt)) == SQLITE_ROW)
{
std::vector<std::shared_ptr<std::string>> row;
for (int i = 0; i < columnNum; i++)
{
switch (sqlite3_column_type(stmt, i))
{
case SQLITE_INTEGER:
case SQLITE_FLOAT:
case SQLITE_TEXT:
case SQLITE_BLOB:
case SQLITE_NULL:
row.push_back(nullptr);
break;
}
}
result._result->push_back(std::move(row));
}
if (r != SQLITE_DONE)
{
//FIXME throw
return;
}
rcb(result);
idleCb();
}

View File

@ -0,0 +1,64 @@
/**
*
* Sqlite3Connection.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include "../DbConnection.h"
#include <drogon/orm/DbClient.h>
#include <trantor/utils/NonCopyable.h>
#include <trantor/utils/SerialTaskQueue.h>
#include <sqlite3.h>
#include <memory>
#include <string>
#include <functional>
#include <iostream>
#include <thread>
namespace drogon
{
namespace orm
{
class Sqlite3Connection;
typedef std::shared_ptr<Sqlite3Connection> Sqlite3ConnectionPtr;
class Sqlite3Connection : public DbConnection, public std::enable_shared_from_this<Sqlite3Connection>
{
public:
Sqlite3Connection(trantor::EventLoop *loop, const std::string &connInfo);
virtual void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb) override;
private:
static std::once_flag _once;
void execSqlInQueue(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb);
trantor::SerialTaskQueue _queue;
std::shared_ptr<sqlite3> _conn;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,55 @@
/**
*
* Sqlite3ResultImpl.cc
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#include "Sqlite3ResultImpl.h"
#include <assert.h>
using namespace drogon::orm;
Result::size_type Sqlite3ResultImpl::size() const noexcept
{
return _result->size();
}
Result::row_size_type Sqlite3ResultImpl::columns() const noexcept
{
return _result->empty() ? 0 : (*_result)[0].size();
}
const char *Sqlite3ResultImpl::columnName(row_size_type number) const
{
//FIXME
return "";
}
Result::size_type Sqlite3ResultImpl::affectedRows() const noexcept
{
return _affectedRows;
}
Result::row_size_type Sqlite3ResultImpl::columnNumber(const char colName[]) const
{
//FIXME
return 0;
}
const char *Sqlite3ResultImpl::getValue(size_type row, row_size_type column) const
{
auto col = (*_result)[row][column];
return col ? nullptr : col->c_str();
}
bool Sqlite3ResultImpl::isNull(size_type row, row_size_type column) const
{
return !(*_result)[row][column];
}
Result::field_size_type Sqlite3ResultImpl::getLength(size_type row, row_size_type column) const
{
auto col = (*_result)[row][column];
return col ? 0 : col->length();
}

View File

@ -0,0 +1,53 @@
/**
*
* Sqlite3ResultImpl.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include "../ResultImpl.h"
#include <sqlite3.h>
#include <memory>
#include <string>
#include <vector>
namespace drogon
{
namespace orm
{
class Sqlite3ResultImpl : public ResultImpl
{
public:
Sqlite3ResultImpl(const std::string &query) noexcept
: _query(query),
_result(new std::vector<std::vector<std::shared_ptr<std::string>>>)
{
}
virtual size_type size() const noexcept override;
virtual row_size_type columns() const noexcept override;
virtual const char *columnName(row_size_type Number) const override;
virtual size_type affectedRows() const noexcept override;
virtual row_size_type columnNumber(const char colName[]) const override;
virtual const char *getValue(size_type row, row_size_type column) const override;
virtual bool isNull(size_type row, row_size_type column) const override;
virtual field_size_type getLength(size_type row, row_size_type column) const override;
private:
friend class Sqlite3Connection;
std::shared_ptr<std::vector<std::vector<std::shared_ptr<std::string>>>> _result;
std::string _query;
size_t _affectedRows = 0;
};
} // namespace orm
} // namespace drogon