4 * Firebird/Interbase driver module for GnuGk
6 * Copyright (c) 2006, Michal Zygmuntowicz
8 * This work is published under the GNU Public License (GPL)
9 * see file COPYING for details.
10 * We also explicitely grant the right to link this code
11 * with the OpenH323 library.
13 * $Log: gksql_firebird.cxx,v $
14 * Revision 1.5 2007/03/13 18:39:43 willamowius
15 * compile fix for PWLib 1.11.3CVS
17 * Revision 1.4 2006/08/08 12:24:36 zvision
18 * Escape quote characters in query strings
20 * Revision 1.3 2006/07/06 15:25:13 willamowius
21 * set all deleted pointers to NULL (most probably more than needed)
23 * Revision 1.2 2006/06/08 07:38:42 willamowius
24 * compile fixes for gcc 3.3.x
26 * Revision 1.1 2006/06/02 09:21:34 zvision
32 #if defined(_WIN32) && (_MSC_VER <= 1200)
33 #pragma warning(disable:4786) // warning about too long debug symbol off
34 #pragma warning(disable:4284)
43 PString XSQLVARToPString(XSQLVAR *sqlvar)
45 if (sqlvar->sqltype & 1)
46 if (*(sqlvar->sqlind) == -1)
49 switch (sqlvar->sqltype & ~1L) {
51 return PString(sqlvar->sqldata, sqlvar->sqllen);
53 return sqlvar->sqlscale < 0
54 ? PString(PString::Decimal, *(int*)(sqlvar->sqldata) * pow(10.0, sqlvar->sqlscale), abs(sqlvar->sqlscale))
55 : PString(*(short*)(sqlvar->sqldata));
57 return sqlvar->sqlscale < 0
58 ? PString(PString::Decimal, *(long*)(sqlvar->sqldata) * pow(10.0, sqlvar->sqlscale), abs(sqlvar->sqlscale))
59 : PString(*(long*)(sqlvar->sqldata));
61 return sqlvar->sqlscale < 0
62 ? PString(PString::Decimal, *(double*)(sqlvar->sqldata) * pow(10.0, sqlvar->sqlscale), abs(sqlvar->sqlscale))
63 : PString(PString::Printf, "%f", *(double*)(sqlvar->sqldata));
65 return sqlvar->sqlscale < 0
66 ? PString(PString::Decimal, *(ISC_INT64*)(sqlvar->sqldata) * pow(10.0, sqlvar->sqlscale), abs(sqlvar->sqlscale))
67 : PString(*(ISC_INT64*)(sqlvar->sqldata));
69 return PString(PString::Printf, "%f", (double)*(float*)(sqlvar->sqldata));
71 return PString(sqlvar->sqldata + 2, *(short*)(sqlvar->sqldata));
78 /** Class that encapsulates SQL query result for Firebird backend.
79 It does not provide any multithread safety, so should be accessed
80 from a single thread at time.
82 class GkIBSQLResult : public GkSQLResult
85 /// Build the result from SELECT type query
87 /// transaction handle
91 /// SELECT type query result
95 /// Build the empty result and store query execution error information
97 /// Firebird specific error code
98 unsigned int errorCode,
99 /// PostgreSQL specific error message text
100 const char* errorMsg,
101 /// transaction handle
102 isc_tr_handle tr = NULL,
104 isc_stmt_handle stmt = NULL,
105 /// SELECT type query result
106 XSQLDA* selectResult = NULL
109 virtual ~GkIBSQLResult();
112 Backend specific error message, if the query failed.
114 virtual PString GetErrorMessage();
117 Backend specific error code, if the query failed.
119 virtual long GetErrorCode();
122 True if rows can be fetched in random access order, false if
123 rows have to be fethed sequentially and can be retrieved only once.
125 virtual bool HasRandomAccess();
127 /** Fetch a single row from the result set. After each row is fetched,
128 cursor position is moved to a next row.
131 True if the row has been fetched, false if no more rows are available.
133 virtual bool FetchRow(
134 /// array to be filled with string representations of the row fields
137 virtual bool FetchRow(
138 /// array to be filled with string representations of the row fields
143 True if the column at the index #fieldOffset# is NULL in the row
144 fetched most recently.
146 virtual bool IsNullField(
147 /// index of the column to check
151 /** Fetch a single row from the result set. This function requires
152 that the backend supports random row access.
155 True if the row has been fetched, false if a row at the given offset
156 does not exists or SQL backend does not support random row access.
158 virtual bool FetchRow(
159 /// array to be filled with string representations of the row fields
160 PStringArray& result,
161 /// index (0 based) of the row to fetch
164 virtual bool FetchRow(
165 /// array to be filled with string representations of the row fields
167 /// index (0 based) of the row to fetch
172 True if the column at the index #fieldOffset# is NULL in the row
173 at the specified index.
175 virtual bool IsNullField(
176 /// index of the column to check
178 /// index (0 based) of the row to check
184 GkIBSQLResult(const GkIBSQLResult&);
185 GkIBSQLResult& operator=(const GkIBSQLResult&);
188 /// query result for SELECT type queries
190 /// transaction handle
193 isc_stmt_handle m_stmt;
194 /// the most recent row returned by fetch operation
196 /// Firebird specific error code (if the query failed)
197 unsigned int m_errorCode;
198 /// Firebird specific error message text (if the query failed)
199 PString m_errorMessage;
202 /// Firebird/Interbase backend connection implementation.
203 class GkIBSQLConnection : public GkSQLConnection
206 /// Build a new Firebird connection object
208 /// name to use in the log
209 const char* name = "Firebird"
212 virtual ~GkIBSQLConnection();
215 class IBSQLConnWrapper : public GkSQLConnection::SQLConnWrapper
219 /// unique identifier for this connection
221 /// host:port this connection is made to
223 /// Firebird connection object
225 ) : SQLConnWrapper(id, host), m_conn(conn) {}
227 virtual ~IBSQLConnWrapper();
231 IBSQLConnWrapper(const IBSQLConnWrapper&);
232 IBSQLConnWrapper& operator=(const IBSQLConnWrapper&);
235 isc_db_handle m_conn;
238 /** Create a new SQL connection using parameters stored in this object.
239 When the connection is to be closed, the object is simply deleted
240 using delete operator.
243 NULL if database connection could not be established
244 or an object of IBSQLConnWrapper class.
246 virtual SQLConnPtr CreateNewConnection(
247 /// unique identifier for this connection
251 /** Execute the query using specified SQL connection.
254 Query execution result.
256 virtual GkSQLResult* ExecuteQuery(
257 /// SQL connection to use for query execution
260 const char* queryStr,
261 /// maximum time (ms) for the query execution, -1 means infinite
265 /** Escape any special characters in the string, so it can be used in a SQL query.
270 virtual PString EscapeString(
271 /// SQL connection to get escaping parameters from
273 /// string to be escaped
278 GkIBSQLConnection(const GkIBSQLConnection&);
279 GkIBSQLConnection& operator=(const GkIBSQLConnection&);
283 GkIBSQLResult::GkIBSQLResult(
284 /// transaction handle
287 isc_stmt_handle stmt,
288 /// SELECT type query result
291 : GkSQLResult(false), m_sqlResult(selectResult), m_tr(tr), m_stmt(stmt), m_sqlRow(-1),
294 if (m_sqlResult != NULL) {
296 m_numFields = m_sqlResult->sqld;
297 for (int i = 0; i < m_sqlResult->sqld; ++i) {
298 m_sqlResult->sqlvar[i].sqlind = new short;
299 if ((m_sqlResult->sqlvar[i].sqltype & ~1L) == SQL_VARYING)
300 m_sqlResult->sqlvar[i].sqldata = new char[m_sqlResult->sqlvar[i].sqllen + 2];
302 m_sqlResult->sqlvar[i].sqldata = new char[m_sqlResult->sqlvar[i].sqllen];
307 m_selectType = (m_numFields != 0);
310 GkIBSQLResult::GkIBSQLResult(
311 /// Firebird specific error code
312 unsigned int errorCode,
313 /// Firebird specific error message text
314 const char* errorMsg,
315 /// transaction handle
318 isc_stmt_handle stmt,
319 /// SELECT type query result
322 : GkSQLResult(true), m_sqlResult(selectResult), m_tr(tr), m_stmt(stmt), m_sqlRow(-1),
323 m_errorCode(errorCode), m_errorMessage(errorMsg)
327 GkIBSQLResult::~GkIBSQLResult()
329 ISC_STATUS status[20];
332 isc_dsql_free_statement(status, &m_stmt, DSQL_drop);
333 if (m_sqlResult != NULL) {
334 for (int i = 0; i < m_sqlResult->sqld; ++i) {
335 if (m_sqlResult->sqlvar[i].sqldata != NULL) {
336 delete [] m_sqlResult->sqlvar[i].sqldata;
337 m_sqlResult->sqlvar[i].sqldata = NULL;
339 if (m_sqlResult->sqlvar[i].sqlind != NULL) {
340 delete m_sqlResult->sqlvar[i].sqlind;
341 m_sqlResult->sqlvar[i].sqlind = NULL;
344 delete [] reinterpret_cast<char*>(m_sqlResult);
349 isc_rollback_transaction(status, &m_tr);
351 isc_commit_transaction(status, &m_tr);
354 bool GkIBSQLResult::HasRandomAccess()
359 PString GkIBSQLResult::GetErrorMessage()
361 return m_errorMessage;
364 long GkIBSQLResult::GetErrorCode()
369 bool GkIBSQLResult::FetchRow(
370 /// array to be filled with string representations of the row fields
374 if (m_sqlResult == NULL || m_numRows <= 0)
380 if (m_sqlRow >= m_numRows)
384 ISC_STATUS status[20];
385 retval = isc_dsql_fetch(status, &m_stmt, 1, m_sqlResult);
386 if (status[0] == 1 && status[1] != 0) {
387 m_numRows = m_sqlRow;
389 long errcode = isc_sqlcode(status);
391 if (errcode == -999) {
393 long *pvector = status;
394 errormsg[isc_interprete(errormsg, &pvector)] = 0;
396 strcpy(errormsg, "SQL:");
397 isc_sql_interprete(static_cast<short>(errcode), errormsg + 4, 512 - 4);
399 PTRACE(2, "Firebird\tFailed to fetch query row (" << errcode
406 result.SetSize(m_sqlResult->sqld);
408 for (PINDEX i = 0; i < m_sqlResult->sqld; ++i)
409 result[i] = XSQLVARToPString(&(m_sqlResult->sqlvar[i]));
416 bool GkIBSQLResult::FetchRow(
417 /// array to be filled with string representations of the row fields
421 if (m_sqlResult == NULL || m_numRows <= 0)
427 if (m_sqlRow >= m_numRows)
430 ISC_STATUS status[20];
431 retval = isc_dsql_fetch(status, &m_stmt, 1, m_sqlResult);
432 if (status[0] == 1 && status[1] != 0) {
433 m_numRows = m_sqlRow;
435 long errcode = isc_sqlcode(status);
437 if (errcode == -999) {
439 long *pvector = status;
440 errormsg[isc_interprete(errormsg, &pvector)] = 0;
442 strcpy(errormsg, "SQL:");
443 isc_sql_interprete(static_cast<short>(errcode), errormsg + 4, 512 - 4);
445 PTRACE(2, "Firebird\tFailed to fetch query row (" << errcode
452 result.resize(m_numFields);
454 for (PINDEX i = 0; i < m_numFields; i++) {
455 result[i].first = XSQLVARToPString(&(m_sqlResult->sqlvar[i]));
456 result[i].second = PString(m_sqlResult->sqlvar[i].aliasname, m_sqlResult->sqlvar[i].aliasname_length);
464 bool GkIBSQLResult::IsNullField(
465 /// index of the column to check
469 return m_sqlResult == NULL || m_sqlRow < 0 || m_sqlRow >= m_numRows
470 || fieldOffset < 0 || fieldOffset >= m_numFields
471 || ((m_sqlResult->sqlvar[fieldOffset].sqltype & 1) && *(m_sqlResult->sqlvar[fieldOffset].sqlind) == -1);
474 bool GkIBSQLResult::FetchRow(
475 /// array to be filled with string representations of the row fields
476 PStringArray& result,
477 /// index (0 based) of the row to fetch
484 bool GkIBSQLResult::FetchRow(
485 /// array to be filled with string representations of the row fields
487 /// index (0 based) of the row to fetch
494 bool GkIBSQLResult::IsNullField(
495 /// index of the column to check
497 /// index (0 based) of the row to check
505 GkIBSQLConnection::GkIBSQLConnection(
506 /// name to use in the log
508 ) : GkSQLConnection(name)
512 GkIBSQLConnection::~GkIBSQLConnection()
516 GkIBSQLConnection::IBSQLConnWrapper::~IBSQLConnWrapper()
518 ISC_STATUS status[20];
519 isc_detach_database(status, &m_conn);
522 GkSQLConnection::SQLConnPtr GkIBSQLConnection::CreateNewConnection(
523 /// unique identifier for this connection
527 unsigned dpb_offset = 0;
528 std::vector<char> dpb(1);
530 dpb[dpb_offset++] = isc_dpb_version1;
533 dpb.resize(dpb.size() + 2 + m_username.GetLength());
534 dpb[dpb_offset++] = isc_dpb_user_name;
535 dpb[dpb_offset++] = m_username.GetLength();
536 memcpy(&(dpb[dpb_offset]), (const char*)m_username, m_username.GetLength());
537 dpb_offset += m_username.GetLength();
541 dpb.resize(dpb.size() + 2 + m_password.GetLength());
542 dpb[dpb_offset++] = isc_dpb_password;
543 dpb[dpb_offset++] = m_password.GetLength();
544 memcpy(&(dpb[dpb_offset]), (const char*)m_password, m_password.GetLength());
545 dpb_offset += m_password.GetLength();
548 ISC_STATUS status[20];
549 isc_db_handle conn = NULL;
550 std::string dbname = m_database;
553 dbname.insert(0, ":");
554 dbname.insert(0, (const char *)m_host);
557 isc_attach_database(status, 0, const_cast<char*>(dbname.c_str()), &conn, dpb_offset, &(dpb[0]));
558 if (status[0] == 1 && status[1] != 0) {
559 long *pvector = status;
561 errormsg[isc_interprete(errormsg, &pvector)] = 0;
562 PTRACE(2, GetName() << "\tFirebird connection to " << m_username << '@' << dbname
563 << " failed (isc_attach_database failed): " << errormsg
568 PTRACE(5, GetName() << "\tFirebird connection to " << m_username << '@' << dbname
569 << " established successfully"
571 return new IBSQLConnWrapper(id, dbname, conn);
574 GkSQLResult* GkIBSQLConnection::ExecuteQuery(
575 /// SQL connection to use for query execution
576 GkSQLConnection::SQLConnPtr con,
578 const char* queryStr,
579 /// maximum time (ms) for the query execution, -1 means infinite
583 isc_db_handle conn = ((IBSQLConnWrapper*)con)->m_conn;
586 ISC_STATUS status[20];
587 isc_tr_handle tr = NULL;
588 isc_stmt_handle stmt = NULL;
590 isc_start_transaction(status, &tr, 1, &conn, 0, NULL);
591 if (status[0] == 1 && status[1] != 0) {
592 long *pvector = status;
594 errormsg[isc_interprete(errormsg, &pvector)] = 0;
595 return new GkIBSQLResult(status[1], errormsg);
598 isc_dsql_allocate_statement(status, &conn, &stmt);
599 if (status[0] == 1 && status[1] != 0) {
600 long errorcode = isc_sqlcode(status);
601 if (errorcode == -999) {
602 errorcode = status[1];
603 long *pvector = status;
604 errormsg[isc_interprete(errormsg, &pvector)] = 0;
606 strcpy(errormsg, "SQL:");
607 isc_sql_interprete(static_cast<short>(errorcode), errormsg, 512 - 4);
609 return new GkIBSQLResult(errorcode, errormsg, tr);
613 XSQLDA *result = reinterpret_cast<XSQLDA*>(new char[XSQLDA_LENGTH(numcols)]);
614 memset(result, 0, XSQLDA_LENGTH(numcols));
615 result->version = SQLDA_VERSION1;
616 result->sqln = numcols;
618 isc_dsql_prepare(status, &tr, &stmt, 0, const_cast<char*>(queryStr), SQL_DIALECT_CURRENT, result);
619 if (status[0] == 1 && status[1] != 0) {
620 long errorcode = isc_sqlcode(status);
621 if (errorcode == -999) {
622 errorcode = status[1];
623 long *pvector = status;
624 errormsg[isc_interprete(errormsg, &pvector)] = 0;
626 strcpy(errormsg, "SQL:");
627 isc_sql_interprete(static_cast<short>(errorcode), errormsg, 512 - 4);
629 return new GkIBSQLResult(errorcode, errormsg, tr, stmt);
632 if (result->sqld > result->sqln) {
633 numcols = result->sqld;
634 delete [] reinterpret_cast<char*>(result);
635 result = reinterpret_cast<XSQLDA*>(new char[XSQLDA_LENGTH(numcols)]);
636 memset(result, 0, XSQLDA_LENGTH(numcols));
637 result->version = SQLDA_VERSION1;
638 result->sqln = numcols;
640 isc_dsql_describe(status, &stmt, SQLDA_VERSION1, result);
641 if (status[0] == 1 && status[1] != 0) {
642 long errorcode = isc_sqlcode(status);
643 if (errorcode == -999) {
644 errorcode = status[1];
645 long *pvector = status;
646 errormsg[isc_interprete(errormsg, &pvector)] = 0;
648 strcpy(errormsg, "SQL:");
649 isc_sql_interprete(static_cast<short>(errorcode), errormsg, 512 - 4);
651 delete [] reinterpret_cast<char*>(result);
653 return new GkIBSQLResult(errorcode, errormsg, tr, stmt);
657 isc_dsql_execute(status, &tr, &stmt, SQLDA_VERSION1, NULL);
658 if (status[0] == 1 && status[1] != 0) {
659 long errorcode = isc_sqlcode(status);
660 if (errorcode == -999) {
661 errorcode = status[1];
662 long *pvector = status;
663 errormsg[isc_interprete(errormsg, &pvector)] = 0;
665 strcpy(errormsg, "SQL:");
666 isc_sql_interprete(static_cast<short>(errorcode), errormsg, 512 - 4);
668 delete [] reinterpret_cast<char*>(result);
670 return new GkIBSQLResult(errorcode, errormsg, tr, stmt);
673 return new GkIBSQLResult(tr, stmt, result);
676 PString GkIBSQLConnection::EscapeString(
677 /// SQL connection to get escaping parameters from
679 /// string to be escaped
685 s.Replace("'", "''", TRUE);
691 GkSQLCreator<GkIBSQLConnection> IBSQLCreator("Firebird");
694 #endif /* HAS_FIREBIRD */