38
38
// dbgPgConn constructors
40
40
// A dbgPgConn object encapsulates the connection to a PostgreSQL server. This
41
// class is a wrapper around a Pgconn that provides a convenient constructor
42
// and a few member functions.
41
// class is a wrapper around a Pgconn that provides a convenient constructor
42
// and a few member functions.
44
44
// The constructor creates a new thread and connects to the specified server
46
46
dbgPgConn::dbgPgConn(frmDebugger *frame, const wxString &server, const wxString &database, const wxString &userName, const wxString &password, const wxString &port, int sslmode, const wxString &applicationname)
49
Init( server, database, userName, password, port, sslmode, applicationname, true );
49
Init( server, database, userName, password, port, sslmode, applicationname, true );
52
dbgPgConn::dbgPgConn(frmDebugger *frame, const dbgConnProp & props, bool startThread )
52
dbgPgConn::dbgPgConn(frmDebugger *frame, const dbgConnProp &props, bool startThread )
55
Init( props.m_host, props.m_database, props.m_userName, props.m_password, props.m_port, props.m_sslMode, props.m_applicationName, startThread );
55
Init( props.m_host, props.m_database, props.m_userName, props.m_password, props.m_port, props.m_sslMode, props.m_applicationName, startThread );
58
58
void dbgPgConn::Init( const wxString &server, const wxString &database, const wxString &username, const wxString &password, const wxString &port, int sslmode, const wxString &applicationname, bool startThread )
60
bool utfConnectString = false;
61
bool libcConnectString = false;
66
m_debuggerApiVersion = DEBUGGER_UNKNOWN_API;
70
m_workerThread = new dbgPgThread( *this );
72
m_workerThread = NULL;
77
// To keep the user interface thread responsive while we're waiting for the
78
// PostgreSQL server, we create a separate thread to interact with the
79
// database - the worker thread sleeps until the GUI thread signals that it
80
// has some work to do. When the result set arrives from the server, the
81
// worker thread creates a DBResult event and posts it to the GUI thread's
86
m_workerThread->Create();
87
m_workerThread->Run();
90
// Figure out the hostname/IP address
93
wxString hostip, hostname;
60
bool utfConnectString = false;
61
bool libcConnectString = false;
66
m_debuggerApiVersion = DEBUGGER_UNKNOWN_API;
70
m_workerThread = new dbgPgThread( *this );
72
m_workerThread = NULL;
77
// To keep the user interface thread responsive while we're waiting for the
78
// PostgreSQL server, we create a separate thread to interact with the
79
// database - the worker thread sleeps until the GUI thread signals that it
80
// has some work to do. When the result set arrives from the server, the
81
// worker thread creates a DBResult event and posts it to the GUI thread's
86
m_workerThread->Create();
87
m_workerThread->Run();
90
// Figure out the hostname/IP address
93
wxString hostip, hostname;
96
struct in_addr ipaddr;
96
struct in_addr ipaddr;
102
if (!(server.IsEmpty() || server.StartsWith(wxT("/"))))
105
addr = inet_addr(server.ToAscii());
106
if (addr == INADDR_NONE) // szServer is not an IP address
108
host = gethostbyname(server.ToAscii());
111
wxLogError(__("Could not resolve hostname %s"), server.c_str());
115
memcpy(&(ipaddr),host->h_addr,host->h_length);
116
hostip = wxString::FromAscii(inet_ntoa(*((struct in_addr*) host->h_addr_list[0])));
130
// Build up a connection string
131
wxString connectParams;
133
if(hostname.Length())
135
connectParams.Append(wxT( "host="));
136
connectParams.Append(hostname);
138
msg += delimiter + server; delimiter = _(":");
143
connectParams.Append(wxT(" hostaddr="));
144
connectParams.Append(hostip);
149
connectParams += wxT(" port=");
150
connectParams += port;
152
msg += delimiter + port; delimiter = _(":");
156
if( database.Length())
158
connectParams.Append(wxT(" dbname="));
159
connectParams.Append(qtConnString(database));
161
msg += delimiter + database; delimiter = _(":");
164
if(username.Length())
166
connectParams.Append(wxT(" user="));
167
connectParams.Append(username );
169
msg += delimiter + username; delimiter = _(":");
172
if(password.Length())
174
connectParams.Append(wxT(" password="));
175
connectParams.Append(password);
181
connectParams.Append(wxT(" sslmode=require"));
185
connectParams.Append(wxT(" sslmode=prefer"));
189
connectParams.Append(wxT(" sslmode=allow"));
193
connectParams.Append(wxT(" sslmode=disable"));
102
if (!(server.IsEmpty() || server.StartsWith(wxT("/"))))
105
addr = inet_addr(server.ToAscii());
106
if (addr == INADDR_NONE) // szServer is not an IP address
108
host = gethostbyname(server.ToAscii());
111
wxLogError(__("Could not resolve hostname %s"), server.c_str());
115
memcpy(&(ipaddr), host->h_addr, host->h_length);
116
hostip = wxString::FromAscii(inet_ntoa(*((struct in_addr *) host->h_addr_list[0])));
130
// Build up a connection string
131
wxString connectParams;
133
if(hostname.Length())
135
connectParams.Append(wxT( "host="));
136
connectParams.Append(hostname);
138
msg += delimiter + server;
144
connectParams.Append(wxT(" hostaddr="));
145
connectParams.Append(hostip);
150
connectParams += wxT(" port=");
151
connectParams += port;
153
msg += delimiter + port;
158
if(database.Length())
160
connectParams.Append(wxT(" dbname="));
161
connectParams.Append(qtConnString(database));
163
msg += delimiter + database;
167
if(username.Length())
169
connectParams.Append(wxT(" user="));
170
connectParams.Append(username );
172
msg += delimiter + username;
176
if(password.Length())
178
connectParams.Append(wxT(" password="));
179
connectParams.Append(password);
185
connectParams.Append(wxT(" sslmode=require"));
189
connectParams.Append(wxT(" sslmode=prefer"));
193
connectParams.Append(wxT(" sslmode=allow"));
197
connectParams.Append(wxT(" sslmode=disable"));
197
201
connectParams.Append(wxT(" sslmode=verify-ca"));
201
205
connectParams.Append(wxT(" sslmode=verify-full"));
208
connectParams.Trim(true);
209
connectParams.Trim(false);
212
connectParams.Trim(true);
213
connectParams.Trim(false);
211
215
#ifdef HAVE_CONNINFO_PARSE
212
if (!applicationname.IsEmpty())
214
// Check connection string with application_name
216
wxString connectParams_with_applicationname = connectParams + wxT(" application_name='") + applicationname + wxT("'");
217
if (PQconninfoParse(connectParams_with_applicationname.mb_str(wxConvUTF8), &errmsg))
219
utfConnectString = true;
220
connectParams = connectParams_with_applicationname;
222
else if (PQconninfoParse(connectParams_with_applicationname.mb_str(wxConvLibc), &errmsg))
224
libcConnectString = true;
225
connectParams = connectParams_with_applicationname;
216
if (!applicationname.IsEmpty())
218
// Check connection string with application_name
220
wxString connectParams_with_applicationname = connectParams + wxT(" application_name='") + applicationname + wxT("'");
221
if (PQconninfoParse(connectParams_with_applicationname.mb_str(wxConvUTF8), &errmsg))
223
utfConnectString = true;
224
connectParams = connectParams_with_applicationname;
226
else if (PQconninfoParse(connectParams_with_applicationname.mb_str(wxConvLibc), &errmsg))
228
libcConnectString = true;
229
connectParams = connectParams_with_applicationname;
230
m_frame->getStatusBar()->SetStatusText( wxString::Format(_( "Connecting to %s" ), msg.c_str()), 1 );
231
wxCharBuffer cstrUTF=connectParams.mb_str(wxConvUTF8);
232
wxCharBuffer cstrLibc=connectParams.mb_str(wxConvLibc);
234
if (!libcConnectString)
235
m_pgConn = PQconnectdb(cstrUTF);
237
m_pgConn = PQconnectdb(cstrLibc);
239
if (PQstatus(m_pgConn) != CONNECTION_OK)
242
m_pgConn = PQconnectdb(cstrLibc);
245
if( PQstatus( m_pgConn ) == CONNECTION_OK )
247
m_frame->getStatusBar()->SetStatusText( wxString::Format(_( "Connected to %s" ), msg.c_str()), 1 );
248
PQsetClientEncoding( m_pgConn, "UNICODE" );
252
throw( std::runtime_error( PQerrorMessage( m_pgConn )));
234
m_frame->getStatusBar()->SetStatusText( wxString::Format(_( "Connecting to %s" ), msg.c_str()), 1 );
235
wxCharBuffer cstrUTF = connectParams.mb_str(wxConvUTF8);
236
wxCharBuffer cstrLibc = connectParams.mb_str(wxConvLibc);
238
if (!libcConnectString)
239
m_pgConn = PQconnectdb(cstrUTF);
241
m_pgConn = PQconnectdb(cstrLibc);
243
if (PQstatus(m_pgConn) != CONNECTION_OK)
246
m_pgConn = PQconnectdb(cstrLibc);
249
if( PQstatus( m_pgConn ) == CONNECTION_OK )
251
m_frame->getStatusBar()->SetStatusText( wxString::Format(_( "Connected to %s" ), msg.c_str()), 1 );
252
PQsetClientEncoding( m_pgConn, "UNICODE" );
256
throw( std::runtime_error( PQerrorMessage( m_pgConn )));
256
260
dbgPgConn::~dbgPgConn()
261
265
////////////////////////////////////////////////////////////////////////////////
347
351
// Register a NOTICE handler with the libpq library - libpq will invoke the
348
352
// given handler whenever a NOTICE arrives on this connection. libpq will
349
// pass 'arg' to the handler. 'handler' is typically a static function and
350
// 'arg' is often a pointer to an object. That lets you use a regular member
351
// function as a callback (because 'arg' is mapped into a 'this' pointer by the
353
// pass 'arg' to the handler. 'handler' is typically a static function and
354
// 'arg' is often a pointer to an object. That lets you use a regular member
355
// function as a callback (because 'arg' is mapped into a 'this' pointer by the
352
356
// callback function).
354
void dbgPgConn::setNoticeHandler( PQnoticeProcessor handler, void * arg )
358
void dbgPgConn::setNoticeHandler( PQnoticeProcessor handler, void *arg )
356
PQnoticeProcessor p=NULL;
357
p = PQsetNoticeProcessor( m_pgConn, handler, arg );
360
PQnoticeProcessor p = NULL;
361
p = PQsetNoticeProcessor( m_pgConn, handler, arg );
360
364
void dbgPgConn::Close()
362
// Attempt to cancel any ongoing query
365
// Wait a tenth of a second or so for things to sort themselves out.
366
// Otherwise on Windows things can get funky here ...
371
m_workerThread->Die();
372
m_workerThread->Wait();
374
delete m_workerThread;
375
m_workerThread = NULL;
366
// Attempt to cancel any ongoing query
369
// Wait a tenth of a second or so for things to sort themselves out.
370
// Otherwise on Windows things can get funky here ...
375
m_workerThread->Die();
376
m_workerThread->Wait();
378
delete m_workerThread;
379
m_workerThread = NULL;
384
388
void dbgPgConn::Cancel()
386
// Attempt to cancel any ongoing query
389
PGcancel *cancel = PQgetCancel(m_pgConn);
391
PQcancel(cancel, errbuf, sizeof(errbuf));
392
PQfreeCancel(cancel);
390
// Attempt to cancel any ongoing query
393
PGcancel *cancel = PQgetCancel(m_pgConn);
395
PQcancel(cancel, errbuf, sizeof(errbuf));
396
PQfreeCancel(cancel);
396
400
// Check the backend version
397
401
bool dbgPgConn::BackendMinimumVersion(int major, int minor)
401
wxString version=GetVersionString();
402
sscanf(version.ToAscii(), "%*s %d.%d", &m_majorVersion, &m_minorVersion);
403
m_isEdb = version.Upper().Matches(wxT("ENTERPRISEDB*"));
405
// EnterpriseDB 8.3 beta 1 & 2 and possibly later actually have PostgreSQL 8.2 style
406
// catalogs. This is expected to change either before GA, but in the meantime we
407
// need to check the catalogue version in more detail, and if we don't see what looks
408
// like a 8.3 catalog, force the version number back to 8.2. Yuck.
409
if (m_isEdb && m_majorVersion == 8 && m_minorVersion == 3)
414
res = waitForCommand(wxT( "SELECT count(*) FROM pg_attribute WHERE attname = 'proconfig' AND attrelid = 'pg_proc'::regclass"));
416
if (PQresultStatus(res) == PGRES_TUPLES_OK)
418
// Retrieve the query result and return it.
419
result=wxString(PQgetvalue(res, 0, 0), wxConvUTF8);
424
if (result == wxT("0"))
428
m_isGreenplum = version.Upper().Matches(wxT("*GREENPLUM DATABASE*"));
431
return m_majorVersion > major || (m_majorVersion == major && m_minorVersion >= minor);
405
wxString version = GetVersionString();
406
sscanf(version.ToAscii(), "%*s %d.%d", &m_majorVersion, &m_minorVersion);
407
m_isEdb = version.Upper().Matches(wxT("ENTERPRISEDB*"));
409
// EnterpriseDB 8.3 beta 1 & 2 and possibly later actually have PostgreSQL 8.2 style
410
// catalogs. This is expected to change either before GA, but in the meantime we
411
// need to check the catalogue version in more detail, and if we don't see what looks
412
// like a 8.3 catalog, force the version number back to 8.2. Yuck.
413
if (m_isEdb && m_majorVersion == 8 && m_minorVersion == 3)
418
res = waitForCommand(wxT( "SELECT count(*) FROM pg_attribute WHERE attname = 'proconfig' AND attrelid = 'pg_proc'::regclass"));
420
if (PQresultStatus(res) == PGRES_TUPLES_OK)
422
// Retrieve the query result and return it.
423
result = wxString(PQgetvalue(res, 0, 0), wxConvUTF8);
428
if (result == wxT("0"))
432
m_isGreenplum = version.Upper().Matches(wxT("*GREENPLUM DATABASE*"));
435
return m_majorVersion > major || (m_majorVersion == major && m_minorVersion >= minor);
434
438
// Check the EDB backend version
435
439
bool dbgPgConn::EdbMinimumVersion(int major, int minor)
437
return BackendMinimumVersion(major, minor) && GetIsEdb();
441
return BackendMinimumVersion(major, minor) && GetIsEdb();
440
444
// Get the debugger API version
441
445
DebuggerApiVersions dbgPgConn::DebuggerApiVersion()
443
if (m_debuggerApiVersion > 0)
444
return m_debuggerApiVersion;
446
// The v1 protocol didn't have pldbg_get_proxy_info()
449
PGresult *res = waitForCommand(wxT( "SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_proxy_info';"));
451
if (PQresultStatus(res) == PGRES_TUPLES_OK)
453
// Retrieve the query result and return it.
454
if (wxString(PQgetvalue(res, 0, 0), wxConvUTF8) == wxT("0"))
457
m_debuggerApiVersion = DEBUGGER_V1_API;
458
return DEBUGGER_V1_API;
465
wxLogError(wxT("%s"), wxString(PQerrorMessage(m_pgConn), wxConvUTF8).c_str());
466
return DEBUGGER_UNKNOWN_API;
469
// We have pldbg_get_proxy_info, so use it to get the API version
470
res = waitForCommand(wxT( "SELECT proxyapiver FROM pldbg_get_proxy_info();"));
472
if (PQresultStatus(res) == PGRES_TUPLES_OK)
474
// Retrieve the query result and return it.
475
m_debuggerApiVersion = (DebuggerApiVersions)atoi(wxString(PQgetvalue(res, 0, 0), wxConvUTF8).ToAscii());
480
wxLogError(wxT("%s"), wxString(PQerrorMessage(m_pgConn), wxConvUTF8).c_str());
481
return DEBUGGER_UNKNOWN_API;
484
return m_debuggerApiVersion;
447
if (m_debuggerApiVersion > 0)
448
return m_debuggerApiVersion;
450
// The v1 protocol didn't have pldbg_get_proxy_info()
453
PGresult *res = waitForCommand(wxT( "SELECT count(*) FROM pg_proc WHERE proname = 'pldbg_get_proxy_info';"));
455
if (PQresultStatus(res) == PGRES_TUPLES_OK)
457
// Retrieve the query result and return it.
458
if (wxString(PQgetvalue(res, 0, 0), wxConvUTF8) == wxT("0"))
461
m_debuggerApiVersion = DEBUGGER_V1_API;
462
return DEBUGGER_V1_API;
469
wxLogError(wxT("%s"), wxString(PQerrorMessage(m_pgConn), wxConvUTF8).c_str());
470
return DEBUGGER_UNKNOWN_API;
473
// We have pldbg_get_proxy_info, so use it to get the API version
474
res = waitForCommand(wxT( "SELECT proxyapiver FROM pldbg_get_proxy_info();"));
476
if (PQresultStatus(res) == PGRES_TUPLES_OK)
478
// Retrieve the query result and return it.
479
m_debuggerApiVersion = (DebuggerApiVersions)atoi(wxString(PQgetvalue(res, 0, 0), wxConvUTF8).ToAscii());
484
wxLogError(wxT("%s"), wxString(PQerrorMessage(m_pgConn), wxConvUTF8).c_str());
485
return DEBUGGER_UNKNOWN_API;
488
return m_debuggerApiVersion;
488
492
wxString dbgPgConn::GetVersionString()
493
res = waitForCommand(wxT( "SELECT version();"));
495
if (PQresultStatus(res) == PGRES_TUPLES_OK)
497
// Retrieve the query result and return it.
498
result=wxString(PQgetvalue(res, 0, 0), wxConvUTF8);
497
res = waitForCommand(wxT( "SELECT version();"));
499
if (PQresultStatus(res) == PGRES_TUPLES_OK)
501
// Retrieve the query result and return it.
502
result = wxString(PQgetvalue(res, 0, 0), wxConvUTF8);
507
511
bool dbgPgConn::GetIsEdb()
509
// to retrieve edb flag
510
BackendMinimumVersion(0,0);
513
// to retrieve edb flag
514
BackendMinimumVersion(0, 0);
514
518
bool dbgPgConn::GetIsGreenplum()
516
// to retrieve edb flag
517
BackendMinimumVersion(0,0);
518
return m_isGreenplum;
520
// to retrieve edb flag
521
BackendMinimumVersion(0, 0);
522
return m_isGreenplum;
525
wxString dbgPgConn::qtDbString(const wxString &value)
527
wxString result = value;
529
result.Replace(wxT("\\"), wxT("\\\\"));
530
result.Replace(wxT("'"), wxT("''"));
531
result.Append(wxT("'"));
533
if (BackendMinimumVersion(8, 1))
535
if (result.Contains(wxT("\\")))
536
result.Prepend(wxT("E'"));
538
result.Prepend(wxT("'"));
541
result.Prepend(wxT("'"));
b'\\ No newline at end of file'