Commit 627ea7d9 authored by Pavel Vainerman's avatar Pavel Vainerman

(PostgreSQL): Заложил начальный вариант реализации (на основе кода ilyap@etersoft.ru)

parent ad03b191
%def_enable docs
%def_enable mysql
%def_enable sqlite
%def_enable postgresql
%def_enable python
%def_enable rrd
%def_enable io
......@@ -12,7 +13,7 @@
Name: libuniset2
Version: 2.0
Release: alt30
Release: alt24.1
Summary: UniSet - library for building distributed industrial control systems
......@@ -42,6 +43,10 @@ BuildRequires: libMySQL-devel
BuildRequires: libsqlite3-devel
%endif
%if_enabled postgresql
BuildRequires: postgresql9.3-devel libpq5.6-devel
%endif
%if_enabled rrd
BuildRequires: librrd-devel
%endif
......@@ -175,6 +180,24 @@ Requires: %name-extension-common = %version-%release
Libraries needed to develop for uniset SQLite
%endif
%if_enabled postgresql
%package extension-postgresql
Group: Development/Databases
Summary: PostgreSQL-dbserver implementatioin for UniSet
Requires: %name-extension-common = %version-%release
%description extension-postgresql
PostgreSQL dbserver for %name
%package extension-postgresql-devel
Group: Development/Databases
Summary: Libraries needed to develop for uniset PostgreSQL
Requires: %name-extension-common-devel = %version-%release
%description extension-postgresql-devel
Libraries needed to develop for uniset PostgreSQL
%endif
%if_enabled rrd
%package extension-rrd
Group: Development/C++
......@@ -241,7 +264,7 @@ SharedMemoryPlus extension ('all in one') for libuniset
%build
%autoreconf
%configure %{subst_enable docs} %{subst_enable mysql} %{subst_enable sqlite} %{subst_enable python} %{subst_enable rrd} %{subst_enable io} %{subst_enable logicproc} %{subst_enable tests}
%configure %{subst_enable doc} %{subst_enable mysql} %{subst_enable sqlite} %{subst_enable postgresql} %{subst_enable python} %{subst_enable rrd} %{subst_enable io} %{subst_enable logicproc} %{subst_enable tests}
%make
%install
......@@ -292,6 +315,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
%if_enabled sqlite
%_includedir/%oname/sqlite/
%endif
%if_enabled postgresql
%_includedir/%oname/postgresql/
%endif
%_libdir/libUniSet2.so
%_datadir/idl/%oname/
......@@ -315,6 +341,15 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
%_pkgconfigdir/libUniSet2SQLite.pc
%endif
%if_enabled postgresql
%files extension-postgresql
%_bindir/%oname-postgresql-*dbserver
%_libdir/*-postgresql.so*
%files extension-postgresql-devel
%_pkgconfigdir/libUniSet2PostgreSQL.pc
%endif
%if_enabled python
%files -n python-module-%oname
%dir %python_sitelibdir/%oname
......@@ -409,38 +444,6 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
# ..
%changelog
* Fri May 08 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt30
- ModbusSlave: added support nbit='' for 0x06 and 0x10 function (setbug #7337)
* Tue May 05 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt29
- SM: add reserv mechanism for initializing (setbug #7289)
- SM: fixed bug in 'heartbeat'
- SM: add tests
- minor fixes
- refactoring
- add new tests
* Thu Apr 23 2015 Pavel Vainerman <pv@etersoft.ru> 2.0-alt28.2
- unet-udp: special build... change maximum for digital and analog data. Set A=1500, D=5000.
* Thu Apr 23 2015 Pavel Vainerman <pv@etersoft.ru> 2.0-alt28.1
- unet-udp: special build... change maximum for digital and analog data. Set A=800, D=5000.
* Mon Apr 20 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt28
- optimization: use std::unordered_map instead of std::map
* Thu Apr 16 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt27
- (UniXML): refactoring UniXML::iterator::find..
* Thu Apr 09 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt26
- (ModbusSlave): added support nbit
- (ModbusSlave): added support 0x01 (readCoilStatus) function
- (ModbusSlave): minor fixes
* Tue Apr 07 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt25
- fixed bug in 'MBSlave' (thank`s Alexandr Hanadeev)
- add --xxx-set-prop-prefix for MBSlave
* Thu Apr 02 2015 Pavel Vainerman <pv@altlinux.ru> 2.0-alt24
- codegen: modify interface for messages (setMsg())
- remove alarm() function (deprecated)
......@@ -498,6 +501,9 @@ mv -f %buildroot%python_sitelibdir_noarch/* %buildroot%python_sitelibdir/%oname
- fixed bug in specfile: --enable-doc --> --enable-docs
- transition to use shared_ptr wherever possible
* Sat Dec 20 2014 Pavel Vainerman <pv@altlinux.ru> 2.0-alt10.1
- added PostgreSQL support
* Mon Nov 24 2014 Pavel Vainerman <pv@altlinux.ru> 2.0-alt10
- use shared_ptr
......
#ifndef DBServer_PostgreSQL_H_
#define DBServer_PostgreSQL_H_
// --------------------------------------------------------------------------
#include <map>
#include <queue>
#include "UniSetTypes.h"
#include "PostgreSQLInterface.h"
#include "DBServer.h"
//------------------------------------------------------------------------------------------
class DBServer_PostgreSQL:
public DBServer
{
public:
DBServer_PostgreSQL( UniSetTypes::ObjectId id );
DBServer_PostgreSQL();
~DBServer_PostgreSQL();
static const Debug::type DBLogInfoLevel = Debug::LEVEL9;
protected:
typedef std::map<int, std::string> DBTableMap;
virtual void initDB(PostgreSQLInterface *db){};
virtual void initDBTableMap(DBTableMap& tblMap){};
virtual void timerInfo( const UniSetTypes::TimerMessage* tm ) override;
virtual void sysCommand( const UniSetTypes::SystemMessage* sm ) override;
virtual void sensorInfo( const UniSetTypes::SensorMessage* sm ) override;
virtual void confirmInfo( const UniSetTypes::ConfirmMessage* cmsg ) override;
virtual void sigterm( int signo ) override;
bool writeToBase( const string& query );
virtual void init_dbserver();
void createTables( PostgreSQLInterface* db );
inline const char* tblName(int key)
{
return tblMap[key].c_str();
}
enum Timers
{
PingTimer, /*!< таймер на переодическую проверку соединения с сервером БД */
ReconnectTimer, /*!< таймер на повторную попытку соединения с сервером БД (или восстановления связи) */
lastNumberOfTimer
};
PostgreSQLInterface *db;
int PingTime;
int ReconnectTime;
bool connect_ok; /*! признак наличия соеднинения с сервером БД */
bool activate;
typedef std::queue<std::string> QueryBuffer;
QueryBuffer qbuf;
unsigned int qbufSize; // размер буфера сообщений.
bool lastRemove;
void flushBuffer();
UniSetTypes::uniset_mutex mqbuf;
private:
DBTableMap tblMap;
};
//------------------------------------------------------------------------------------------
#endif
if DISABLE_PGSQL
else
UPGSQL_VER=@LIBVER@
lib_LTLIBRARIES = libUniSet2-postgresql.la
libUniSet2_postgresql_la_LDFLAGS = -version-info $(UPGSQL_VER)
libUniSet2_postgresql_la_SOURCES = PostgreSQLInterface.cc DBServer_PostgreSQL.cc
libUniSet2_postgresql_la_LIBADD = $(top_builddir)/lib/libUniSet2.la $(POSTGRESQL_LIBS)
libUniSet2_postgresql_la_CXXFLAGS = $(POSTGRESQL_CFLAGS)
bin_PROGRAMS = @PACKAGE@-postgresql-dbserver
@PACKAGE@_postgresql_dbserver_LDADD = libUniSet2-postgresql.la $(top_builddir)/lib/libUniSet2.la $(POSTGRESQL_LIBS)
@PACKAGE@_postgresql_dbserver_CXXFLAGS = $(POSTGRESQL_CFLAGS)
@PACKAGE@_postgresql_dbserver_SOURCES = main.cc
noinst_PROGRAMS = postgresql-test
postgresql_test_LDADD = libUniSet2-postgresql.la $(top_builddir)/lib/libUniSet2.la $(POSTGRESQL_LIBS)
postgresql_test_CXXFLAGS = $(POSTGRESQL_CFLAGS)
postgresql_test_SOURCES = test.cc
# install
devel_include_HEADERS = *.h
devel_includedir = $(includedir)/@PACKAGE@/postgresql
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libUniSet2PostgreSQL.pc
endif
// --------------------------------------------------------------------------
#include <sstream>
#include <cstdio>
#include <UniSetTypes.h>
#include "PostgreSQLInterface.h"
// --------------------------------------------------------------------------
using namespace std;
using namespace UniSetTypes;
// --------------------------------------------------------------------------
PostgreSQLInterface::PostgreSQLInterface():
db(0),
lastQ(""),
lastE(""),
queryok(false),
connected(false),
last_inserted_id(0)
{
}
PostgreSQLInterface::~PostgreSQLInterface()
{
close();
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::ping()
{
return db && ( PQstatus(db) == CONNECTION_OK );
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::connect( const string& host, const string& user, const string& pswd, const string& dbname)
{
if(connected == true)
return true;
std::string conninfo ="dbname="+dbname+" host="+host+" user="+user+" password="+pswd;
db = PQconnectdb(conninfo.c_str());
if (PQstatus(db) == CONNECTION_BAD) {
connected = false;
return false;
}
connected = true;
return true;
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::close()
{
if( db )
{
freeResult();
PQfinish(db);
db = 0;
}
return true;
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::insert( const string& q )
{
if( !db )
return false;
result = PQexec(db, q.c_str());
ExecStatusType status = PQresultStatus(result);
if( !checkResult(status) ){
queryok = false;
freeResult();
return false;
}
queryok = true;
freeResult();
return true;
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::insertAndSaveRowid( const string& q )
{
if( !db )
return false;
std::string qplus=q+" RETURNING id";
result = PQexec(db, qplus.c_str());
ExecStatusType status = PQresultStatus(result);
if( !checkResult(status) ){
queryok = false;
freeResult();
return false;
}
save_inserted_id(result);
queryok = true;
freeResult();
return true;
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::checkResult( ExecStatusType rc )
{
if( rc == PGRES_BAD_RESPONSE || rc == PGRES_NONFATAL_ERROR || rc == PGRES_FATAL_ERROR )
return false;
/*
NORMAL
PGRES-COMMAND-OK -- Successful completion of a command returning no data
PGRES-TUPLES-OK -- The query successfully executed
PGRES-COPY-OUT -- Copy Out (from server) data transfer started
PGRES-COPY-IN -- Copy In (to server) data transfer started
PGRES-EMPTY-QUERY -- The string sent to the backend was empty
ERRORS:
PGRES-BAD-RESPONSE -- The server's response was not understood
PGRES-NONFATAL-ERROR
PGRES-FATAL-ERROR
*/
return true;
}
// -----------------------------------------------------------------------------------------
PostgreSQLResult PostgreSQLInterface::query( const string& q )
{
if( !db )
return PostgreSQLResult();
result = PQexec(db, q.c_str());
ExecStatusType status = PQresultStatus(result);
if( !checkResult(status) )
{
queryok = false;
return PostgreSQLResult();
}
lastQ = q;
queryok = true;
return PostgreSQLResult(result);
}
// -----------------------------------------------------------------------------------------
string PostgreSQLInterface::error()
{
if( db )
lastE = PQerrorMessage(db);
return lastE;
}
// -----------------------------------------------------------------------------------------
const string PostgreSQLInterface::lastQuery()
{
return lastQ;
}
// -----------------------------------------------------------------------------------------
double PostgreSQLInterface::insert_id()
{
return last_inserted_id;
}
// -----------------------------------------------------------------------------------------
void PostgreSQLInterface::save_inserted_id( PGresult* res )
{
if( PQntuples(res) == 1 && PQnfields(res) == 1 )
last_inserted_id = atoll(PQgetvalue(res, 0, 0));
}
// -----------------------------------------------------------------------------------------
bool PostgreSQLInterface::isConnection()
{
return connected;
}
// -----------------------------------------------------------------------------------------
void PostgreSQLInterface::freeResult()
{
if( !result )
return;
//
queryok = false;
PQclear(result);
}
// -----------------------------------------------------------------------------------------
int num_cols( PostgreSQLResult::iterator& it )
{
return it->size();
}
// -----------------------------------------------------------------------------------------
int as_int( PostgreSQLResult::iterator& it, int col )
{
// if( col<0 || col >it->size() )
// return 0;
return uni_atoi( (*it)[col] );
}
// -----------------------------------------------------------------------------------------
double as_double( PostgreSQLResult::iterator& it, int col )
{
return atof( ((*it)[col]).c_str() );
}
// -----------------------------------------------------------------------------------------
string as_string( PostgreSQLResult::iterator& it, int col )
{
return ((*it)[col]);
}
// -----------------------------------------------------------------------------------------
int as_int( PostgreSQLResult::COL::iterator& it )
{
return uni_atoi( (*it) );
}
// -----------------------------------------------------------------------------------------
double as_double( PostgreSQLResult::COL::iterator& it )
{
return atof( (*it).c_str() );
}
// -----------------------------------------------------------------------------------------
std::string as_string( PostgreSQLResult::COL::iterator& it )
{
return (*it);
}
// -----------------------------------------------------------------------------------------
#if 0
PostgreSQLResult::COL get_col( PostgreSQLResult::ROW::iterator& it )
{
return (*it);
}
#endif
// -----------------------------------------------------------------------------------------
PostgreSQLResult::~PostgreSQLResult()
{
}
// -----------------------------------------------------------------------------------------
PostgreSQLResult::PostgreSQLResult( PGresult* res, bool clearRES )
{
int rec_count = PQntuples(res);
int rec_fields = PQnfields(res);
for (int nrow=0; nrow<rec_count; nrow++) {
COL c;
for (int ncol=0; ncol<rec_fields; ncol++) {
// printf("%s\t", PQgetvalue(res, row, col));
char* p = (char*)PQgetvalue(res, nrow, ncol);
if( p )
c.push_back(p);
else
c.push_back("");
}
row.push_back(c);
}
if( clearRES )
PQclear(res);
}
// -----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef PostgreSQLInterface_H_
#define PostgreSQLInterface_H_
// ---------------------------------------------------------------------------
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <libpq-fe.h>
#include <PassiveTimer.h>
// ----------------------------------------------------------------------------
class PostgreSQLResult;
// ----------------------------------------------------------------------------
class PostgreSQLInterface
{
public:
PostgreSQLInterface();
~PostgreSQLInterface();
bool connect( const std::string& host, const std::string& user, const std::string& pswd, const std::string& dbname );
bool close();
bool isConnection();
bool ping(); // проверка доступности БД
PostgreSQLResult query( const std::string& q );
const std::string lastQuery();
bool insert( const std::string& q );
bool insertAndSaveRowid( const std::string& q );
double insert_id();
void save_inserted_id(PGresult*);
std::string error();
void freeResult();
protected:
// bool wait( ExecStatusType rc );
static bool checkResult( ExecStatusType rc );
private:
PGconn* db;
PGresult* result;
std::string lastQ;
std::string lastE;
bool queryok; // успешность текущего запроса
bool connected;
double last_inserted_id;
// timeout_t opTimeout;
// timeout_t opCheckPause;
};
// ----------------------------------------------------------------------------------
class PostgreSQLResult
{
public:
PostgreSQLResult(){}
PostgreSQLResult( PGresult *res, bool clear = true ); // clear - clear result (call PQclear)
~PostgreSQLResult();
typedef std::vector<std::string> COL;
typedef std::list<COL> ROW;
typedef ROW::iterator iterator;
inline iterator begin(){ return row.begin(); }
inline iterator end(){ return row.end(); }
inline operator bool(){ return !row.empty(); }
inline int size(){ return row.size(); }
inline bool empty(){ return row.empty(); }
protected:
ROW row;
};
// ----------------------------------------------------------------------------
int num_cols( PostgreSQLResult::iterator& );
// ROW
int as_int( PostgreSQLResult::iterator&, int col );
double as_double( PostgreSQLResult::iterator&, int col );
std::string as_text( PostgreSQLResult::iterator&, int col );
// ----------------------------------------------------------------------------
// COL
int as_int( PostgreSQLResult::COL::iterator& );
double as_double( PostgreSQLResult::COL::iterator& );
std::string as_string( PostgreSQLResult::COL::iterator& );
//----------------------------------------------------------------------------
#endif
// ----------------------------------------------------------------------------------
#!/bin/sh
ln -s -f /usr/bin/uniset2-stop.sh stop.sh
ln -s -f ../../conf/test.xml test.xml
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libUniSet2PostgreSQL
Description: Support library for libUniSet2PostgreSQL
Requires: libUniSet2 libpq
Version: @VERSION@
Libs: -L${libdir} -lUniSet2-postgresql
Cflags: -I${includedir}/@PACKAGE@ -I${includedir}/@PACKAGE@/postgresql
#include "Configuration.h"
#include "DBServer_PostgreSQL.h"
#include "UniSetActivator.h"
#include "Debug.h"
// --------------------------------------------------------------------------
using namespace UniSetTypes;
using namespace std;
// --------------------------------------------------------------------------
static void short_usage()
{
cout << "Usage: uniset-mysql-dbserver [--name ObjectId] [--confile configure.xml]\n";
}
// --------------------------------------------------------------------------
int main(int argc, char** argv)
{
try
{
if( argc > 1 && !strcmp(argv[1],"--help") )
{
short_usage();
return 0;
}
auto conf = uniset_init(argc,argv,"configure.xml");
ObjectId ID = conf->getDBServer();
// определяем ID объекта
string name = conf->getArgParam("--name");
if( !name.empty())
{
if( ID != UniSetTypes::DefaultObjectId )
{
ulog.warn() << "(DBServer::main): переопределяем ID заданнй в "
<< conf->getConfFileName() << endl;
}
ID = conf->oind->getIdByName(conf->getServicesSection()+"/"+name);
if( ID == UniSetTypes::DefaultObjectId )
{
cerr << "(DBServer::main): идентификатор '" << name
<< "' не найден в конф. файле!"
<< " в секции " << conf->getServicesSection() << endl;
return 1;
}
}
else if( ID == UniSetTypes::DefaultObjectId )
{
cerr << "(DBServer::main): Не удалось определить ИДЕНТИФИКАТОР сервера" << endl;
short_usage();
return 1;
}
DBServer_PostgreSQL dbs(ID);
UniSetActivatorPtr act = UniSetActivator::Instance();
act->addObject(static_cast<class UniSetObject*>(&dbs));
act->run(false);
}
catch(Exception& ex)
{
cerr << "(DBServer::main): " << ex << endl;
}
catch(...)
{
cerr << "(DBServer::main): catch ..." << endl;
}
return 0;
}
#!/bin/sh
ulimit -Sc 1000000
uniset2-start.sh -f ./uniset2-postgresql-dbserver --confile test.xml --name DBServer1 \
--ulog-add-levels info,crit,warn,level9,system \
--dbserver-buffer-size 100
#include <iostream>
#include <sstream>
#include "Exceptions.h"
#include "PostgreSQLInterface.h"
// --------------------------------------------------------------------------
using namespace UniSetTypes;
using namespace std;
// --------------------------------------------------------------------------
int main(int argc, char** argv)
{
std::string dbname("test-db");
if( argc > 1 )
dbname = string(argv[1]);
try
{
PostgreSQLInterface db;
if( !db.connect("localhost","dbadmin","dbadmin",dbname) )
{
cerr << "db connect error: " << db.error() << endl;
return 1;
}
stringstream q;
q << "SELECT * from main_history";
PostgreSQLResult r = db.query(q.str());
if( !r )
{
cerr << "db connect error: " << db.error() << endl;
return 1;
}
for( PostgreSQLResult::iterator it=r.begin(); it!=r.end(); it++ )
{
cout << "ROW: ";
PostgreSQLResult::COL col(*it);
for( PostgreSQLResult::COL::iterator cit = it->begin(); cit!=it->end(); cit++ )
cout << as_string(cit) << "(" << as_double(cit) << ") | ";
cout << endl;
}
db.close();
}
catch( Exception& ex )
{
cerr << "(test): " << ex << endl;
}
catch( std::exception& ex )
{
cerr << "(test): " << ex.what() << endl;
}
catch(...)
{
cerr << "(test): catch ..." << endl;
}
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment