Loading [MathJax]/extensions/tex2jax.js
Logo ROOT  
Reference Guide
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
TPgSQLServer.cxx
Go to the documentation of this file.
1// @(#)root/pgsql:$Id$
2// Author: g.p.ciceri <gp.ciceri@acm.org> 01/06/2001
3
4/*************************************************************************
5 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TPgSQLServer.h"
13#include "TPgSQLResult.h"
14#include "TPgSQLStatement.h"
15
16#include "TSQLColumnInfo.h"
17#include "TSQLTableInfo.h"
18#include "TSQLRow.h"
19#include "TUrl.h"
20#include "TList.h"
21
22#define pgsql_success(x) (((x) == PGRES_EMPTY_QUERY) \
23 || ((x) == PGRES_COMMAND_OK) \
24 || ((x) == PGRES_TUPLES_OK))
25
26
27////////////////////////////////////////////////////////////////////////////////
28/// PluginManager generator function
29
30TSQLServer* ROOT_Plugin_TPgSQLServer(const char* db, const char* uid, const char* pw) {
31 return new TPgSQLServer(db, uid, pw);
32}
33
34
36
37////////////////////////////////////////////////////////////////////////////////
38/// Open a connection to a PgSQL DB server. The db arguments should be
39/// of the form "pgsql://<host>[:<port>][/<database>]", e.g.:
40/// "pgsql://pcroot.cern.ch:3456/test". The uid is the username and pw
41/// the password that should be used for the connection.
42
43TPgSQLServer::TPgSQLServer(const char *db, const char *uid, const char *pw)
44{
45 fPgSQL = 0;
46 fSrvInfo="";
47
48 TUrl url(db);
49
50 if (!url.IsValid()) {
51 Error("TPgSQLServer", "malformed db argument %s", db);
52 MakeZombie();
53 return;
54 }
55
56 if (strncmp(url.GetProtocol(), "pgsql", 5)) {
57 Error("TPgSQLServer", "protocol in db argument should be pgsql it is %s",
58 url.GetProtocol());
59 MakeZombie();
60 return;
61 }
62
63 const char *dbase = url.GetFile();
64
65 if (url.GetPort()) {
66 TString port;
67 port += url.GetPort();
68 fPgSQL = PQsetdbLogin(url.GetHost(), port, 0, 0, dbase, uid, pw);
69 } else {
70 fPgSQL = PQsetdbLogin(url.GetHost(), 0, 0, 0, dbase, uid, pw);
71 }
72
73 if (PQstatus(fPgSQL) != CONNECTION_BAD) {
74 fType = "PgSQL";
75 fHost = url.GetHost();
76 fDB = dbase;
77 fPort = url.GetPort();
78
79 // Populate server-info
80 fSrvInfo = "postgres ";
81 static const char *sql = "select setting from pg_settings where name='server_version'";
82 PGresult *res = PQexec(fPgSQL, sql);
83 int stat = PQresultStatus(res);
84 if (stat == PGRES_TUPLES_OK && PQntuples(res)) {
85 char *vers = PQgetvalue(res,0,0);
86 fSrvInfo += vers;
87 PQclear(res);
88 } else {
89 fSrvInfo += "unknown version number";
90 }
91 } else {
92 Error("TPgSQLServer", "connection to %s failed", url.GetHost());
93 MakeZombie();
94 }
95}
96
97////////////////////////////////////////////////////////////////////////////////
98/// Close connection to PgSQL DB server.
99
101{
102 if (IsConnected())
103 Close();
104}
105
106////////////////////////////////////////////////////////////////////////////////
107/// Close connection to PgSQL DB server.
108
110{
111 if (!fPgSQL)
112 return;
113
114 PQfinish(fPgSQL);
115 fPort = -1;
116}
117
118////////////////////////////////////////////////////////////////////////////////
119/// Execute SQL command. Result object must be deleted by the user.
120/// Returns a pointer to a TSQLResult object if successful, 0 otherwise.
121/// The result object must be deleted by the user.
122
124{
125 if (!IsConnected()) {
126 Error("Query", "not connected");
127 return 0;
128 }
129
130 PGresult *res = PQexec(fPgSQL, sql);
131 //cout << " Query called " << sql << ":" << PQntuples(res) << endl;
132
133 if ((PQresultStatus(res) != PGRES_COMMAND_OK) &&
134 (PQresultStatus(res) != PGRES_TUPLES_OK)) {
135 Error("Query", "%s",PQresultErrorMessage(res));
136 PQclear(res);
137 return 0;
138 }
139
140 return new TPgSQLResult(res);
141}
142
143////////////////////////////////////////////////////////////////////////////////
144/// Select a database. Returns 0 if successful, non-zero otherwise.
145
147{
148 TString usr;
149 TString pwd;
150 TString port;
151 TString opts;
152
153 if (!IsConnected()) {
154 Error("SelectDataBase", "not connected");
155 return -1;
156 }
157
158 if (dbname == fDB) {
159 return 0;
160 } else {
161 usr = PQuser(fPgSQL);
162 pwd = PQpass(fPgSQL);
163 port = PQport(fPgSQL);
164 opts = PQoptions(fPgSQL);
165
166 Close();
167 fPgSQL = PQsetdbLogin(fHost.Data(), port.Data(),
168 opts.Data(), 0, dbname,
169 usr.Data(), pwd.Data());
170
171 if (PQstatus(fPgSQL) == CONNECTION_OK) {
172 fDB=dbname;
173 fPort=port.Atoi();
174 } else {
175 Error("SelectDataBase", "%s",PQerrorMessage(fPgSQL));
176 return -1;
177 }
178 }
179 return 0;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183/// List all available databases. Wild is for wildcarding "t%" list all
184/// databases starting with "t".
185/// Returns a pointer to a TSQLResult object if successful, 0 otherwise.
186/// The result object must be deleted by the user.
187
189{
190 if (!IsConnected()) {
191 Error("GetDataBases", "not connected");
192 return 0;
193 }
194
195 TString sql = "SELECT pg_database.datname FROM pg_database";
196 if (wild)
197 sql += Form(" WHERE pg_database.datname LIKE '%s'", wild);
198
199 return Query(sql);
200}
201
202////////////////////////////////////////////////////////////////////////////////
203/// List all tables in the specified database. Wild is for wildcarding
204/// "t%" list all tables starting with "t".
205/// Returns a pointer to a TSQLResult object if successful, 0 otherwise.
206/// The result object must be deleted by the user.
207
208TSQLResult *TPgSQLServer::GetTables(const char *dbname, const char *wild)
209{
210 if (!IsConnected()) {
211 Error("GetTables", "not connected");
212 return 0;
213 }
214
215 if (SelectDataBase(dbname) != 0) {
216 Error("GetTables", "no such database %s", dbname);
217 return 0;
218 }
219
220 TString sql = "SELECT relname FROM pg_class where relkind='r'";
221 if (wild)
222 sql += Form(" AND relname LIKE '%s'", wild);
223
224 return Query(sql);
225}
226
227////////////////////////////////////////////////////////////////////////////////
228/// List all columns in specified table in the specified database.
229/// Wild is for wildcarding "t%" list all columns starting with "t".
230/// Returns a pointer to a TSQLResult object if successful, 0 otherwise.
231/// The result object must be deleted by the user.
232
233TSQLResult *TPgSQLServer::GetColumns(const char *dbname, const char *table,
234 const char *wild)
235{
236 if (!IsConnected()) {
237 Error("GetColumns", "not connected");
238 return 0;
239 }
240
241 if (SelectDataBase(dbname) != 0) {
242 Error("GetColumns", "no such database %s", dbname);
243 return 0;
244 }
245
246 char *sql;
247 if (wild)
248 sql = Form("select a.attname,t.typname,a.attnotnull \
249 from pg_attribute a, pg_class c, pg_type t \
250 where c.oid=a.attrelid and c.relname='%s' and \
251 a.atttypid=t.oid and a.attnum>0 \
252 and a.attname like '%s' order by a.attnum ", table,wild);
253 else
254 sql = Form("select a.attname,t.typname,a.attnotnull \
255 from pg_attribute a, pg_class c, pg_type t \
256 where c.oid=a.attrelid and c.relname='%s' and \
257 a.atttypid=t.oid and a.attnum>0 order by a.attnum",table);
258
259 return Query(sql);
260}
261
262////////////////////////////////////////////////////////////////////////////////
263/// Create a database. Returns 0 if successful, non-zero otherwise.
264
266{
267 if (!IsConnected()) {
268 Error("CreateDataBase", "not connected");
269 return -1;
270 }
271 char *sql;
272 sql = Form("CREATE DATABASE %s", dbname);
273 PGresult *res = PQexec(fPgSQL, sql);
274 PQclear(res);
275 return 0;
276}
277
278////////////////////////////////////////////////////////////////////////////////
279/// Drop (i.e. delete) a database. Returns 0 if successful, non-zero
280/// otherwise.
281
283{
284 if (!IsConnected()) {
285 Error("DropDataBase", "not connected");
286 return -1;
287 }
288 char *sql;
289 sql = Form("DROP DATABASE %s", dbname);
290 PGresult *res = PQexec(fPgSQL, sql);
291 PQclear(res);
292 return 0;
293}
294
295////////////////////////////////////////////////////////////////////////////////
296/// Reload permission tables. Returns 0 if successful, non-zero
297/// otherwise. User must have reload permissions.
298
300{
301 if (!IsConnected()) {
302 Error("Reload", "not connected");
303 return -1;
304 }
305
306 Error("Reload", "not implemented");
307 return 0;
308}
309
310////////////////////////////////////////////////////////////////////////////////
311/// Shutdown the database server. Returns 0 if successful, non-zero
312/// otherwise. User must have shutdown permissions.
313
315{
316 if (!IsConnected()) {
317 Error("Shutdown", "not connected");
318 return -1;
319 }
320
321 Error("Shutdown", "not implemented");
322 return 0;
323}
324
325////////////////////////////////////////////////////////////////////////////////
326/// Return server info.
327
329{
330 if (!IsConnected()) {
331 Error("ServerInfo", "not connected");
332 return 0;
333 }
334
335 return fSrvInfo.Data();
336}
337
338////////////////////////////////////////////////////////////////////////////////
339/// PG_VERSION_NUM conveniently only started being #defined at 8.2.3
340/// which is the first version of libpq which explicitly supports prepared
341/// statements
342
344{
345#ifdef PG_VERSION_NUM
346 return kTRUE;
347#else
348 return kFALSE;
349#endif
350}
351
352////////////////////////////////////////////////////////////////////////////////
353/// Produce TPgSQLStatement.
354
355#ifdef PG_VERSION_NUM
357#else
359#endif
360{
361#ifdef PG_VERSION_NUM
362 if (!sql || !*sql) {
363 SetError(-1, "no query string specified","Statement");
364 return 0;
365 }
366
367 PgSQL_Stmt_t *stmt = new PgSQL_Stmt_t;
368 if (!stmt){
369 SetError(-1, "cannot allocate PgSQL_Stmt_t", "Statement");
370 return 0;
371 }
372 stmt->fConn = fPgSQL;
373 stmt->fRes = PQprepare(fPgSQL, "preparedstmt", sql, 0, (const Oid*)0);
374
375 ExecStatusType stat = PQresultStatus(stmt->fRes);
376 if (pgsql_success(stat)) {
377 fErrorOut = stat;
378 return new TPgSQLStatement(stmt, fErrorOut);
379 } else {
380 SetError(stat, PQresultErrorMessage(stmt->fRes), "Statement");
381 stmt->fConn = 0;
382 delete stmt;
383 return 0;
384 }
385#else
386 Error("Statement", "not implemented for pgsql < 8.2");
387#endif
388 return 0;
389}
390
391////////////////////////////////////////////////////////////////////////////////
392/// Produce TSQLTableInfo.
393
395{
396 if (!IsConnected()) {
397 Error("GetColumns", "not connected");
398 return NULL;
399 }
400
401 // Check table name
402 if ((tablename==0) || (*tablename==0)) return 0;
403 // Query first row ( works same way as MySQL)
404 PGresult *res = PQexec(fPgSQL, TString::Format("SELECT * FROM %s LIMIT 1;", tablename));
405
406 if ((PQresultStatus(res) != PGRES_COMMAND_OK) &&
407 (PQresultStatus(res) != PGRES_TUPLES_OK)) {
408 Error("Query", "%s",PQresultErrorMessage(res));
409 PQclear(res);
410 return 0;
411 }
412
413 if (fOidTypNameMap.empty()) {
414 // Oid-TypNameMap empty, populate it, stays valid at least for connection
415 // lifetime.
416 PGresult *res_type = PQexec(fPgSQL, "SELECT OID, TYPNAME FROM PG_TYPE;");
417
418 if ((PQresultStatus(res_type) != PGRES_COMMAND_OK) &&
419 (PQresultStatus(res_type) != PGRES_TUPLES_OK)) {
420 Error("Query", "%s", PQresultErrorMessage(res_type));
421 PQclear(res);
422 PQclear(res_type);
423 return 0;
424 }
425
426 Int_t nOids = PQntuples(res_type);
427 for (Int_t oid=0; oid<nOids; oid++) {
428 Int_t tOid;
429 char* oidString = PQgetvalue(res_type, oid, 0);
430 char* typeString = PQgetvalue(res_type, oid, 1);
431 if (sscanf(oidString, "%10d", &tOid) != 1) {
432 Error("GetTableInfo", "Bad non-numeric oid '%s' for type '%s'", oidString, typeString);
433 }
434 fOidTypNameMap[tOid]=std::string(typeString);
435 }
436 PQclear(res_type);
437 }
438
439 TList * lst = NULL;
440
441 Int_t nfields = PQnfields(res);
442
443 for (Int_t col=0;col<nfields;col++){
444 Int_t sqltype = kSQL_NONE;
445 Int_t data_size = -1; // size in bytes
446 Int_t data_length = -1; // declaration like VARCHAR(n) or NUMERIC(n)
447 Int_t data_scale = -1; // second argument in declaration
448 Int_t data_sign = -1; // signed type or not
449 Bool_t nullable = 0;
450
451 const char* column_name = PQfname(res,col);
452 const char* type_name;
453 int imod = PQfmod(res,col);
454 //int isize = PQfsize(res,col);
455
456 int oid_code = PQftype(res,col);
457
458 // Search for oid in map
459 std::map<Int_t,std::string>::iterator lookupOid = fOidTypNameMap.find(oid_code);
460 if (lookupOid == fOidTypNameMap.end()) {
461 // Not found.
462 //default
463 sqltype = kSQL_NUMERIC;
464 type_name = "NUMERIC";
465 data_size=-1;
466 } else if (lookupOid->second == "int2"){
467 sqltype = kSQL_INTEGER;
468 type_name = "INT";
469 data_size=2;
470 } else if (lookupOid->second == "int4"){
471 sqltype = kSQL_INTEGER;
472 type_name = "INT";
473 data_size=4;
474 } else if (lookupOid->second == "int8"){
475 sqltype = kSQL_INTEGER;
476 type_name = "INT";
477 data_size=8;
478 } else if (lookupOid->second == "float4"){
479 sqltype = kSQL_FLOAT;
480 type_name = "FLOAT";
481 data_size=4;
482 } else if (lookupOid->second == "float8"){
483 sqltype = kSQL_DOUBLE;
484 type_name = "DOUBLE";
485 data_size=8;
486 } else if (lookupOid->second == "bool"){
487 sqltype = kSQL_INTEGER;
488 type_name = "INT";
489 data_size=1;
490 } else if (lookupOid->second == "char"){
491 sqltype = kSQL_CHAR;
492 type_name = "CHAR";
493 data_size=1;
494 } else if (lookupOid->second == "varchar"){
495 sqltype = kSQL_VARCHAR;
496 type_name = "VARCHAR";
497 data_size=imod;
498 } else if (lookupOid->second == "text"){
499 sqltype = kSQL_VARCHAR;
500 type_name = "VARCHAR";
501 data_size=imod;
502 } else if (lookupOid->second == "name"){
503 sqltype = kSQL_VARCHAR;
504 type_name = "VARCHAR";
505 data_size=imod;
506 } else if (lookupOid->second == "date"){
507 sqltype = kSQL_TIMESTAMP;
508 type_name = "TIMESTAMP";
509 data_size=8;
510 } else if (lookupOid->second == "time"){
511 sqltype = kSQL_TIMESTAMP;
512 type_name = "TIMESTAMP";
513 data_size=8;
514 } else if (lookupOid->second == "timetz"){
515 sqltype = kSQL_TIMESTAMP;
516 type_name = "TIMESTAMP";
517 data_size=8;
518 } else if (lookupOid->second == "timestamp"){
519 sqltype = kSQL_TIMESTAMP;
520 type_name = "TIMESTAMP";
521 data_size=8;
522 } else if (lookupOid->second == "timestamptz"){
523 sqltype = kSQL_TIMESTAMP;
524 type_name = "TIMESTAMP";
525 data_size=8;
526 } else if (lookupOid->second == "interval"){
527 sqltype = kSQL_TIMESTAMP;
528 type_name = "TIMESTAMP";
529 data_size=8;
530 } else if (lookupOid->second == "bytea"){
531 sqltype = kSQL_BINARY;
532 type_name = "BINARY";
533 data_size=-1;
534 } else if (lookupOid->second == ""){
535 sqltype = kSQL_NONE;
536 type_name = "UNKNOWN";
537 data_size=-1;
538 } else{
539 //default
540 sqltype = kSQL_NUMERIC;
541 type_name = "NUMERIC";
542 data_size=-1;
543 }
544
545 if (!lst) {
546 lst = new TList;
547 }
548
549 lst->Add(new TSQLColumnInfo(column_name,
550 type_name,
551 nullable,
552 sqltype,
553 data_size,
554 data_length,
555 data_scale,
556 data_sign));
557 } //! ( cols)
558
559 PQclear(res);
560 return (new TSQLTableInfo(tablename,lst));
561}
int Int_t
Definition: RtypesCore.h:43
const Bool_t kFALSE
Definition: RtypesCore.h:90
const Bool_t kTRUE
Definition: RtypesCore.h:89
const char Option_t
Definition: RtypesCore.h:64
#define ClassImp(name)
Definition: Rtypes.h:361
void Error(const char *location, const char *msgfmt,...)
#define pgsql_success(x)
TSQLServer * ROOT_Plugin_TPgSQLServer(const char *db, const char *uid, const char *pw)
PluginManager generator function.
char * Form(const char *fmt,...)
A doubly linked list.
Definition: TList.h:44
virtual void Add(TObject *obj)
Definition: TList.h:87
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:891
void MakeZombie()
Definition: TObject.h:49
TSQLResult * GetTables(const char *dbname, const char *wild=nullptr) final
List all tables in the specified database.
Int_t CreateDataBase(const char *dbname) final
Create a database. Returns 0 if successful, non-zero otherwise.
TSQLResult * Query(const char *sql) final
Execute SQL command.
const char * ServerInfo() final
Return server info.
Int_t SelectDataBase(const char *dbname) final
Select a database. Returns 0 if successful, non-zero otherwise.
Bool_t HasStatement() const final
PG_VERSION_NUM conveniently only started being #defined at 8.2.3 which is the first version of libpq ...
TSQLTableInfo * GetTableInfo(const char *tablename) final
Produce TSQLTableInfo.
std::map< Int_t, std::string > fOidTypNameMap
Definition: TPgSQLServer.h:27
Int_t Reload() final
Reload permission tables.
Int_t Shutdown() final
Shutdown the database server.
Int_t DropDataBase(const char *dbname) final
Drop (i.e.
TSQLResult * GetDataBases(const char *wild=nullptr) final
List all available databases.
PGconn * fPgSQL
Definition: TPgSQLServer.h:25
void Close(Option_t *opt="") final
Close connection to PgSQL DB server.
TPgSQLServer(const char *db, const char *uid, const char *pw)
Open a connection to a PgSQL DB server.
TString fSrvInfo
Definition: TPgSQLServer.h:26
TSQLStatement * Statement(const char *sql, Int_t=100) final
Produce TPgSQLStatement.
~TPgSQLServer()
Close connection to PgSQL DB server.
TSQLResult * GetColumns(const char *dbname, const char *table, const char *wild=nullptr) final
List all columns in specified table in the specified database.
TString fHost
Definition: TSQLServer.h:45
Int_t fPort
Definition: TSQLServer.h:47
TString fDB
Definition: TSQLServer.h:46
virtual Bool_t IsConnected() const
Definition: TSQLServer.h:93
TString fType
Definition: TSQLServer.h:44
@ kSQL_VARCHAR
Definition: TSQLServer.h:63
@ kSQL_TIMESTAMP
Definition: TSQLServer.h:69
@ kSQL_INTEGER
Definition: TSQLServer.h:64
@ kSQL_NUMERIC
Definition: TSQLServer.h:67
Basic string class.
Definition: TString.h:131
Int_t Atoi() const
Return integer value of string.
Definition: TString.cxx:1921
const char * Data() const
Definition: TString.h:364
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition: TString.cxx:2311
This class represents a WWW compatible URL.
Definition: TUrl.h:35
const char * GetFile() const
Definition: TUrl.h:71
Bool_t IsValid() const
Definition: TUrl.h:81
const char * GetHost() const
Definition: TUrl.h:69
const char * GetProtocol() const
Definition: TUrl.h:66
Int_t GetPort() const
Definition: TUrl.h:80
PGconn * fConn
PGresult * fRes