~ubuntu-branches/ubuntu/utopic/pgadmin3/utopic-proposed

« back to all changes in this revision

Viewing changes to src/db/pgConn.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Raphael Enrici
  • Date: 2004-12-14 23:46:39 UTC
  • Revision ID: james.westby@ubuntu.com-20041214234639-tve0i5l49fq13jli
Tags: upstream-1.2.0
ImportĀ upstreamĀ versionĀ 1.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//////////////////////////////////////////////////////////////////////////
 
2
//
 
3
// pgAdmin III - PostgreSQL Tools
 
4
// RCS-ID:      $Id: pgConn.cpp,v 1.60 2004/11/21 14:19:41 andreas Exp $
 
5
// Copyright (C) 2002 - 2004, The pgAdmin Development Team
 
6
// This software is released under the Artistic Licence
 
7
//
 
8
// pgConn.cpp - PostgreSQL Connection class
 
9
//
 
10
/////////////////////////////////////////////////////////////////////////
 
11
 
 
12
#include "pgAdmin3.h"
 
13
 
 
14
// wxWindows headers
 
15
#include <wx/wx.h>
 
16
 
 
17
// PostgreSQL headers
 
18
#include <libpq-fe.h>
 
19
#include "pgfeatures.h"
 
20
 
 
21
// Network  headers
 
22
#ifdef __WXMSW__
 
23
#include <winsock.h>
 
24
#else
 
25
 
 
26
#include <arpa/inet.h>
 
27
#include <netdb.h>
 
28
#include <netinet/in.h>
 
29
 
 
30
#ifndef INADDR_NONE
 
31
#define INADDR_NONE (-1)
 
32
#endif
 
33
 
 
34
#endif
 
35
 
 
36
// App headers
 
37
#include "pgConn.h"
 
38
#include "misc.h"
 
39
#include "pgSet.h"
 
40
#include "sysLogger.h"
 
41
 
 
42
 
 
43
extern double libpqVersion;
 
44
 
 
45
static void pgNoticeProcessor(void *arg, const char *message)
 
46
{
 
47
    ((pgConn*)arg)->Notice(message);
 
48
}
 
49
 
 
50
 
 
51
pgConn::pgConn(const wxString& server, const wxString& database, const wxString& username, const wxString& password, int port, int sslmode, OID oid)
 
52
{
 
53
    wxLogInfo(wxT("Creating pgConn object"));
 
54
    wxString msg, hostip;
 
55
 
 
56
    conv = &wxConvLibc;
 
57
    needColQuoting = false;
 
58
 
 
59
    // Check the hostname/ipaddress
 
60
    struct hostent *host;
 
61
    unsigned long addr;
 
62
    conn=0;
 
63
    majorVersion=0;
 
64
    noticeArg=0;
 
65
    connStatus = PGCONN_BAD;
 
66
    memset(features, 0, sizeof(features));
 
67
    
 
68
#ifdef __WXMSW__
 
69
    struct in_addr ipaddr;
 
70
#else
 
71
    unsigned long ipaddr;
 
72
#endif
 
73
    
 
74
    
 
75
    addr = inet_addr(server.ToAscii());
 
76
        if (addr == INADDR_NONE) // szServer is not an IP address
 
77
        {
 
78
                host = gethostbyname(server.ToAscii());
 
79
                if (host == NULL)
 
80
                {
 
81
            connStatus = PGCONN_DNSERR;
 
82
            wxLogError(__("Could not resolve hostname %s"), server.c_str());
 
83
                        return;
 
84
                }
 
85
 
 
86
        memcpy(&(ipaddr),host->h_addr,host->h_length); 
 
87
            hostip = wxString::FromAscii(inet_ntoa(*((struct in_addr*) host->h_addr_list[0])));
 
88
 
 
89
    }
 
90
    else
 
91
        hostip = server;
 
92
 
 
93
    wxLogInfo(wxT("Server name: %s (resolved to: %s)"), server.c_str(), hostip.c_str());
 
94
 
 
95
    // Create the connection string
 
96
    wxString connstr;
 
97
    if (!server.IsEmpty()) {
 
98
      connstr.Append(wxT(" hostaddr="));
 
99
      connstr.Append(qtString(hostip));
 
100
    }
 
101
    if (!database.IsEmpty()) {
 
102
      connstr.Append(wxT(" dbname="));
 
103
      connstr.Append(qtString(database));
 
104
    }
 
105
    if (!username.IsEmpty()) {
 
106
      connstr.Append(wxT(" user="));
 
107
      connstr.Append(qtString(username));
 
108
    }
 
109
    if (!password.IsEmpty()) {
 
110
      connstr.Append(wxT(" password="));
 
111
      connstr.Append(qtString(password));
 
112
    }
 
113
    if (port > 0) {
 
114
      connstr.Append(wxT(" port="));
 
115
      connstr.Append(NumToStr((long)port));
 
116
    }
 
117
 
 
118
    if (libpqVersion > 7.3)
 
119
    {
 
120
        switch (sslmode)
 
121
        {
 
122
            case 1: connstr.Append(wxT(" sslmode=require"));   break;
 
123
            case 2: connstr.Append(wxT(" sslmode=prefer"));    break;
 
124
            case 3: connstr.Append(wxT(" sslmode=allow"));     break;
 
125
            case 4: connstr.Append(wxT(" sslmode=disable"));   break;
 
126
        }
 
127
    }
 
128
    else
 
129
    {
 
130
        switch (sslmode)
 
131
        {
 
132
            case 1: connstr.Append(wxT(" requiressl=1"));   break;
 
133
            case 2: connstr.Append(wxT(" requiressl=0"));   break;
 
134
        }
 
135
    }
 
136
    connstr.Trim(FALSE);
 
137
 
 
138
    // Open the connection
 
139
    wxLogInfo(wxT("Opening connection with connection string: %s"), connstr.c_str());
 
140
 
 
141
#if wxUSE_UNICODE
 
142
    conn = PQconnectdb(connstr.mb_str(wxConvUTF8));
 
143
    if (PQstatus(conn) != CONNECTION_OK)
 
144
    {
 
145
        PQfinish(conn);
 
146
        conn = PQconnectdb(connstr.mb_str(wxConvLibc));
 
147
    }
 
148
#else
 
149
    conn = PQconnectdb(connstr.ToAscii());
 
150
#endif
 
151
 
 
152
    dbHost = server;
 
153
 
 
154
    // Set client encoding to Unicode/Ascii
 
155
    if (PQstatus(conn) == CONNECTION_OK)
 
156
    {
 
157
        connStatus = PGCONN_OK;
 
158
        PQsetNoticeProcessor(conn, pgNoticeProcessor, this);
 
159
 
 
160
 
 
161
        wxString sql=wxT("SELECT oid, pg_encoding_to_char(encoding) AS encoding, datlastsysoid\n")
 
162
                      wxT("  FROM pg_database WHERE ");
 
163
        if (oid)
 
164
            sql += wxT("oid = ") + NumToStr(oid);
 
165
        else
 
166
            sql += wxT("datname=") + qtString(database);
 
167
 
 
168
        pgSet *set = ExecuteSet(sql);
 
169
 
 
170
 
 
171
        if (set)
 
172
        {
 
173
            if (set->ColNumber(wxT("\"datlastsysoid\"")) >= 0)
 
174
                needColQuoting = true;
 
175
 
 
176
            lastSystemOID = set->GetOid(wxT("datlastsysoid"));
 
177
            dbOid = set->GetOid(wxT("oid"));
 
178
            wxString encoding = set->GetVal(wxT("encoding"));
 
179
 
 
180
#if wxUSE_UNICODE
 
181
            if (encoding != wxT("SQL_ASCII") && encoding != wxT("MULE_INTERNAL"))
 
182
            {
 
183
                encoding = wxT("UNICODE");
 
184
                conv = &wxConvUTF8;
 
185
            }
 
186
            else
 
187
                conv = &wxConvLibc;
 
188
#endif
 
189
 
 
190
            wxLogInfo(wxT("Setting client_encoding to '%s'"), encoding.c_str());
 
191
            if (PQsetClientEncoding(conn, encoding.ToAscii()))
 
192
                                wxLogError(wxT("%s"), GetLastError().c_str());
 
193
 
 
194
            delete set;
 
195
        }
 
196
    }
 
197
}
 
198
 
 
199
 
 
200
pgConn::~pgConn()
 
201
{
 
202
    wxLogInfo(wxT("Destroying pgConn object"));
 
203
    Close();
 
204
}
 
205
 
 
206
 
 
207
 
 
208
void pgConn::Close()
 
209
{
 
210
    if (conn)
 
211
        PQfinish(conn);
 
212
    conn=0;
 
213
    connStatus=PGCONN_BAD;
 
214
}
 
215
 
 
216
 
 
217
#ifdef SSL
 
218
// we don't define USE_SSL so we don't get ssl.h included
 
219
extern "C"
 
220
{
 
221
extern void *PQgetssl(PGconn *conn);
 
222
}
 
223
 
 
224
bool pgConn::IsSSLconnected()
 
225
{
 
226
    return (conn && PQstatus(conn) == CONNECTION_OK && PQgetssl(conn) != NULL);
 
227
}
 
228
#endif
 
229
 
 
230
 
 
231
void pgConn::RegisterNoticeProcessor(PQnoticeProcessor proc, void *arg)
 
232
{
 
233
    noticeArg=arg;
 
234
    noticeProc=proc;
 
235
}
 
236
 
 
237
 
 
238
wxString pgConn::SystemNamespaceRestriction(const wxString &nsp)
 
239
{
 
240
    return wxT("(") + nsp + wxT(" NOT LIKE 'pg\\_%' AND ") + nsp + wxT(" NOT LIKE 'information_schema')");
 
241
}
 
242
 
 
243
 
 
244
void pgConn::Notice(const char *msg)
 
245
{
 
246
    wxString str(msg, *conv);
 
247
    wxLogNotice(wxT("%s"), str.c_str());
 
248
 
 
249
    if (noticeArg && noticeProc)
 
250
        (*noticeProc)(noticeArg, msg);
 
251
}
 
252
 
 
253
 
 
254
//////////////////////////////////////////////////////////////////////////
 
255
// Execute SQL
 
256
//////////////////////////////////////////////////////////////////////////
 
257
 
 
258
bool pgConn::ExecuteVoid(const wxString& sql)
 
259
{
 
260
    if (GetStatus() != PGCONN_OK)
 
261
        return false;
 
262
 
 
263
    // Execute the query and get the status.
 
264
    PGresult *qryRes;
 
265
 
 
266
    wxLogSql(wxT("Void query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
 
267
    qryRes = PQexec(conn, sql.mb_str(*conv));
 
268
    lastResultStatus = PQresultStatus(qryRes);
 
269
 
 
270
    // Check for errors
 
271
    if (lastResultStatus != PGRES_TUPLES_OK &&
 
272
        lastResultStatus != PGRES_COMMAND_OK)
 
273
    {
 
274
        LogError();
 
275
        return false;
 
276
    }
 
277
 
 
278
    // Cleanup & exit
 
279
    PQclear(qryRes);
 
280
    return  true;
 
281
}
 
282
 
 
283
 
 
284
bool pgConn::HasPrivilege(const wxString &objTyp, const wxString &objName, const wxString &priv)
 
285
{
 
286
    wxString res=ExecuteScalar(
 
287
        wxT("SELECT has_") + objTyp.Lower() 
 
288
        + wxT("_privilege(") + qtString(objName)
 
289
        + wxT(", ") + qtString(priv) + wxT(")"));
 
290
 
 
291
    return StrToBool(res);
 
292
}
 
293
 
 
294
 
 
295
wxString pgConn::ExecuteScalar(const wxString& sql)
 
296
{
 
297
    wxString result;
 
298
 
 
299
    if (GetStatus() == PGCONN_OK)
 
300
    {
 
301
        // Execute the query and get the status.
 
302
        PGresult *qryRes;
 
303
        wxLogSql(wxT("Scalar query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
 
304
        qryRes = PQexec(conn, sql.mb_str(*conv));
 
305
        lastResultStatus = PQresultStatus(qryRes);
 
306
        
 
307
        // Check for errors
 
308
        if (lastResultStatus != PGRES_TUPLES_OK)
 
309
        {
 
310
            LogError();
 
311
            PQclear(qryRes);
 
312
            return wxEmptyString;
 
313
        }
 
314
 
 
315
            // Check for a returned row
 
316
        if (PQntuples(qryRes) < 1)
 
317
        {
 
318
                    wxLogInfo(wxT("Query returned no tuples"));
 
319
            PQclear(qryRes);
 
320
            return wxEmptyString;
 
321
            }
 
322
            
 
323
            // Retrieve the query result and return it.
 
324
        result=wxString(PQgetvalue(qryRes, 0, 0), *conv);
 
325
 
 
326
        wxLogSql(wxT("Query result: %s"), result.c_str());
 
327
 
 
328
        // Cleanup & exit
 
329
        PQclear(qryRes);
 
330
    }
 
331
 
 
332
    return result;
 
333
}
 
334
 
 
335
pgSet *pgConn::ExecuteSet(const wxString& sql)
 
336
{
 
337
    // Execute the query and get the status.
 
338
    if (GetStatus() == PGCONN_OK)
 
339
    {
 
340
        PGresult *qryRes;
 
341
        wxLogSql(wxT("Set query (%s:%d): %s"), this->GetHost().c_str(), this->GetPort(), sql.c_str());
 
342
        qryRes = PQexec(conn, sql.mb_str(*conv));
 
343
 
 
344
        lastResultStatus= PQresultStatus(qryRes);
 
345
 
 
346
        if (lastResultStatus == PGRES_TUPLES_OK || lastResultStatus == PGRES_COMMAND_OK)
 
347
        {
 
348
            pgSet *set = new pgSet(qryRes, this, *conv, needColQuoting);
 
349
            if (!set)
 
350
            {
 
351
                wxLogError(__("Couldn't create a pgSet object!"));
 
352
                PQclear(qryRes);
 
353
            }
 
354
            return set;
 
355
        }
 
356
        else
 
357
        {
 
358
            LogError();
 
359
            PQclear(qryRes);
 
360
        }
 
361
    }
 
362
    return 0;
 
363
}
 
364
 
 
365
//////////////////////////////////////////////////////////////////////////
 
366
// Info
 
367
//////////////////////////////////////////////////////////////////////////
 
368
 
 
369
wxString pgConn::GetLastError() const
 
370
 
371
    wxString errmsg;
 
372
        char *pqErr;
 
373
    if (conn && (pqErr = PQerrorMessage(conn)) != 0)
 
374
        errmsg=wxString(pqErr, wxConvUTF8);
 
375
    else
 
376
    {
 
377
        if (connStatus == PGCONN_BROKEN)
 
378
            errmsg = _("Connection to database broken.");
 
379
        else
 
380
            errmsg = _("No connection to database.");
 
381
    }
 
382
    return errmsg;
 
383
}
 
384
 
 
385
 
 
386
 
 
387
void pgConn::LogError()
 
388
{
 
389
    if (conn)
 
390
    {
 
391
        wxLogError(wxT("%s"), GetLastError().c_str());
 
392
 
 
393
        IsAlive();
 
394
#if 0
 
395
        ConnStatusType status = PQstatus(conn);
 
396
        if (status == CONNECTION_BAD)
 
397
        {
 
398
            PQfinish(conn);
 
399
            conn=0;
 
400
            connStatus = PGCONN_BROKEN;
 
401
        }
 
402
#endif
 
403
    }
 
404
}
 
405
 
 
406
 
 
407
 
 
408
bool pgConn::IsAlive()
 
409
{
 
410
    if (GetStatus() != PGCONN_OK)
 
411
        return false;
 
412
 
 
413
    PGresult *qryRes = PQexec(conn, "SELECT 1;");
 
414
    lastResultStatus = PQresultStatus(qryRes);
 
415
    PQclear(qryRes);
 
416
 
 
417
    // Check for errors
 
418
    if (lastResultStatus != PGRES_TUPLES_OK)
 
419
    {
 
420
        PQfinish(conn);
 
421
        conn=0;
 
422
        connStatus = PGCONN_BROKEN;
 
423
        return false;
 
424
    }
 
425
 
 
426
    return true;
 
427
}
 
428
 
 
429
 
 
430
int pgConn::GetStatus() const
 
431
{
 
432
    if (conn)
 
433
        ((pgConn*)this)->connStatus = PQstatus(conn);
 
434
 
 
435
    return connStatus;
 
436
}
 
437
 
 
438
 
 
439
wxString pgConn::GetVersionString()
 
440
{
 
441
        return ExecuteScalar(wxT("SELECT version();"));
 
442
}
 
443
 
 
444
 
 
445
bool pgConn::BackendMinimumVersion(int major, int minor)
 
446
{
 
447
    if (!majorVersion)
 
448
    {
 
449
            sscanf(GetVersionString().ToAscii(), "%*s %d.%d", &majorVersion, &minorVersion);
 
450
    }
 
451
        return majorVersion > major || (majorVersion == major && minorVersion >= minor);
 
452
}
 
453
 
 
454
 
 
455
bool pgConn::HasFeature(int featureNo)
 
456
{
 
457
    if (!features[FEATURE_INITIALIZED])
 
458
    {
 
459
        features[FEATURE_INITIALIZED] = true;
 
460
 
 
461
        pgSet *set=ExecuteSet(
 
462
            wxT("SELECT proname, pronargs, proargtypes[0] AS arg0, proargtypes[1] AS arg1, proargtypes[2] AS arg2\n")
 
463
            wxT("  FROM pg_proc\n")
 
464
            wxT(" WHERE proname IN ('pg_tablespace_size', 'pg_file_read', 'pg_rotate_log',")
 
465
            wxT(                  " 'pg_postmaster_starttime', 'pg_terminate_backend', 'pg_reload_conf')"));
 
466
 
 
467
        if (set)
 
468
        {
 
469
            while (!set->Eof())
 
470
            {
 
471
                wxString proname=set->GetVal(wxT("proname"));
 
472
                long pronargs = set->GetLong(wxT("pronargs"));
 
473
 
 
474
                if (proname == wxT("pg_tablespace_size") && pronargs == 1 && set->GetLong(wxT("arg0")) == 26)
 
475
                    features[FEATURE_SIZE]= true;
 
476
                else if (proname == wxT("pg_file_read") && pronargs == 3 && set->GetLong(wxT("arg0")) == 25
 
477
                    && set->GetLong(wxT("arg1")) == 20 && set->GetLong(wxT("arg2")) == 20)
 
478
                    features[FEATURE_FILEREAD] = true;
 
479
                else if (proname == wxT("pg_rotate_log") && pronargs == 0)
 
480
                    features[FEATURE_ROTATELOG] = true;
 
481
                else if (proname == wxT("pg_postmaster_starttime") && pronargs == 0)
 
482
                    features[FEATURE_POSTMASTER_STARTTIME] = true;
 
483
                else if (proname == wxT("pg_terminate_backend") && pronargs == 1 && set->GetLong(wxT("arg0")) == 23)
 
484
                    features[FEATURE_TERMINATE_BACKEND] = true;
 
485
                else if (proname == wxT("pg_reload_conf") && pronargs == 0)
 
486
                    features[FEATURE_RELOAD_CONF] = true;
 
487
 
 
488
                set->MoveNext();
 
489
            }
 
490
            delete set;
 
491
        }
 
492
    }
 
493
 
 
494
    if (featureNo <= FEATURE_INITIALIZED || featureNo >= FEATURE_LAST)
 
495
        return false;
 
496
    return features[featureNo];
 
497
}