source: trunk/src/sql/drivers/ibase/qsql_ibase.cpp

Last change on this file was 196, checked in by rudi, 14 years ago

Add SQL module (currently it isn't build by default, however it's needed for QtDesigner)

File size: 29.0 KB
Line 
1/****************************************************************************
2**
3** Implementation of Interbase driver classes.
4**
5** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
6**
7** This file is part of the sql module of the Qt GUI Toolkit.
8** EDITIONS: FREE, ENTERPRISE
9**
10** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12**
13****************************************************************************/
14
15#include "qsql_ibase.h"
16
17#include <qdatetime.h>
18#include <private/qsqlextension_p.h>
19
20#include <ibase.h>
21#include <stdlib.h>
22#include <limits.h>
23#include <math.h>
24
25#define QIBASE_DRIVER_NAME "QIBASE"
26
27class QIBasePreparedExtension : public QSqlExtension
28{
29public:
30 QIBasePreparedExtension(QIBaseResult *r)
31 : result(r) {}
32
33 bool prepare(const QString &query)
34 {
35 return result->prepare(query);
36 }
37
38 bool exec()
39 {
40 return result->exec();
41 }
42
43 QIBaseResult *result;
44};
45
46static bool getIBaseError(QString& msg, ISC_STATUS* status, long &sqlcode)
47{
48 if (status[0] != 1 || status[1] <= 0)
49 return FALSE;
50
51 sqlcode = isc_sqlcode(status);
52 char buf[512];
53 isc_sql_interprete(sqlcode, buf, 512);
54 msg = QString::fromUtf8(buf);
55 return TRUE;
56}
57
58static void createDA(XSQLDA *&sqlda)
59{
60 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
61 sqlda->sqln = 1;
62 sqlda->sqld = 0;
63 sqlda->version = SQLDA_VERSION1;
64 sqlda->sqlvar[0].sqlind = 0;
65 sqlda->sqlvar[0].sqldata = 0;
66}
67
68static void enlargeDA(XSQLDA *&sqlda, int n)
69{
70 free(sqlda);
71 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
72 sqlda->sqln = n;
73 sqlda->version = SQLDA_VERSION1;
74}
75
76static void initDA(XSQLDA *sqlda)
77{
78 for (int i = 0; i < sqlda->sqld; ++i) {
79 switch (sqlda->sqlvar[i].sqltype & ~1) {
80 case SQL_INT64:
81 case SQL_LONG:
82 case SQL_SHORT:
83 case SQL_FLOAT:
84 case SQL_DOUBLE:
85 case SQL_TIMESTAMP:
86 case SQL_TYPE_TIME:
87 case SQL_TYPE_DATE:
88 case SQL_TEXT:
89 case SQL_BLOB:
90 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
91 break;
92 case SQL_VARYING:
93 sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
94 break;
95 default:
96 // not supported - do not bind.
97 sqlda->sqlvar[i].sqldata = 0;
98 break;
99 }
100 if (sqlda->sqlvar[i].sqltype & 1) {
101 sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short));
102 *(sqlda->sqlvar[i].sqlind) = 0;
103 } else {
104 sqlda->sqlvar[i].sqlind = 0;
105 }
106 }
107}
108
109static void delDA(XSQLDA *&sqlda)
110{
111 if (!sqlda)
112 return;
113 for (int i = 0; i < sqlda->sqld; ++i) {
114 free(sqlda->sqlvar[i].sqlind);
115 free(sqlda->sqlvar[i].sqldata);
116 }
117 free(sqlda);
118 sqlda = 0;
119}
120
121static QVariant::Type qIBaseTypeName(int iType)
122{
123 switch (iType) {
124 case blr_varying:
125 case blr_varying2:
126 case blr_text:
127 case blr_cstring:
128 case blr_cstring2:
129 return QVariant::String;
130 case blr_sql_time:
131 return QVariant::Time;
132 case blr_sql_date:
133 return QVariant::Date;
134 case blr_timestamp:
135 return QVariant::DateTime;
136 case blr_blob:
137 return QVariant::ByteArray;
138 case blr_quad:
139 case blr_short:
140 case blr_long:
141 return QVariant::Int;
142 case blr_int64:
143 return QVariant::LongLong;
144 case blr_float:
145 case blr_d_float:
146 case blr_double:
147 return QVariant::Double;
148 }
149 return QVariant::Invalid;
150}
151
152static QVariant::Type qIBaseTypeName2(int iType)
153{
154 switch(iType & ~1) {
155 case SQL_VARYING:
156 case SQL_TEXT:
157 return QVariant::String;
158 case SQL_LONG:
159 case SQL_SHORT:
160 return QVariant::Int;
161 case SQL_INT64:
162 return QVariant::LongLong;
163 case SQL_FLOAT:
164 case SQL_DOUBLE:
165 return QVariant::Double;
166 case SQL_TIMESTAMP:
167 return QVariant::DateTime;
168 case SQL_TYPE_DATE:
169 return QVariant::Date;
170 case SQL_TYPE_TIME:
171 return QVariant::Time;
172 default:
173 return QVariant::Invalid;
174 }
175}
176
177static ISC_TIME toTime(const QTime &t)
178{
179 static const QTime midnight(0, 0, 0, 0);
180 return (ISC_TIME)midnight.msecsTo(t) * 10;
181}
182
183static ISC_DATE toDate(const QDate &d)
184{
185 static const QDate basedate(1858, 11, 17);
186 return (ISC_DATE)basedate.daysTo(d);
187}
188
189static ISC_TIMESTAMP toTimeStamp(const QDateTime &dt)
190{
191 ISC_TIMESTAMP ts;
192 ts.timestamp_time = toTime(dt.time());
193 ts.timestamp_date = toDate(dt.date());
194 return ts;
195}
196
197static QTime toQTime(ISC_TIME time)
198{
199 // have to demangle the structure ourselves because isc_decode_time
200 // strips the msecs
201 static const QTime t;
202 return t.addMSecs(time / 10);
203}
204
205static QDate toQDate(ISC_DATE d)
206{
207 static const QDate bd(1858, 11, 17);
208 return bd.addDays(d);
209}
210
211static QDateTime toQDateTime(ISC_TIMESTAMP *ts)
212{
213 return QDateTime(toQDate(ts->timestamp_date), toQTime(ts->timestamp_time));
214}
215
216class QIBaseDriverPrivate
217{
218public:
219 QIBaseDriverPrivate(QIBaseDriver *d): q(d)
220 {
221 ibase = 0;
222 trans = 0;
223 }
224
225 bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
226 {
227 QString imsg;
228 long sqlcode;
229 if (!getIBaseError(imsg, status, sqlcode))
230 return FALSE;
231
232 q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
233 return TRUE;
234 }
235
236public:
237 QIBaseDriver* q;
238 isc_db_handle ibase;
239 isc_tr_handle trans;
240 ISC_STATUS status[20];
241};
242
243class QIBaseResultPrivate
244{
245public:
246 QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb);
247 ~QIBaseResultPrivate() { cleanup(); }
248
249 void cleanup();
250 bool isError(const QString &msg = QString::null, QSqlError::Type typ = QSqlError::Unknown)
251 {
252 QString imsg;
253 long sqlcode;
254 if (!getIBaseError(imsg, status, sqlcode))
255 return FALSE;
256
257 q->setLastError(QSqlError(msg, imsg, typ, (int)sqlcode));
258 return TRUE;
259 }
260
261 bool transaction();
262 bool commit();
263
264 bool isSelect();
265 QVariant fetchBlob(ISC_QUAD *bId);
266 void writeBlob(int i, const QByteArray &ba);
267
268public:
269 QIBaseResult *q;
270 const QIBaseDriver *db;
271 ISC_STATUS status[20];
272 isc_tr_handle trans;
273 //indicator whether we have a local transaction or a transaction on driver level
274 bool localTransaction;
275 isc_stmt_handle stmt;
276 isc_db_handle ibase;
277 XSQLDA *sqlda; // output sqlda
278 XSQLDA *inda; // input parameters
279 int queryType;
280};
281
282QIBaseResultPrivate::QIBaseResultPrivate(QIBaseResult *d, const QIBaseDriver *ddb):
283 q(d), db(ddb), trans(0), stmt(0), ibase(ddb->d->ibase), sqlda(0), inda(0), queryType(-1)
284{
285 localTransaction = (ddb->d->ibase == 0);
286}
287
288void QIBaseResultPrivate::cleanup()
289{
290 if (stmt) {
291 isc_dsql_free_statement(status, &stmt, DSQL_drop);
292 stmt = 0;
293 }
294
295 commit();
296 if (!localTransaction)
297 trans = 0;
298
299 delDA(sqlda);
300 delDA(inda);
301
302 queryType = -1;
303 q->cleanup();
304}
305
306void QIBaseResultPrivate::writeBlob(int i, const QByteArray &ba)
307{
308 isc_blob_handle handle = 0;
309 ISC_QUAD *bId = (ISC_QUAD*)inda->sqlvar[i].sqldata;
310 isc_create_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
311 if (!isError("Unable to create BLOB", QSqlError::Statement)) {
312 uint i = 0;
313 while (i < ba.size()) {
314 isc_put_segment(status, &handle, QMIN(ba.size() - i, SHRT_MAX), ba.data());
315 if (isError("Unable to write BLOB"))
316 break;
317 i += SHRT_MAX;
318 }
319 }
320 isc_close_blob(status, &handle);
321}
322
323QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
324{
325 isc_blob_handle handle = 0;
326
327 isc_open_blob2(status, &ibase, &trans, &handle, bId, 0, 0);
328 if (isError("Unable to open BLOB", QSqlError::Statement))
329 return QVariant();
330
331 unsigned short len = 0;
332 QByteArray ba(255);
333 ISC_STATUS stat = isc_get_segment(status, &handle, &len, ba.size(), ba.data());
334 while (status[1] == isc_segment) {
335 uint osize = ba.size();
336 // double the amount of data fetched on each iteration
337 ba.resize(QMIN(ba.size() * 2, SHRT_MAX));
338 stat = isc_get_segment(status, &handle, &len, osize, ba.data() + osize);
339 }
340 bool isErr = isError("Unable to read BLOB", QSqlError::Statement);
341 isc_close_blob(status, &handle);
342 if (isErr)
343 return QVariant();
344
345 if (ba.size() > 255)
346 ba.resize(ba.size() / 2 + len);
347 else
348 ba.resize(len);
349
350 return ba;
351}
352
353bool QIBaseResultPrivate::isSelect()
354{
355 char acBuffer[9];
356 char qType = isc_info_sql_stmt_type;
357 isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer);
358 if (isError("Could not get query info", QSqlError::Statement))
359 return FALSE;
360 int iLength = isc_vax_integer(&acBuffer[1], 2);
361 queryType = isc_vax_integer(&acBuffer[3], iLength);
362 return (queryType == isc_info_sql_stmt_select);
363}
364
365bool QIBaseResultPrivate::transaction()
366{
367 if (trans)
368 return TRUE;
369 if (db->d->trans) {
370 localTransaction = FALSE;
371 trans = db->d->trans;
372 return TRUE;
373 }
374 localTransaction = TRUE;
375
376 isc_start_transaction(status, &trans, 1, &ibase, 0, NULL);
377 if (isError("Could not start transaction", QSqlError::Statement))
378 return FALSE;
379
380 return TRUE;
381}
382
383// does nothing if the transaction is on the
384// driver level
385bool QIBaseResultPrivate::commit()
386{
387 if (!trans)
388 return FALSE;
389 // don't commit driver's transaction, the driver will do it for us
390 if (!localTransaction)
391 return TRUE;
392
393 isc_commit_transaction(status, &trans);
394 trans = 0;
395 return !isError("Unable to commit transaction", QSqlError::Statement);
396}
397
398//////////
399
400QIBaseResult::QIBaseResult(const QIBaseDriver* db):
401 QtSqlCachedResult(db)
402{
403 d = new QIBaseResultPrivate(this, db);
404 setExtension(new QIBasePreparedExtension(this));
405}
406
407QIBaseResult::~QIBaseResult()
408{
409 delete d;
410}
411
412bool QIBaseResult::prepare(const QString& query)
413{
414 //qDebug("prepare: %s", query.ascii());
415 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
416 return FALSE;
417 d->cleanup();
418 setActive(FALSE);
419 setAt(QSql::BeforeFirst);
420
421 createDA(d->sqlda);
422 createDA(d->inda);
423
424 if (!d->transaction())
425 return FALSE;
426
427 isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
428 if (d->isError("Could not allocate statement", QSqlError::Statement))
429 return FALSE;
430 isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
431 if (d->isError("Could not prepare statement", QSqlError::Statement))
432 return FALSE;
433
434 isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
435 if (d->isError("Could not describe input statement", QSqlError::Statement))
436 return FALSE;
437 if (d->inda->sqld > d->inda->sqln) {
438 enlargeDA(d->inda, d->inda->sqld);
439
440 isc_dsql_describe_bind(d->status, &d->stmt, 1, d->inda);
441 if (d->isError("Could not describe input statement", QSqlError::Statement))
442 return FALSE;
443 }
444 initDA(d->inda);
445 if (d->sqlda->sqld > d->sqlda->sqln) {
446 // need more field descriptors
447 enlargeDA(d->sqlda, d->sqlda->sqld);
448
449 isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
450 if (d->isError("Could not describe statement", QSqlError::Statement))
451 return FALSE;
452 }
453 initDA(d->sqlda);
454
455 setSelect(d->isSelect());
456 if (!isSelect()) {
457 free(d->sqlda);
458 d->sqlda = 0;
459 }
460
461 return TRUE;
462}
463
464bool QIBaseResult::exec()
465{
466 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
467 return FALSE;
468 setActive(FALSE);
469 setAt(QSql::BeforeFirst);
470
471 if (d->inda && extension()->index.count() > 0) {
472 QMap<int, QString>::ConstIterator it;
473 if ((int)extension()->index.count() > d->inda->sqld) {
474 qWarning("QIBaseResult::exec: Parameter mismatch, expected %d, got %d parameters", d->inda->sqld, extension()->index.count());
475 return FALSE;
476 }
477 int para = 0;
478 for (it = extension()->index.constBegin(); it != extension()->index.constEnd(); ++it, ++para) {
479 if (para >= d->inda->sqld)
480 break;
481 if (!d->inda->sqlvar[para].sqldata)
482 continue;
483 const QVariant val(extension()->values[it.data()].value);
484 if (d->inda->sqlvar[para].sqltype & 1) {
485 if (val.isNull()) {
486 // set null indicator
487 *(d->inda->sqlvar[para].sqlind) = 1;
488 // and set the value to 0, otherwise it would count as empty string.
489 *((short*)d->inda->sqlvar[para].sqldata) = 0;
490 continue;
491 }
492 // a value of 0 means non-null.
493 *(d->inda->sqlvar[para].sqlind) = 0;
494 }
495 switch(d->inda->sqlvar[para].sqltype & ~1) {
496 case SQL_INT64:
497 if (d->inda->sqlvar[para].sqlscale < 0)
498 *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = Q_LLONG(val.toDouble() *
499 pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
500 else
501 *((Q_LLONG*)d->inda->sqlvar[para].sqldata) = val.toLongLong();
502 break;
503 case SQL_LONG:
504 *((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
505 break;
506 case SQL_SHORT:
507 *((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
508 break;
509 case SQL_FLOAT:
510 *((float*)d->inda->sqlvar[para].sqldata) = (float)val.toDouble();
511 break;
512 case SQL_DOUBLE:
513 *((double*)d->inda->sqlvar[para].sqldata) = val.toDouble();
514 break;
515 case SQL_TIMESTAMP:
516 *((ISC_TIMESTAMP*)d->inda->sqlvar[para].sqldata) = toTimeStamp(val.toDateTime());
517 break;
518 case SQL_TYPE_TIME:
519 *((ISC_TIME*)d->inda->sqlvar[para].sqldata) = toTime(val.toTime());
520 break;
521 case SQL_TYPE_DATE:
522 *((ISC_DATE*)d->inda->sqlvar[para].sqldata) = toDate(val.toDate());
523 break;
524 case SQL_VARYING: {
525 QCString str(val.toString().utf8()); // keep a copy of the string alive in this scope
526 short buflen = d->inda->sqlvar[para].sqllen;
527 if (str.length() < (uint)buflen)
528 buflen = str.length();
529 *(short*)d->inda->sqlvar[para].sqldata = buflen; // first two bytes is the length
530 memcpy(d->inda->sqlvar[para].sqldata + sizeof(short), str.data(), buflen);
531 break; }
532 case SQL_TEXT: {
533 QCString str(val.toString().utf8().leftJustify(d->inda->sqlvar[para].sqllen, ' ', TRUE));
534 memcpy(d->inda->sqlvar[para].sqldata, str.data(), d->inda->sqlvar[para].sqllen);
535 break; }
536 case SQL_BLOB:
537 d->writeBlob(para, val.toByteArray());
538 break;
539 default:
540 break;
541 }
542 }
543 }
544
545 if (colCount()) {
546 isc_dsql_free_statement(d->status, &d->stmt, DSQL_close);
547 if (d->isError("Unable to close statement"))
548 return FALSE;
549 cleanup();
550 }
551 if (d->sqlda)
552 init(d->sqlda->sqld);
553 isc_dsql_execute2(d->status, &d->trans, &d->stmt, 1, d->inda, 0);
554 if (d->isError("Unable to execute query"))
555 return FALSE;
556
557 setActive(TRUE);
558 return TRUE;
559}
560
561bool QIBaseResult::reset (const QString& query)
562{
563// qDebug("reset: %s", query.ascii());
564 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
565 return FALSE;
566 d->cleanup();
567 setActive(FALSE);
568 setAt(QSql::BeforeFirst);
569
570 createDA(d->sqlda);
571
572 if (!d->transaction())
573 return FALSE;
574
575 isc_dsql_allocate_statement(d->status, &d->ibase, &d->stmt);
576 if (d->isError("Could not allocate statement", QSqlError::Statement))
577 return FALSE;
578 isc_dsql_prepare(d->status, &d->trans, &d->stmt, 0, query.utf8().data(), 3, d->sqlda);
579 if (d->isError("Could not prepare statement", QSqlError::Statement))
580 return FALSE;
581
582 if (d->sqlda->sqld > d->sqlda->sqln) {
583 // need more field descriptors
584 int n = d->sqlda->sqld;
585 free(d->sqlda);
586 d->sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
587 d->sqlda->sqln = n;
588 d->sqlda->version = SQLDA_VERSION1;
589
590 isc_dsql_describe(d->status, &d->stmt, 1, d->sqlda);
591 if (d->isError("Could not describe statement", QSqlError::Statement))
592 return FALSE;
593 }
594
595 initDA(d->sqlda);
596
597 setSelect(d->isSelect());
598 if (isSelect()) {
599 init(d->sqlda->sqld);
600 } else {
601 free(d->sqlda);
602 d->sqlda = 0;
603 }
604
605 isc_dsql_execute(d->status, &d->trans, &d->stmt, 1, 0);
606 if (d->isError("Unable to execute query"))
607 return FALSE;
608
609 // commit non-select queries (if they are local)
610 if (!isSelect() && !d->commit())
611 return FALSE;
612
613 setActive(TRUE);
614 return TRUE;
615}
616
617bool QIBaseResult::gotoNext(QtSqlCachedResult::RowCache* row)
618{
619 ISC_STATUS stat = isc_dsql_fetch(d->status, &d->stmt, 1, d->sqlda);
620
621 if (stat == 100) {
622 // no more rows
623 setAt(QSql::AfterLast);
624 return FALSE;
625 }
626 if (d->isError("Could not fetch next item", QSqlError::Statement))
627 return FALSE;
628 if (!row) // not interested in actual values
629 return TRUE;
630
631 Q_ASSERT(row);
632 Q_ASSERT((int)row->size() == d->sqlda->sqld);
633 for (int i = 0; i < d->sqlda->sqld; ++i) {
634 char *buf = d->sqlda->sqlvar[i].sqldata;
635 int size = d->sqlda->sqlvar[i].sqllen;
636 Q_ASSERT(buf);
637
638 if ((d->sqlda->sqlvar[i].sqltype & 1) && *d->sqlda->sqlvar[i].sqlind) {
639 // null value
640 QVariant v;
641 v.cast(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype));
642 (*row)[i] = v;
643 continue;
644 }
645
646 switch(d->sqlda->sqlvar[i].sqltype & ~1) {
647 case SQL_VARYING:
648 // pascal strings - a short with a length information followed by the data
649 (*row)[i] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
650 break;
651 case SQL_INT64:
652 if (d->sqlda->sqlvar[i].sqlscale < 0)
653 (*row)[i] = *(Q_LLONG*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
654 else
655 (*row)[i] = QVariant(*(Q_LLONG*)buf);
656 break;
657 case SQL_LONG:
658 if (sizeof(int) == sizeof(long)) //dear compiler: please optimize me out.
659 (*row)[i] = QVariant((int)(*(long*)buf));
660 else
661 (*row)[i] = QVariant((Q_LLONG)(*(long*)buf));
662 break;
663 case SQL_SHORT:
664 (*row)[i] = QVariant((int)(*(short*)buf));
665 break;
666 case SQL_FLOAT:
667 (*row)[i] = QVariant((double)(*(float*)buf));
668 break;
669 case SQL_DOUBLE:
670 (*row)[i] = QVariant(*(double*)buf);
671 break;
672 case SQL_TIMESTAMP:
673 (*row)[i] = toQDateTime((ISC_TIMESTAMP*)buf);
674 break;
675 case SQL_TYPE_TIME:
676 (*row)[i] = toQTime(*(ISC_TIME*)buf);
677 break;
678 case SQL_TYPE_DATE:
679 (*row)[i] = toQDate(*(ISC_DATE*)buf);
680 break;
681 case SQL_TEXT:
682 (*row)[i] = QString::fromUtf8(buf, size);
683 break;
684 case SQL_BLOB:
685 (*row)[i] = d->fetchBlob((ISC_QUAD*)buf);
686 break;
687 default:
688 // unknown type - don't even try to fetch
689 (*row)[i] = QVariant();
690 break;
691 }
692 }
693
694 return TRUE;
695}
696
697int QIBaseResult::size()
698{
699 static char sizeInfo[] = {isc_info_sql_records};
700 char buf[33];
701
702 if (!isActive() || !isSelect())
703 return -1;
704
705 isc_dsql_sql_info(d->status, &d->stmt, sizeof(sizeInfo), sizeInfo, sizeof(buf), buf);
706 for (char* c = buf + 3; *c != isc_info_end; /*nothing*/) {
707 char ct = *c++;
708 short len = isc_vax_integer(c, 2);
709 c += 2;
710 int val = isc_vax_integer(c, len);
711 c += len;
712 if (ct == isc_info_req_select_count)
713 return val;
714 }
715 return -1;
716}
717
718int QIBaseResult::numRowsAffected()
719{
720 static char acCountInfo[] = {isc_info_sql_records};
721 char cCountType;
722
723 switch (d->queryType) {
724 case isc_info_sql_stmt_select:
725 cCountType = isc_info_req_select_count;
726 break;
727 case isc_info_sql_stmt_update:
728 cCountType = isc_info_req_update_count;
729 break;
730 case isc_info_sql_stmt_delete:
731 cCountType = isc_info_req_delete_count;
732 break;
733 case isc_info_sql_stmt_insert:
734 cCountType = isc_info_req_insert_count;
735 break;
736 }
737
738 char acBuffer[33];
739 int iResult = -1;
740 isc_dsql_sql_info(d->status, &d->stmt, sizeof(acCountInfo), acCountInfo, sizeof(acBuffer), acBuffer);
741 if (d->isError("Could not get statement info", QSqlError::Statement))
742 return -1;
743 for (char *pcBuf = acBuffer + 3; *pcBuf != isc_info_end; /*nothing*/) {
744 char cType = *pcBuf++;
745 short sLength = isc_vax_integer (pcBuf, 2);
746 pcBuf += 2;
747 int iValue = isc_vax_integer (pcBuf, sLength);
748 pcBuf += sLength;
749
750 if (cType == cCountType) {
751 iResult = iValue;
752 break;
753 }
754 }
755 return iResult;
756}
757
758/*********************************/
759
760QIBaseDriver::QIBaseDriver(QObject * parent, const char * name)
761 : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
762{
763 d = new QIBaseDriverPrivate(this);
764}
765
766QIBaseDriver::QIBaseDriver(void *connection, QObject *parent, const char *name)
767 : QSqlDriver(parent, name ? name : QIBASE_DRIVER_NAME)
768{
769 d = new QIBaseDriverPrivate(this);
770 d->ibase = (isc_db_handle)connection;
771 setOpen(TRUE);
772 setOpenError(FALSE);
773}
774
775QIBaseDriver::~QIBaseDriver()
776{
777 delete d;
778}
779
780bool QIBaseDriver::hasFeature(DriverFeature f) const
781{
782 switch (f) {
783 case Transactions:
784// case QuerySize:
785 case PreparedQueries:
786 case PositionalPlaceholders:
787 case Unicode:
788 case BLOB:
789 return TRUE;
790 default:
791 return FALSE;
792 }
793}
794
795bool QIBaseDriver::open(const QString & db,
796 const QString & user,
797 const QString & password,
798 const QString & host,
799 int /*port*/,
800 const QString & /* connOpts */)
801{
802 if (isOpen())
803 close();
804
805 static const char enc[8] = "UTF_FSS";
806 QCString usr = user.local8Bit();
807 QCString pass = password.local8Bit();
808 usr.truncate(255);
809 pass.truncate(255);
810
811 QByteArray ba(usr.length() + pass.length() + sizeof(enc) + 6);
812 int i = -1;
813 ba[++i] = isc_dpb_version1;
814 ba[++i] = isc_dpb_user_name;
815 ba[++i] = usr.length();
816 memcpy(&ba[++i], usr.data(), usr.length());
817 i += usr.length();
818 ba[i] = isc_dpb_password;
819 ba[++i] = pass.length();
820 memcpy(&ba[++i], pass.data(), pass.length());
821 i += pass.length();
822 ba[i] = isc_dpb_lc_ctype;
823 ba[++i] = sizeof(enc) - 1;
824 memcpy(&ba[++i], enc, sizeof(enc) - 1);
825 i += sizeof(enc) - 1;
826
827 QString ldb;
828 if (!host.isEmpty())
829 ldb += host + ":";
830 ldb += db;
831 isc_attach_database(d->status, 0, (char*)ldb.latin1(), &d->ibase, i, ba.data());
832 if (d->isError("Error opening database", QSqlError::Connection)) {
833 setOpenError(TRUE);
834 return FALSE;
835 }
836
837 setOpen(TRUE);
838 return TRUE;
839}
840
841void QIBaseDriver::close()
842{
843 if (isOpen()) {
844 isc_detach_database(d->status, &d->ibase);
845 d->ibase = 0;
846 setOpen(FALSE);
847 setOpenError(FALSE);
848 }
849}
850
851QSqlQuery QIBaseDriver::createQuery() const
852{
853 return QSqlQuery(new QIBaseResult(this));
854}
855
856bool QIBaseDriver::beginTransaction()
857{
858 if (!isOpen() || isOpenError())
859 return FALSE;
860 if (d->trans)
861 return FALSE;
862
863 isc_start_transaction(d->status, &d->trans, 1, &d->ibase, 0, NULL);
864 return !d->isError("Could not start transaction", QSqlError::Transaction);
865}
866
867bool QIBaseDriver::commitTransaction()
868{
869 if (!isOpen() || isOpenError())
870 return FALSE;
871 if (!d->trans)
872 return FALSE;
873
874 isc_commit_transaction(d->status, &d->trans);
875 d->trans = 0;
876 return !d->isError("Unable to commit transaction", QSqlError::Transaction);
877}
878
879bool QIBaseDriver::rollbackTransaction()
880{
881 if (!isOpen() || isOpenError())
882 return FALSE;
883 if (!d->trans)
884 return FALSE;
885
886 isc_rollback_transaction(d->status, &d->trans);
887 d->trans = 0;
888 return !d->isError("Unable to rollback transaction", QSqlError::Transaction);
889}
890
891QStringList QIBaseDriver::tables(const QString& typeName) const
892{
893 QStringList res;
894 if (!isOpen())
895 return res;
896
897 int type = typeName.isEmpty() ? (int)QSql::Tables | (int)QSql::Views : typeName.toInt();
898 QString typeFilter;
899
900 if (type == (int)QSql::SystemTables) {
901 typeFilter += "RDB$SYSTEM_FLAG != 0";
902 } else if (type == ((int)QSql::SystemTables | (int)QSql::Views)) {
903 typeFilter += "RDB$SYSTEM_FLAG != 0 OR RDB$VIEW_BLR NOT NULL";
904 } else {
905 if (!(type & (int)QSql::SystemTables))
906 typeFilter += "RDB$SYSTEM_FLAG = 0 AND ";
907 if (!(type & (int)QSql::Views))
908 typeFilter += "RDB$VIEW_BLR IS NULL AND ";
909 if (!(type & (int)QSql::Tables))
910 typeFilter += "RDB$VIEW_BLR IS NOT NULL AND ";
911 if (!typeFilter.isEmpty())
912 typeFilter.truncate(typeFilter.length() - 5);
913 }
914 if (!typeFilter.isEmpty())
915 typeFilter.prepend("where ");
916
917 QSqlQuery q = createQuery();
918 q.setForwardOnly(TRUE);
919 if (!q.exec("select rdb$relation_name from rdb$relations " + typeFilter))
920 return res;
921 while(q.next())
922 res << q.value(0).toString().stripWhiteSpace();
923
924 return res;
925}
926
927QSqlRecord QIBaseDriver::record(const QString& tablename) const
928{
929 QSqlRecord rec;
930 if (!isOpen())
931 return rec;
932
933 QSqlQuery q = createQuery();
934 q.setForwardOnly(TRUE);
935
936 q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE "
937 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
938 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
939 "AND a.RDB$RELATION_NAME = '" + tablename.upper()+ "' "
940 "ORDER BY RDB$FIELD_POSITION");
941 while (q.next()) {
942 QSqlField field(q.value(0).toString().stripWhiteSpace(), qIBaseTypeName(q.value(1).toInt()));
943 rec.append(field);
944 }
945
946 return rec;
947}
948
949QSqlRecordInfo QIBaseDriver::recordInfo(const QString& tablename) const
950{
951 QSqlRecordInfo rec;
952 if (!isOpen())
953 return rec;
954
955 QSqlQuery q = createQuery();
956 q.setForwardOnly(TRUE);
957
958 q.exec("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, b.RDB$FIELD_SCALE, "
959 "b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
960 "FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
961 "WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
962 "AND a.RDB$RELATION_NAME = '" + tablename.upper() + "' "
963 "ORDER BY a.RDB$FIELD_POSITION");
964
965 while (q.next()) {
966 QVariant::Type type = qIBaseTypeName(q.value(1).toInt());
967 QSqlFieldInfo field(q.value(0).toString().stripWhiteSpace(), type, q.value(5).toInt(),
968 q.value(2).toInt(), q.value(4).toInt(), QVariant());
969
970 rec.append(field);
971 }
972
973 return rec;
974}
975
976QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
977{
978 QSqlIndex index(table);
979 if (!isOpen())
980 return index;
981
982 QSqlQuery q = createQuery();
983 q.setForwardOnly(TRUE);
984 q.exec("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE "
985 "FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
986 "WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
987 "AND a.RDB$RELATION_NAME = '" + table.upper() + "' "
988 "AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
989 "AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
990 "AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
991 "AND d.RDB$FIELD_NAME = c.RDB$FIELD_SOURCE "
992 "ORDER BY b.RDB$FIELD_POSITION");
993
994 while (q.next()) {
995 QSqlField field(q.value(1).toString().stripWhiteSpace(), qIBaseTypeName(q.value(2).toInt()));
996 index.append(field); //TODO: asc? desc?
997 index.setName(q.value(0).toString());
998 }
999
1000 return index;
1001}
1002
1003QSqlRecord QIBaseDriver::record(const QSqlQuery& query) const
1004{
1005 QSqlRecord rec;
1006 if (query.isActive() && query.driver() == this) {
1007 QIBaseResult* result = (QIBaseResult*)query.result();
1008 if (!result->d->sqlda)
1009 return rec;
1010 XSQLVAR v;
1011 for (int i = 0; i < result->d->sqlda->sqld; ++i) {
1012 v = result->d->sqlda->sqlvar[i];
1013 QSqlField f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
1014 qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype));
1015 rec.append(f);
1016 }
1017 }
1018 return rec;
1019}
1020
1021QSqlRecordInfo QIBaseDriver::recordInfo(const QSqlQuery& query) const
1022{
1023 QSqlRecordInfo rec;
1024 if (query.isActive() && query.driver() == this) {
1025 QIBaseResult* result = (QIBaseResult*)query.result();
1026 if (!result->d->sqlda)
1027 return rec;
1028 XSQLVAR v;
1029 for (int i = 0; i < result->d->sqlda->sqld; ++i) {
1030 v = result->d->sqlda->sqlvar[i];
1031 QSqlFieldInfo f(QString::fromLatin1(v.sqlname, v.sqlname_length).stripWhiteSpace(),
1032 qIBaseTypeName2(result->d->sqlda->sqlvar[i].sqltype),
1033 -1, v.sqllen, QABS(v.sqlscale), QVariant(), v.sqltype);
1034 rec.append(f);
1035 }
1036 }
1037 return rec;
1038}
1039
1040QString QIBaseDriver::formatValue(const QSqlField* field, bool trimStrings) const
1041{
1042 switch (field->type()) {
1043 case QVariant::DateTime: {
1044 QDateTime datetime = field->value().toDateTime();
1045 if (datetime.isValid())
1046 return "'" + QString::number(datetime.date().year()) + "-" +
1047 QString::number(datetime.date().month()) + "-" +
1048 QString::number(datetime.date().day()) + " " +
1049 QString::number(datetime.time().hour()) + ":" +
1050 QString::number(datetime.time().minute()) + ":" +
1051 QString::number(datetime.time().second()) + "." +
1052 QString::number(datetime.time().msec()).rightJustify(3, '0', TRUE) + "'";
1053 else
1054 return "NULL";
1055 }
1056 case QVariant::Time: {
1057 QTime time = field->value().toTime();
1058 if (time.isValid())
1059 return "'" + QString::number(time.hour()) + ":" +
1060 QString::number(time.minute()) + ":" +
1061 QString::number(time.second()) + "." +
1062 QString::number(time.msec()).rightJustify(3, '0', TRUE) + "'";
1063 else
1064 return "NULL";
1065 }
1066 case QVariant::Date: {
1067 QDate date = field->value().toDate();
1068 if (date.isValid())
1069 return "'" + QString::number(date.year()) + "-" +
1070 QString::number(date.month()) + "-" +
1071 QString::number(date.day()) + "'";
1072 else
1073 return "NULL";
1074 }
1075 default:
1076 return QSqlDriver::formatValue(field, trimStrings);
1077 }
1078}
Note: See TracBrowser for help on using the repository browser.