Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSql module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "qsql_symsql.h" |
43 | |
44 | |
45 | #define SYMBIAN_ENABLE_PUBLIC_PLATFORM_HEADER_SPLIT |
46 | |
47 | #include <qcoreapplication.h> |
48 | #include <qvariant.h> |
49 | #include <qsqlerror.h> |
50 | #include <qsqlfield.h> |
51 | #include <qsqlindex.h> |
52 | #include <qsqlquery.h> |
53 | #include <qstringlist.h> |
54 | #include <qvector.h> |
55 | #include <qdebug.h> |
56 | #include "../../../corelib/kernel/qcore_symbian_p.h" |
57 | |
58 | #if defined Q_OS_WIN |
59 | # include <qt_windows.h> |
60 | #else |
61 | # include <unistd.h> |
62 | #endif |
63 | |
64 | #include <sqldb.h> |
65 | #include <e32capability.h> |
66 | |
67 | const char* qCapabilityNames[ECapability_Limit] = |
68 | { |
69 | "TCB", |
70 | "CommDD", |
71 | "PowerMgmt", |
72 | "MultimediaDD", |
73 | "ReadDeviceData", |
74 | "WriteDeviceData", |
75 | "DRM", |
76 | "TrustedUI", |
77 | "ProtServ", |
78 | "DiskAdmin", |
79 | "NetworkControl", |
80 | "AllFiles", |
81 | "SwEvent", |
82 | "NetworkServices", |
83 | "LocalServices", |
84 | "ReadUserData", |
85 | "WriteUserData", |
86 | "Location", |
87 | "SurroundingsDD", |
88 | "UserEnvironment" |
89 | }; |
90 | |
91 | const char qCapabilityNone[] = "None"; |
92 | |
93 | |
94 | Q_DECLARE_METATYPE(RSqlDatabase) |
95 | Q_DECLARE_METATYPE(RSqlStatement) |
96 | |
97 | QT_BEGIN_NAMESPACE |
98 | |
99 | const QString valueSeparator(QLatin1String("=")); |
100 | const QString fieldSeparator(QLatin1String(",")); |
101 | |
102 | static QString _q_escapeIdentifier(const QString &identifier) |
103 | { |
104 | QString res = identifier; |
105 | if (!identifier.isEmpty() |
106 | && identifier.left(1) != QString(QLatin1Char( |
107 | && identifier.right(1) != QString(QLatin1Char( |
108 | res.replace(QLatin1Char("\"\"")); |
109 | res.prepend(QLatin1Char( |
110 | res.replace(QLatin1Char("\".\"")); |
111 | } |
112 | return res; |
113 | } |
114 | |
115 | static QVariant::Type qGetColumnType(const TSqlColumnType coltype) |
116 | { |
117 | switch(coltype){ |
118 | case ESqlInt: |
119 | case ESqlInt64: |
120 | return QVariant::Int; |
121 | case ESqlReal: |
122 | return QVariant::Double; |
123 | case ESqlBinary: |
124 | return QVariant::ByteArray; |
125 | case ESqlText: |
126 | case ESqlNull: |
127 | default: |
128 | return QVariant::String; |
129 | } |
130 | } |
131 | |
132 | static QVariant::Type qGetColumnType(const QString &tpName) |
133 | { |
134 | const QString typeName = tpName.toLower(); |
135 | |
136 | if (typeName == QLatin1String("integer") |
137 | || typeName == QLatin1String("int")) |
138 | return QVariant::Int; |
139 | if (typeName == QLatin1String("double") |
140 | || typeName == QLatin1String("float") |
141 | || typeName == QLatin1String("real") |
142 | || typeName.startsWith(QLatin1String("numeric"))) |
143 | return QVariant::Double; |
144 | if (typeName == QLatin1String("blob")) |
145 | return QVariant::ByteArray; |
146 | return QVariant::String; |
147 | } |
148 | |
149 | static QSqlError qMakeError(RSqlDatabase& access, |
150 | const QString &descr, |
151 | QSqlError::ErrorType type, |
152 | int errorCode = -1) |
153 | { |
154 | return QSqlError(descr, |
155 | QString::fromUtf16(static_cast<const ushort *>(access.LastErrorMessage().Ptr())), |
156 | type, |
157 | errorCode); |
158 | } |
159 | |
160 | |
161 | static QSqlError gMakeErrorOpen(const QString &descr, |
162 | QSqlError::ErrorType type, |
163 | TInt errorCode) |
164 | { |
165 | return QSqlError(descr, QLatin1String(""), type, errorCode); |
166 | } |
167 | |
168 | class QSymSQLDriverPrivate |
169 | { |
170 | public: |
171 | inline QSymSQLDriverPrivate() {} |
172 | RSqlDatabase access; |
173 | }; |
174 | |
175 | class QSymSQLResultPrivate |
176 | { |
177 | public: |
178 | QSymSQLResultPrivate(QSymSQLResult *res); |
179 | void cleanup(); |
180 | bool fetchNext(bool initialFetch); |
181 | // initializes the recordInfo |
182 | void initColumns(QSqlRecord& rec); |
183 | void finalize(); |
184 | |
185 | QSymSQLResult* q; |
186 | RSqlDatabase access; |
187 | RSqlStatement stmt; |
188 | bool skipRow; // skip the next fetchNext()? |
189 | bool skippedStatus; // the status of the fetchNext() that's skipped |
190 | bool prepareCalled; |
191 | }; |
192 | |
193 | QSymSQLResultPrivate::QSymSQLResultPrivate(QSymSQLResult* res) : q(res), |
194 | skipRow(false), |
195 | skippedStatus(false), |
196 | prepareCalled(false) |
197 | { |
198 | } |
199 | |
200 | void QSymSQLResultPrivate::cleanup() |
201 | { |
202 | finalize(); |
203 | skippedStatus = false; |
204 | skipRow = false; |
205 | q->setAt(QSql::BeforeFirstRow); |
206 | q->setActive(false); |
207 | } |
208 | |
209 | void QSymSQLResultPrivate::finalize() |
210 | { |
211 | prepareCalled = false; |
212 | stmt.Close(); |
213 | } |
214 | |
215 | void QSymSQLResultPrivate::initColumns(QSqlRecord& rec) |
216 | { |
217 | int nCols = stmt.ColumnCount(); |
218 | if (nCols <= 0) { |
219 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", |
220 | "Error retrieving column count"), QSqlError::UnknownError, nCols)); |
221 | return; |
222 | } |
223 | |
224 | for (int i = 0; i < nCols; ++i) { |
225 | TPtrC cName; |
226 | TInt err = stmt.ColumnName(i, cName); |
227 | |
228 | if (err != KErrNone) { |
229 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", |
230 | "Error retrieving column name"), QSqlError::UnknownError, err)); |
231 | return; |
232 | } |
233 | |
234 | QString colName = qt_TDesC2QString(cName); |
235 | |
236 | // must use typeName for resolving the type to match QSymSQLDriver::record |
237 | TPtrC tName; |
238 | TSqlColumnType decColType; |
239 | err = stmt.DeclaredColumnType(i, decColType); |
240 | |
241 | if (err != KErrNone) { |
242 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", |
243 | "Error retrieving column type"), QSqlError::UnknownError, err)); |
244 | return; |
245 | } |
246 | |
247 | int dotIdx = colName.lastIndexOf(QLatin1Char( |
248 | QSqlField fld(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), qGetColumnType(decColType)); |
249 | |
250 | rec.append(fld); |
251 | } |
252 | } |
253 | |
254 | bool QSymSQLResultPrivate::fetchNext(bool initialFetch) |
255 | { |
256 | int res; |
257 | |
258 | if (skipRow) { |
259 | // already fetched |
260 | Q_ASSERT(!initialFetch); |
261 | skipRow = false; |
262 | return skippedStatus; |
263 | } |
264 | |
265 | skipRow = initialFetch; |
266 | res = stmt.Next(); |
267 | |
268 | switch(res) { |
269 | case KSqlAtRow: |
270 | return true; |
271 | case KSqlAtEnd: |
272 | stmt.Reset(); |
273 | return false; |
274 | case KSqlErrGeneral: |
275 | // KSqlErrGeneral is a generic error code and we must call stmt.Reset() |
276 | // to get the specific error message. |
277 | stmt.Reset(); |
278 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", |
279 | "Unable to fetch row"), QSqlError::ConnectionError, res)); |
280 | q->setAt(QSql::AfterLastRow); |
281 | return false; |
282 | case KSqlErrMisuse: |
283 | case KSqlErrBusy: |
284 | default: |
285 | // something wrong, don't get col info, but still return false |
286 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSymSQLResult", |
287 | "Unable to fetch row"), QSqlError::ConnectionError, res)); |
288 | stmt.Reset(); |
289 | q->setAt(QSql::AfterLastRow); |
290 | return false; |
291 | } |
292 | return false; |
293 | } |
294 | |
295 | ////////////////////////////////// QSymSQLResult ///////////////////////////////////////////////// |
296 | |
297 | QSymSQLResult::QSymSQLResult(const QSymSQLDriver* db) |
298 | : QSqlResult(db) |
299 | { |
300 | d = new QSymSQLResultPrivate(this); |
301 | d->access = db->d->access; |
302 | } |
303 | |
304 | |
305 | QSymSQLResult::~QSymSQLResult() |
306 | { |
307 | d->cleanup(); |
308 | delete d; |
309 | } |
310 | |
311 | bool QSymSQLResult::reset(const QString &query) |
312 | { |
313 | if (!prepare(query)) |
314 | return false; |
315 | |
316 | return exec(); |
317 | } |
318 | |
319 | bool QSymSQLResult::prepare(const QString &query) |
320 | { |
321 | if (!driver() || !driver()->isOpen() || driver()->isOpenError()) |
322 | return false; |
323 | |
324 | d->cleanup(); |
325 | setSelect(false); |
326 | |
327 | TInt res = d->stmt.Prepare(d->access, qt_QString2TPtrC(query)); |
328 | |
329 | if (res != KErrNone) { |
330 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", |
331 | "Unable to execute statement"), QSqlError::StatementError, res)); |
332 | d->finalize(); |
333 | return false; |
334 | } |
335 | |
336 | d->prepareCalled = true; |
337 | |
338 | return true; |
339 | } |
340 | |
341 | bool QSymSQLResult::exec() |
342 | { |
343 | if (d->prepareCalled == false) { |
344 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", |
345 | "Statement is not prepared"), QSqlError::StatementError, KErrGeneral)); |
346 | return false; |
347 | } |
348 | |
349 | const QVector<QVariant> values = boundValues(); |
350 | |
351 | d->skippedStatus = false; |
352 | d->skipRow = false; |
353 | setAt(QSql::BeforeFirstRow); |
354 | setLastError(QSqlError()); |
355 | int res = d->stmt.Reset(); |
356 | |
357 | if (res != KErrNone) { |
358 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", |
359 | "Unable to reset statement"), QSqlError::StatementError, res)); |
360 | d->finalize(); |
361 | return false; |
362 | } |
363 | TPtrC tmp; |
364 | TInt paramCount = 0; |
365 | while (d->stmt.ParamName(paramCount, tmp) == KErrNone) |
366 | paramCount++; |
367 | |
368 | if (paramCount == values.count()) { |
369 | for (int i = 0; i < paramCount; ++i) { |
370 | res = KErrNone; |
371 | const QVariant value = values.at(i); |
372 | |
373 | if (value.isNull()) { |
374 | res = d->stmt.BindNull(i); //replaced i + 1 with i |
375 | } else { |
376 | switch (value.type()) { |
377 | case QVariant::ByteArray: { |
378 | const QByteArray *ba = static_cast<const QByteArray*>(value.constData()); |
379 | TPtrC8 data(reinterpret_cast<const TUint8 *>(ba->constData()), ba->length()); |
380 | res = d->stmt.BindBinary(i, data); //replaced i + 1 with i |
381 | break; } |
382 | case QVariant::Int: |
383 | res = d->stmt.BindInt(i, value.toInt()); //replaced i + 1 with i |
384 | break; |
385 | case QVariant::Double: |
386 | res = d->stmt.BindReal(i, value.toDouble()); //replaced i + 1 with i |
387 | |
388 | break; |
389 | case QVariant::UInt: |
390 | case QVariant::LongLong: |
391 | res = d->stmt.BindReal(i, value.toLongLong()); //replaced i + 1 with i |
392 | break; |
393 | |
394 | case QVariant::String: { |
395 | // lifetime of string == lifetime of its qvariant |
396 | const QString *str = static_cast<const QString*>(value.constData()); |
397 | res = d->stmt.BindText(i, qt_QString2TPtrC(*str)); // replaced i + 1 with i |
398 | break; } |
399 | default: { |
400 | QString str = value.toString(); |
401 | res = d->stmt.BindText(i, qt_QString2TPtrC(str)); //replaced i + 1 with i |
402 | break; } |
403 | } |
404 | } |
405 | if (res != KErrNone) { |
406 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSymSQLResult", |
407 | "Unable to bind parameters"), QSqlError::StatementError, res)); |
408 | d->finalize(); |
409 | return false; |
410 | } |
411 | } |
412 | } else { |
413 | setLastError(QSqlError(QCoreApplication::translate("QSymSQLResult", |
414 | "Parameter count mismatch"), QString(), QSqlError::StatementError)); |
415 | return false; |
416 | } |
417 | |
418 | d->skippedStatus = d->fetchNext(true); |
419 | |
420 | if (lastError().isValid()) { |
421 | setSelect(false); |
422 | setActive(false); |
423 | return false; |
424 | } |
425 | |
426 | if (d->stmt.ColumnCount() > 0) { |
427 | //If there is something, it has to be select |
428 | setSelect(true); |
429 | } else { |
430 | //If there isn't it might be just bad query, let's check manually whether we can find SELECT |
431 | QString query = this->lastQuery(); |
432 | query = query.trimmed(); |
433 | query = query.toLower(); |
434 | |
435 | //Just check whether there is one in the beginning, don't know if this is enough |
436 | //Comments should be at the end of line if those are passed |
437 | //For some reason, case insensitive indexOf didn't work for me |
438 | if (query.indexOf(QLatin1String("select")) == 0) { |
439 | setSelect(true); |
440 | } else { |
441 | setSelect(false); |
442 | } |
443 | } |
444 | |
445 | setActive(true); |
446 | return true; |
447 | } |
448 | |
449 | |
450 | int QSymSQLResult::size() |
451 | { |
452 | return -1; |
453 | } |
454 | |
455 | int QSymSQLResult::numRowsAffected() |
456 | { |
457 | return -1; |
458 | } |
459 | |
460 | QVariant QSymSQLResult::lastInsertId() const |
461 | { |
462 | if (isActive()) { |
463 | qint64 id = static_cast<qint64>(d->access.LastInsertedRowId()); |
464 | if (id) |
465 | return id; |
466 | } |
467 | |
468 | return QVariant(); |
469 | } |
470 | |
471 | QSqlRecord QSymSQLResult::record() const |
472 | { |
473 | if (!isActive() || !isSelect()) |
474 | return QSqlRecord(); |
475 | |
476 | QSqlRecord res; |
477 | d->initColumns(res); |
478 | |
479 | return res; |
480 | } |
481 | |
482 | QVariant QSymSQLResult::handle() const |
483 | { |
484 | return qVariantFromValue(d->stmt); |
485 | } |
486 | |
487 | |
488 | void QSymSQLResult::virtual_hook(int id, void *data) |
489 | { |
490 | switch (id) |
491 | { |
492 | case QSqlResult::DetachFromResultSet: |
493 | d->stmt.Reset(); |
494 | break; |
495 | default: |
496 | QSqlResult::virtual_hook(id, data); |
497 | } |
498 | } |
499 | |
500 | QVariant QSymSQLResult::data(int idx) |
501 | { |
502 | QVariant r; |
503 | |
504 | switch (d->stmt.ColumnType(idx)) { |
505 | case ESqlBinary: |
506 | { |
507 | TPtrC8 data; |
508 | d->stmt.ColumnBinary(idx, data); |
509 | return QByteArray(reinterpret_cast<const char *>(data.Ptr()), data.Length()); |
510 | break; |
511 | } |
512 | case ESqlInt: |
513 | r = QVariant(d->stmt.ColumnInt(idx)); |
514 | break; |
515 | case ESqlInt64: |
516 | r = QVariant(d->stmt.ColumnInt64(idx)); |
517 | break; |
518 | case ESqlReal: |
519 | switch(numericalPrecisionPolicy()) { |
520 | case QSql::LowPrecisionInt32: |
521 | r = QVariant(d->stmt.ColumnInt(idx)); |
522 | break; |
523 | case QSql::LowPrecisionInt64: |
524 | r = QVariant(d->stmt.ColumnInt64(idx)); |
525 | break; |
526 | case QSql::LowPrecisionDouble: |
527 | r = QVariant(d->stmt.ColumnReal(idx)); |
528 | break; |
529 | case QSql::HighPrecision: |
530 | default: |
531 | TPtrC res; |
532 | d->stmt.ColumnText(idx, res); |
533 | r = QVariant(qt_TDesC2QString(res)); |
534 | break; |
535 | }; |
536 | break; |
537 | case ESqlNull: |
538 | r = QVariant(QVariant::String); |
539 | break; |
540 | default: |
541 | TPtrC res; |
542 | d->stmt.ColumnText(idx, res); |
543 | r = QVariant(qt_TDesC2QString(res)); |
544 | break; |
545 | } |
546 | |
547 | return r; |
548 | } |
549 | |
550 | bool QSymSQLResult::isNull(int i) |
551 | { |
552 | return d->stmt.IsNull(i); |
553 | } |
554 | |
555 | bool QSymSQLResult::fetch(int i) |
556 | { |
557 | //Single return point modified according to review |
558 | bool retVal = true; |
559 | |
560 | if (i < 0 || !isActive()) { |
561 | retVal = false; |
562 | } else { |
563 | if (at() <= -1 || i < at()) { |
564 | d->stmt.Reset(); |
565 | setAt(-1); |
566 | d->skipRow = false; |
567 | } |
568 | |
569 | while (at() < i) { |
570 | if (!d->fetchNext(false)) { |
571 | retVal = false; |
572 | break; |
573 | } |
574 | |
575 | setAt(at() + 1); |
576 | } |
577 | } |
578 | |
579 | return retVal; |
580 | } |
581 | |
582 | bool QSymSQLResult::fetchNext() |
583 | { |
584 | bool res = d->fetchNext(false); |
585 | if (res) { |
586 | setAt(at()+1); |
587 | } |
588 | |
589 | return res; |
590 | } |
591 | |
592 | bool QSymSQLResult::fetchPrevious() |
593 | { |
594 | return QSqlResult::fetchPrevious(); |
595 | } |
596 | |
597 | bool QSymSQLResult::fetchFirst() |
598 | { |
599 | return fetch(0); |
600 | } |
601 | |
602 | bool QSymSQLResult::fetchLast() |
603 | { |
604 | if (!isActive()) |
605 | return false; |
606 | |
607 | if (at() <= -1) { |
608 | if (!fetchFirst()) |
609 | return false; |
610 | } |
611 | |
612 | TInt res; |
613 | |
614 | do { |
615 | res = d->stmt.Next(); |
616 | setAt(at()+1); |
617 | } while (res == KSqlAtRow); |
618 | |
619 | if (res != KSqlAtEnd) |
620 | return false; |
621 | |
622 | d->skippedStatus = false; |
623 | d->skipRow = false; |
624 | |
625 | return fetchPrevious(); |
626 | } |
627 | ////////////////////////////////// QSymSQLDriver ////////////////////////////////////////// |
628 | |
629 | QSymSQLDriver::QSymSQLDriver(QObject * parent) |
630 | : QSqlDriver(parent) |
631 | { |
632 | d = new QSymSQLDriverPrivate(); |
633 | } |
634 | |
635 | QSymSQLDriver::QSymSQLDriver(RSqlDatabase& connection, QObject *parent) |
636 | : QSqlDriver(parent) |
637 | { |
638 | d = new QSymSQLDriverPrivate(); |
639 | d->access = connection; |
640 | setOpen(true); |
641 | setOpenError(false); |
642 | } |
643 | |
644 | |
645 | QSymSQLDriver::~QSymSQLDriver() |
646 | { |
647 | d->access.Close(); |
648 | delete d; |
649 | } |
650 | |
651 | bool QSymSQLDriver::hasFeature(DriverFeature f) const |
652 | { |
653 | switch (f) { |
654 | case BLOB: |
655 | case Transactions: |
656 | case Unicode: |
657 | case PreparedQueries: |
658 | case PositionalPlaceholders: |
659 | case SimpleLocking: |
660 | case FinishQuery: |
661 | case LowPrecisionNumbers: |
662 | case LastInsertId: |
663 | case NamedPlaceholders: |
664 | return true; |
665 | case QuerySize: |
666 | case BatchOperations: |
667 | case EventNotifications: |
668 | case MultipleResultSets: |
669 | return false; |
670 | } |
671 | return false; |
672 | } |
673 | |
674 | /*! |
675 | Converts capability string to TCapability |
676 | */ |
677 | TCapability qMatchCapStr(QString& str) |
678 | { |
679 | TCapability cap = ECapability_HardLimit; |
680 | |
681 | for (int i = 0; i < static_cast<int>(ECapability_Limit); i++) { |
682 | if (str.compare(QLatin1String(qCapabilityNames[i]), Qt::CaseInsensitive) == 0) { |
683 | cap = static_cast<TCapability>(i); |
684 | break; |
685 | } |
686 | } |
687 | |
688 | //Special case, we allow ECapability_None to be defined |
689 | if (cap == ECapability_HardLimit |
690 | && str.compare(QLatin1String(qCapabilityNone), Qt::CaseInsensitive) == 0) |
691 | cap = ECapability_None; |
692 | |
693 | return cap; |
694 | } |
695 | |
696 | bool qExtractSecurityPolicyFromString(const QString &string, TSecurityPolicy &policy) |
697 | { |
698 | int startPos = string.indexOf(QLatin1Char( |
699 | QStringList values; |
700 | bool ret = false; |
701 | |
702 | if (startPos == -1) { |
703 | values = string.split(QLatin1Char( |
704 | } else { |
705 | values = string.mid(startPos + 1).split(QLatin1Char( |
706 | } |
707 | |
708 | if (values.count() > 0) { |
709 | const QString findVid(QLatin1String("vid[")); |
710 | const QString findSid(QLatin1String("sid[")); |
711 | const int MaxCapCount = 7; |
712 | const int VidMaxCount = 3; |
713 | const int SidMaxCount = 3; |
714 | |
715 | TCapability capList[MaxCapCount] = { ECapability_None,ECapability_None,ECapability_None, |
716 | ECapability_None,ECapability_None,ECapability_None,ECapability_None }; |
717 | |
718 | bool isVID = false; |
719 | bool isSID = false; |
720 | |
721 | QString idString(QLatin1String("")); |
722 | int maxAllowed = MaxCapCount; |
723 | |
724 | if (values[0].contains(findVid, Qt::CaseInsensitive)) { |
725 | idString = values[0].remove(findVid, Qt::CaseInsensitive); |
726 | idString = idString.remove(QLatin1Char( |
727 | values.removeAt(0); |
728 | isVID = true; |
729 | maxAllowed = VidMaxCount; |
730 | |
731 | } else if (values[0].contains(findSid, Qt::CaseInsensitive)) { |
732 | idString = values[0].remove(findSid, Qt::CaseInsensitive); |
733 | idString = idString.remove(QLatin1Char( |
734 | values.removeAt(0); |
735 | isSID = true; |
736 | maxAllowed = SidMaxCount; |
737 | } |
738 | |
739 | if (values.count() <= maxAllowed) { |
740 | bool wasSuccesful = true; |
741 | |
742 | for (int i = 0; i < values.count(); i++) { |
743 | capList[i] = qMatchCapStr(values[i]); |
744 | |
745 | if (capList[i] == ECapability_HardLimit) { |
746 | wasSuccesful = false; |
747 | break; |
748 | } |
749 | } |
750 | |
751 | if (wasSuccesful) { |
752 | if (isVID || isSID){ |
753 | bool ok = true; |
754 | quint32 id = idString.toUInt(&ok, 16); |
755 | |
756 | if (ok) { |
757 | if (isVID) { |
758 | TVendorId vid(id); |
759 | policy = TSecurityPolicy(vid, capList[0], capList[1], capList[2]); |
760 | } else { |
761 | TSecureId sid(id); |
762 | policy = TSecurityPolicy(sid, capList[0], capList[1], capList[2]); |
763 | } |
764 | |
765 | ret = true; //Everything is fine |
766 | } |
767 | } else { |
768 | policy = TSecurityPolicy(capList[0], capList[1], capList[2], capList[3], |
769 | capList[4], capList[5], capList[6]); |
770 | |
771 | ret = true; //Everything is fine |
772 | } |
773 | } |
774 | } |
775 | } |
776 | |
777 | return ret; |
778 | } |
779 | |
780 | /*! |
781 | Opens the database connection using the given connection options. Returns true on success; otherwise returns false. |
782 | Error information can be retrieved using the lastError() function. Symbian SQL dbs have no \a user, \a password, \a host |
783 | or \a port just file names. |
784 | |
785 | \a connOpts Connection options hold definition for security policies and all parameters that does not contain "POLICY_" will be |
786 | passed to RSqlDatabase. Policy will be filled according to parsed values. |
787 | |
788 | Value in database wide parameters starts by definition which can be vendorId or secureId. These come directly from TSecurityPolicy class in Symbian. |
789 | |
790 | POLICY_DB_DEFAULT |
791 | Default security policy which will be used for the database and all database objects. POLICY_DB_DEFAULT must be |
792 | defined before any other policy definitions can be used. |
793 | POLICY_DB_READ |
794 | Read database security policy. An application with read database security policy can read from database. |
795 | POLICY_DB_WRITE: |
796 | Write database security policy. An application with write database security policy can write to database. |
797 | POLICY_DB_SCHEMA: |
798 | Schema database security policy. An application with schema database security policy can modify |
799 | the database schema, write to database, read from database. |
800 | |
801 | Format: |
802 | POLICY_DB_DEFAULT=cap1,cap2,cap3,cap4,cap5,cap6,cap7 (Up to 7 capabilities) |
803 | POLICY_DB_READ=cap1,cap2,cap3,cap4,cap5,cap6,cap7 (Up to 7 capabilities) |
804 | POLICY_DB_WRITE=vendorid,cap1,cap2,cap3 (Vendor ID and up to 3 capabilities) |
805 | POLICY_DB_SCHEMA=secureid,cap1,cap2,cap3 (Secure ID and up to 3 capabilities) |
806 | |
807 | Table policies does not support schema policy as database level does. |
808 | |
809 | Table specific parameters would be as: |
810 | POLICY_TABLE_WRITE=tablename,cap1,cap2,cap3,cap4,cap5,cap6,cap7 |
811 | POLICY_TABLE_READ=tablename,cap1,cap2,cap3,cap4,cap5,cap6,cap7 |
812 | |
813 | Vendor Id and Secure id format: |
814 | vid[0x12345678] (Hex) |
815 | sid[0x12345678] (Hex) |
816 | |
817 | Example: |
818 | \code |
819 | QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); |
820 | database.setConnectOptions("POLICY_DB_DEFAULT=ReadDeviceData"); |
821 | database.setDatabaseName("[12345678]myDatabase"); |
822 | bool ok = database.open(); |
823 | \encode |
824 | |
825 | \code |
826 | QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); |
827 | database.setConnectOptions("POLICY_DB_DEFAULT=None; POLICY_DB_WRITE=sid[0x12345678], WriteDeviceData"); |
828 | database.setDatabaseName("[12345678]myDatabase"); |
829 | bool ok = database.open(); |
830 | \encode |
831 | |
832 | FOREIGN KEY: |
833 | Enabling foreign key support from underlying SQLite |
834 | add: "foreign_keys = ON" to your connection options string. This will be passes to SQLite. |
835 | |
836 | Foreign key Example: |
837 | \code |
838 | QSqlDatabase database = QSqlDatabase::addDatabase("QSYMSQL", "MyConnection"); |
839 | database.setDatabaseName("[12345678]myDatabase"); |
840 | database.setConnectOptions("foreign_keys = ON"); |
841 | bool ok = database.open(); |
842 | \encode |
843 | |
844 | More information about Symbian Security Policy can be found from Symbian documentation. |
845 | |
846 | */ |
847 | bool QSymSQLDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) |
848 | { |
849 | if (isOpen()) |
850 | close(); |
851 | if (db.isEmpty()) |
852 | return false; |
853 | |
854 | //Separating our parameters from Symbian ones and construct new connection options |
855 | const QString itemSeparator(QLatin1String(";")); |
856 | QRegExp isOurOption(QLatin1String("POLICY_*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
857 | |
858 | QStringList optionList = conOpts.split(itemSeparator, QString::SkipEmptyParts); |
859 | QStringList symbianList; |
860 | |
861 | for (int i = optionList.count() - 1; i >= 0; i--) { |
862 | if (!optionList[i].contains(isOurOption)) { |
863 | symbianList.append(optionList[i]); |
864 | optionList.removeAt(i); |
865 | } else { |
866 | //Removing whitespace |
867 | QString formatted = optionList[i]; |
868 | formatted = formatted.remove(QLatin1Char( |
869 | formatted = formatted.remove(QLatin1Char( |
870 | formatted = formatted.remove(QLatin1Char( |
871 | formatted = formatted.remove(QLatin1Char( |
872 | optionList[i] = formatted; |
873 | } |
874 | } |
875 | |
876 | QString symbianOpt; |
877 | |
878 | for (int i = 0; i < symbianList.count(); i++) { |
879 | symbianOpt += symbianList[i]; |
880 | symbianOpt += itemSeparator; |
881 | } |
882 | |
883 | TPtrC dbName(qt_QString2TPtrC(db)); |
884 | QByteArray conOpts8 = symbianOpt.toUtf8(); |
885 | const TPtrC8 config(reinterpret_cast<const TUint8*>(conOpts8.constData()), (conOpts8.length())); |
886 | |
887 | TInt res = d->access.Open(dbName, &config); |
888 | |
889 | if (res == KErrNotFound) { |
890 | |
891 | QRegExp findDefault(QLatin1String("POLICY_DB_DEFAULT=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
892 | QRegExp findRead(QLatin1String("POLICY_DB_READ=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
893 | QRegExp findWrite(QLatin1String("POLICY_DB_WRITE=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
894 | QRegExp findSchema(QLatin1String("POLICY_DB_SCHEMA=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
895 | QRegExp findTableRead(QLatin1String("POLICY_TABLE_READ=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
896 | QRegExp findTableWrite(QLatin1String("POLICY_TABLE_WRITE=*"), Qt::CaseInsensitive, QRegExp::Wildcard); |
897 | |
898 | int policyIndex = optionList.indexOf(findDefault); |
899 | |
900 | if (policyIndex != -1) { |
901 | QString defaultPolicyString = optionList[policyIndex]; |
902 | optionList.removeAt(policyIndex); |
903 | |
904 | TSecurityPolicy policyItem; |
905 | |
906 | if (qExtractSecurityPolicyFromString(defaultPolicyString, policyItem)) { |
907 | RSqlSecurityPolicy policy; |
908 | res = policy.Create(policyItem); |
909 | |
910 | if (res == KErrNone) { |
911 | for (int i = 0; i < optionList.count(); i++) { |
912 | QString option = optionList[i]; |
913 | |
914 | if (option.contains(findRead)) { |
915 | if (qExtractSecurityPolicyFromString(option, policyItem)) { |
916 | res = policy.SetDbPolicy(RSqlSecurityPolicy::EReadPolicy, policyItem); |
917 | } else { |
918 | res = KErrArgument; |
919 | } |
920 | } else if (option.contains(findWrite)) { |
921 | if (qExtractSecurityPolicyFromString(option, policyItem)) { |
922 | res = policy.SetDbPolicy(RSqlSecurityPolicy::EWritePolicy, policyItem); |
923 | } else { |
924 | res = KErrArgument; |
925 | } |
926 | } else if (option.contains(findSchema)) { |
927 | if (qExtractSecurityPolicyFromString(option, policyItem)) { |
928 | res = policy.SetDbPolicy(RSqlSecurityPolicy::ESchemaPolicy, policyItem); |
929 | } else { |
930 | res = KErrArgument; |
931 | } |
932 | } else if (option.contains(findTableWrite)) { |
933 | QString tableOption = option.mid(option.indexOf(QLatin1Char( |
934 | int firstComma = tableOption.indexOf(QLatin1Char( |
935 | |
936 | if (firstComma != -1) { |
937 | QString tableName = tableOption.left(firstComma); |
938 | tableOption = tableOption.mid(firstComma + 1); |
939 | |
940 | if (qExtractSecurityPolicyFromString(tableOption, policyItem)) { |
941 | TPtrC symTableName(qt_QString2TPtrC(tableName)); |
942 | |
943 | res = policy.SetPolicy(RSqlSecurityPolicy::ETable, symTableName, |
944 | RSqlSecurityPolicy::EWritePolicy, policyItem); |
945 | } else { |
946 | res = KErrArgument; |
947 | } |
948 | } else { |
949 | res = KErrArgument; |
950 | } |
951 | } else if (option.contains(findTableRead)) { |
952 | QString tableOption = option.mid(option.indexOf(QLatin1Char( |
953 | int firstComma = tableOption.indexOf(QLatin1Char( |
954 | |
955 | if (firstComma != -1) { |
956 | QString tableName = tableOption.left(firstComma); |
957 | tableOption = tableOption.mid(firstComma + 1); |
958 | |
959 | if (qExtractSecurityPolicyFromString(tableOption, policyItem)) { |
960 | TPtrC symTableName(qt_QString2TPtrC(tableName)); |
961 | |
962 | res = policy.SetPolicy(RSqlSecurityPolicy::ETable, symTableName, |
963 | RSqlSecurityPolicy::EReadPolicy, policyItem); |
964 | } else { |
965 | res = KErrArgument; |
966 | } |
967 | } else { |
968 | res = KErrArgument; |
969 | } |
970 | } else { |
971 | res = KErrArgument; |
972 | } |
973 | |
974 | if (res != KErrNone) { |
975 | setLastError(gMakeErrorOpen(tr("Invalid option: ") + option, QSqlError::ConnectionError, res)); |
976 | break; |
977 | } |
978 | } |
979 | |
980 | if (res == KErrNone) { |
981 | res = d->access.Create(dbName, policy, &config); |
982 | policy.Close(); |
983 | |
984 | if (res != KErrNone) |
985 | setLastError(gMakeErrorOpen(tr("Error opening database"), QSqlError::ConnectionError, res)); |
986 | } |
987 | } |
988 | |
989 | } else { |
990 | res = KErrArgument; |
991 | setLastError(gMakeErrorOpen(tr("Invalid option: ") + defaultPolicyString, QSqlError::ConnectionError, res)); |
992 | } |
993 | |
994 | } else { |
995 | //Check whether there is some of our options, fail if so. |
996 | policyIndex = optionList.indexOf(isOurOption); |
997 | |
998 | if (policyIndex == -1) { |
999 | res = d->access.Create(dbName, &config); |
1000 | |
1001 | if (res != KErrNone) |
1002 | setLastError(gMakeErrorOpen(tr("Error opening database"), QSqlError::ConnectionError, res)); |
1003 | } else { |
1004 | res = KErrArgument; |
1005 | setLastError(gMakeErrorOpen(tr("POLICY_DB_DEFAULT must be defined before any other POLICY definitions can be used"), QSqlError::ConnectionError, res)); |
1006 | } |
1007 | } |
1008 | } |
1009 | |
1010 | if (res == KErrNone) { |
1011 | setOpen(true); |
1012 | setOpenError(false); |
1013 | return true; |
1014 | } else { |
1015 | setOpenError(true); |
1016 | return false; |
1017 | } |
1018 | } |
1019 | |
1020 | void QSymSQLDriver::close() |
1021 | { |
1022 | if (isOpen()) { |
1023 | d->access.Close(); |
1024 | setOpen(false); |
1025 | setOpenError(false); |
1026 | } |
1027 | } |
1028 | |
1029 | QSqlResult *QSymSQLDriver::createResult() const |
1030 | { |
1031 | return new QSymSQLResult(this); |
1032 | } |
1033 | |
1034 | bool QSymSQLDriver::beginTransaction() |
1035 | { |
1036 | if (!isOpen() || isOpenError()) |
1037 | return false; |
1038 | |
1039 | TInt err = d->access.Exec(_L("BEGIN")); |
1040 | if (err < KErrNone) { |
1041 | setLastError(QSqlError(tr("Unable to begin transaction"), |
1042 | qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); |
1043 | return false; |
1044 | } |
1045 | |
1046 | return true; |
1047 | } |
1048 | |
1049 | bool QSymSQLDriver::commitTransaction() |
1050 | { |
1051 | if (!isOpen() || isOpenError()) |
1052 | return false; |
1053 | |
1054 | TInt err = d->access.Exec(_L("COMMIT")); |
1055 | if (err < KErrNone) { |
1056 | setLastError(QSqlError(tr("Unable to commit transaction"), |
1057 | qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); |
1058 | return false; |
1059 | } |
1060 | |
1061 | return true; |
1062 | } |
1063 | |
1064 | bool QSymSQLDriver::rollbackTransaction() |
1065 | { |
1066 | if (!isOpen() || isOpenError()) |
1067 | return false; |
1068 | |
1069 | TInt err = d->access.Exec(_L("ROLLBACK")); |
1070 | if (err < KErrNone) { |
1071 | setLastError(QSqlError(tr("Unable to rollback transaction"), |
1072 | qt_TDesC2QString(d->access.LastErrorMessage()), QSqlError::TransactionError, err)); |
1073 | return false; |
1074 | } |
1075 | |
1076 | return true; |
1077 | } |
1078 | |
1079 | QStringList QSymSQLDriver::tables(QSql::TableType type) const |
1080 | { |
1081 | QStringList res; |
1082 | if (!isOpen()) |
1083 | return res; |
1084 | |
1085 | QSqlQuery q(createResult()); |
1086 | q.setForwardOnly(true); |
1087 | |
1088 | QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " |
1089 | "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); |
1090 | if ((type & QSql::Tables) && (type & QSql::Views)) |
1091 | sql = sql.arg(QLatin1String("type='table' OR type='view'")); |
1092 | else if (type & QSql::Tables) |
1093 | sql = sql.arg(QLatin1String("type='table'")); |
1094 | else if (type & QSql::Views) |
1095 | sql = sql.arg(QLatin1String("type='view'")); |
1096 | else |
1097 | sql.clear(); |
1098 | |
1099 | if (!sql.isEmpty() && q.exec(sql)) { |
1100 | while (q.next()) |
1101 | res.append(q.value(0).toString()); |
1102 | } |
1103 | |
1104 | if (type & QSql::SystemTables) |
1105 | // there are no internal tables beside this one: |
1106 | res.append(QLatin1String("sqlite_master")); |
1107 | |
1108 | return res; |
1109 | } |
1110 | |
1111 | static QSqlIndex qGetTableInfo(QSqlQuery &q, QString &tableName, bool onlyPIndex = false) |
1112 | { |
1113 | QString dbName; |
1114 | QString table(tableName); |
1115 | int indexOfSeparator = tableName.indexOf(QLatin1Char( |
1116 | if (indexOfSeparator > -1) { |
1117 | dbName = tableName.left(indexOfSeparator +1 ); |
1118 | table = tableName.mid(indexOfSeparator + 1); |
1119 | } |
1120 | q.exec(QLatin1String("PRAGMA ") + dbName + QLatin1String( "table_info (") + _q_escapeIdentifier(table) + QLatin1String( ")")); |
1121 | |
1122 | const int NAME_IDX = 1; |
1123 | const int TYPE_IDX = 2; |
1124 | const int NOTNULL_IDX = 3; |
1125 | const int DFLT_VALUE_IDX = 4; |
1126 | const int PK_IDX = 5; |
1127 | |
1128 | QSqlIndex ind; |
1129 | while (q.next()) { |
1130 | bool isPk = q.value(PK_IDX).toInt(); |
1131 | if (onlyPIndex && !isPk) |
1132 | continue; |
1133 | QString typeName = q.value(TYPE_IDX).toString().toLower(); |
1134 | QSqlField fld(q.value(NAME_IDX).toString(), qGetColumnType(typeName)); |
1135 | if (isPk && (typeName == QLatin1String("integer"))) |
1136 | // INTEGER PRIMARY KEY fields are auto-generated in sqlite |
1137 | // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! |
1138 | fld.setAutoValue(true); |
1139 | fld.setRequired(q.value(NOTNULL_IDX).toInt() != 0); |
1140 | fld.setDefaultValue(q.value(DFLT_VALUE_IDX)); |
1141 | ind.append(fld); |
1142 | } |
1143 | return ind; |
1144 | } |
1145 | |
1146 | QSqlIndex QSymSQLDriver::primaryIndex(const QString &tblname) const |
1147 | { |
1148 | if (!isOpen()) |
1149 | return QSqlIndex(); |
1150 | |
1151 | QString table = tblname; |
1152 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
1153 | table = stripDelimiters(table, QSqlDriver::TableName); |
1154 | |
1155 | QSqlQuery q(createResult()); |
1156 | q.setForwardOnly(true); |
1157 | return qGetTableInfo(q, table, true); |
1158 | } |
1159 | |
1160 | QSqlRecord QSymSQLDriver::record(const QString &tbl) const |
1161 | { |
1162 | if (!isOpen()) |
1163 | return QSqlRecord(); |
1164 | |
1165 | QString table = tbl; |
1166 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) |
1167 | table = stripDelimiters(table, QSqlDriver::TableName); |
1168 | |
1169 | QSqlQuery q(createResult()); |
1170 | q.setForwardOnly(true); |
1171 | return qGetTableInfo(q, table); |
1172 | } |
1173 | |
1174 | QVariant QSymSQLDriver::handle() const |
1175 | { |
1176 | return qVariantFromValue(d->access); |
1177 | } |
1178 | |
1179 | QString QSymSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const |
1180 | { |
1181 | Q_UNUSED(type); |
1182 | return _q_escapeIdentifier(identifier); |
1183 | } |
1184 | |
1185 | QT_END_NAMESPACE |
1186 |
Warning: That file was not part of the compilation database. It may have many parsing errors.