1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qsqlresult.h" |
5 | |
6 | #include "qlist.h" |
7 | #include "qsqldriver.h" |
8 | #include "qsqlerror.h" |
9 | #include "qsqlfield.h" |
10 | #include "qsqlrecord.h" |
11 | #include "qsqlresult_p.h" |
12 | #include "quuid.h" |
13 | #include "qvariant.h" |
14 | #include "qdatetime.h" |
15 | #include "private/qsqldriver_p.h" |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | using namespace Qt::StringLiterals; |
20 | |
21 | QString QSqlResultPrivate::holderAt(int index) const |
22 | { |
23 | return holders.size() > index ? holders.at(i: index).holderName : fieldSerial(index); |
24 | } |
25 | |
26 | QString QSqlResultPrivate::fieldSerial(qsizetype i) const |
27 | { |
28 | return QString(":%1"_L1 ).arg(a: i); |
29 | } |
30 | |
31 | static bool qIsAlnum(QChar ch) |
32 | { |
33 | uint u = uint(ch.unicode()); |
34 | // matches [a-zA-Z0-9_] |
35 | return u - 'a' < 26 || u - 'A' < 26 || u - '0' < 10 || u == '_'; |
36 | } |
37 | |
38 | QString QSqlResultPrivate::positionalToNamedBinding(const QString &query) const |
39 | { |
40 | const qsizetype n = query.size(); |
41 | |
42 | QString result; |
43 | result.reserve(asize: n * 5 / 4); |
44 | QChar closingQuote; |
45 | qsizetype count = 0; |
46 | bool ignoreBraces = (sqldriver->dbmsType() == QSqlDriver::PostgreSQL); |
47 | |
48 | for (qsizetype i = 0; i < n; ++i) { |
49 | QChar ch = query.at(i); |
50 | if (!closingQuote.isNull()) { |
51 | if (ch == closingQuote) { |
52 | if (closingQuote == u']' |
53 | && i + 1 < n && query.at(i: i + 1) == closingQuote) { |
54 | // consume the extra character. don't close. |
55 | ++i; |
56 | result += ch; |
57 | } else { |
58 | closingQuote = QChar(); |
59 | } |
60 | } |
61 | result += ch; |
62 | } else { |
63 | if (ch == u'?') { |
64 | result += fieldSerial(i: count++); |
65 | } else { |
66 | if (ch == u'\'' || ch == u'"' || ch == u'`') |
67 | closingQuote = ch; |
68 | else if (!ignoreBraces && ch == u'[') |
69 | closingQuote = u']'; |
70 | result += ch; |
71 | } |
72 | } |
73 | } |
74 | result.squeeze(); |
75 | return result; |
76 | } |
77 | |
78 | QString QSqlResultPrivate::namedToPositionalBinding(const QString &query) |
79 | { |
80 | // In the Interbase case if it is an EXECUTE BLOCK then it is up to the |
81 | // caller to make sure that it is not using named bindings for the wrong |
82 | // parts of the query since Interbase uses them literally |
83 | if (sqldriver->dbmsType() == QSqlDriver::Interbase && |
84 | query.trimmed().startsWith(s: "EXECUTE BLOCK"_L1 , cs: Qt::CaseInsensitive)) |
85 | return query; |
86 | |
87 | const qsizetype n = query.size(); |
88 | |
89 | QString result; |
90 | result.reserve(asize: n); |
91 | QChar closingQuote; |
92 | int count = 0; |
93 | qsizetype i = 0; |
94 | bool ignoreBraces = (sqldriver->dbmsType() == QSqlDriver::PostgreSQL); |
95 | |
96 | while (i < n) { |
97 | QChar ch = query.at(i); |
98 | if (!closingQuote.isNull()) { |
99 | if (ch == closingQuote) { |
100 | if (closingQuote == u']' |
101 | && i + 1 < n && query.at(i: i + 1) == closingQuote) { |
102 | // consume the extra character. don't close. |
103 | ++i; |
104 | result += ch; |
105 | } else { |
106 | closingQuote = QChar(); |
107 | } |
108 | } |
109 | result += ch; |
110 | ++i; |
111 | } else { |
112 | if (ch == u':' |
113 | && (i == 0 || query.at(i: i - 1) != u':') |
114 | && (i + 1 < n && qIsAlnum(ch: query.at(i: i + 1)))) { |
115 | int pos = i + 2; |
116 | while (pos < n && qIsAlnum(ch: query.at(i: pos))) |
117 | ++pos; |
118 | QString holder(query.mid(position: i, n: pos - i)); |
119 | indexes[holder].append(t: count++); |
120 | holders.append(t: QHolder(holder, i)); |
121 | result += u'?'; |
122 | i = pos; |
123 | } else { |
124 | if (ch == u'\'' || ch == u'"' || ch == u'`') |
125 | closingQuote = ch; |
126 | else if (!ignoreBraces && ch == u'[') |
127 | closingQuote = u']'; |
128 | result += ch; |
129 | ++i; |
130 | } |
131 | } |
132 | } |
133 | result.squeeze(); |
134 | values.resize(size: holders.size()); |
135 | return result; |
136 | } |
137 | |
138 | /*! |
139 | \class QSqlResult |
140 | \brief The QSqlResult class provides an abstract interface for |
141 | accessing data from specific SQL databases. |
142 | |
143 | \ingroup database |
144 | \inmodule QtSql |
145 | |
146 | Normally, you would use QSqlQuery instead of QSqlResult, since |
147 | QSqlQuery provides a generic wrapper for database-specific |
148 | implementations of QSqlResult. |
149 | |
150 | If you are implementing your own SQL driver (by subclassing |
151 | QSqlDriver), you will need to provide your own QSqlResult |
152 | subclass that implements all the pure virtual functions and other |
153 | virtual functions that you need. |
154 | |
155 | \sa QSqlDriver |
156 | */ |
157 | |
158 | /*! |
159 | \enum QSqlResult::BindingSyntax |
160 | |
161 | This enum type specifies the different syntaxes for specifying |
162 | placeholders in prepared queries. |
163 | |
164 | \value PositionalBinding Use the ODBC-style positional syntax, with "?" as placeholders. |
165 | \value NamedBinding Use the Oracle-style syntax with named placeholders (e.g., ":id") |
166 | |
167 | \sa bindingSyntax() |
168 | */ |
169 | |
170 | /*! |
171 | \enum QSqlResult::VirtualHookOperation |
172 | \internal |
173 | */ |
174 | |
175 | /*! |
176 | Creates a QSqlResult using database driver \a db. The object is |
177 | initialized to an inactive state. |
178 | |
179 | \sa isActive(), driver() |
180 | */ |
181 | |
182 | QSqlResult::QSqlResult(const QSqlDriver *db) |
183 | { |
184 | d_ptr = new QSqlResultPrivate(this, db); |
185 | Q_D(QSqlResult); |
186 | if (d->sqldriver) |
187 | setNumericalPrecisionPolicy(d->sqldriver->numericalPrecisionPolicy()); |
188 | } |
189 | |
190 | /*! \internal |
191 | */ |
192 | QSqlResult::QSqlResult(QSqlResultPrivate &dd) |
193 | : d_ptr(&dd) |
194 | { |
195 | Q_D(QSqlResult); |
196 | if (d->sqldriver) |
197 | setNumericalPrecisionPolicy(d->sqldriver->numericalPrecisionPolicy()); |
198 | } |
199 | |
200 | /*! |
201 | Destroys the object and frees any allocated resources. |
202 | */ |
203 | |
204 | QSqlResult::~QSqlResult() |
205 | { |
206 | Q_D(QSqlResult); |
207 | delete d; |
208 | } |
209 | |
210 | /*! |
211 | Sets the current query for the result to \a query. You must call |
212 | reset() to execute the query on the database. |
213 | |
214 | \sa reset(), lastQuery() |
215 | */ |
216 | |
217 | void QSqlResult::setQuery(const QString& query) |
218 | { |
219 | Q_D(QSqlResult); |
220 | d->sql = query; |
221 | } |
222 | |
223 | /*! |
224 | Returns the current SQL query text, or an empty string if there |
225 | isn't one. |
226 | |
227 | \sa setQuery() |
228 | */ |
229 | |
230 | QString QSqlResult::lastQuery() const |
231 | { |
232 | Q_D(const QSqlResult); |
233 | return d->sql; |
234 | } |
235 | |
236 | /*! |
237 | Returns the current (zero-based) row position of the result. May |
238 | return the special values QSql::BeforeFirstRow or |
239 | QSql::AfterLastRow. |
240 | |
241 | \sa setAt(), isValid() |
242 | */ |
243 | int QSqlResult::at() const |
244 | { |
245 | Q_D(const QSqlResult); |
246 | return d->idx; |
247 | } |
248 | |
249 | |
250 | /*! |
251 | Returns \c true if the result is positioned on a valid record (that |
252 | is, the result is not positioned before the first or after the |
253 | last record); otherwise returns \c false. |
254 | |
255 | \sa at() |
256 | */ |
257 | |
258 | bool QSqlResult::isValid() const |
259 | { |
260 | Q_D(const QSqlResult); |
261 | return d->idx != QSql::BeforeFirstRow && d->idx != QSql::AfterLastRow; |
262 | } |
263 | |
264 | /*! |
265 | \fn bool QSqlResult::isNull(int index) |
266 | |
267 | Returns \c true if the field at position \a index in the current row |
268 | is null; otherwise returns \c false. |
269 | */ |
270 | |
271 | /*! |
272 | Returns \c true if the result has records to be retrieved; otherwise |
273 | returns \c false. |
274 | */ |
275 | |
276 | bool QSqlResult::isActive() const |
277 | { |
278 | Q_D(const QSqlResult); |
279 | return d->active; |
280 | } |
281 | |
282 | /*! |
283 | This function is provided for derived classes to set the |
284 | internal (zero-based) row position to \a index. |
285 | |
286 | \sa at() |
287 | */ |
288 | |
289 | void QSqlResult::setAt(int index) |
290 | { |
291 | Q_D(QSqlResult); |
292 | d->idx = index; |
293 | } |
294 | |
295 | |
296 | /*! |
297 | This function is provided for derived classes to indicate whether |
298 | or not the current statement is a SQL \c SELECT statement. The \a |
299 | select parameter should be true if the statement is a \c SELECT |
300 | statement; otherwise it should be false. |
301 | |
302 | \sa isSelect() |
303 | */ |
304 | |
305 | void QSqlResult::setSelect(bool select) |
306 | { |
307 | Q_D(QSqlResult); |
308 | d->isSel = select; |
309 | } |
310 | |
311 | /*! |
312 | Returns \c true if the current result is from a \c SELECT statement; |
313 | otherwise returns \c false. |
314 | |
315 | \sa setSelect() |
316 | */ |
317 | |
318 | bool QSqlResult::isSelect() const |
319 | { |
320 | Q_D(const QSqlResult); |
321 | return d->isSel; |
322 | } |
323 | |
324 | /*! |
325 | Returns the driver associated with the result. This is the object |
326 | that was passed to the constructor. |
327 | */ |
328 | |
329 | const QSqlDriver *QSqlResult::driver() const |
330 | { |
331 | Q_D(const QSqlResult); |
332 | return d->sqldriver; |
333 | } |
334 | |
335 | |
336 | /*! |
337 | This function is provided for derived classes to set the internal |
338 | active state to \a active. |
339 | |
340 | \sa isActive() |
341 | */ |
342 | |
343 | void QSqlResult::setActive(bool active) |
344 | { |
345 | Q_D(QSqlResult); |
346 | if (active) |
347 | d->executedQuery = d->sql; |
348 | |
349 | d->active = active; |
350 | } |
351 | |
352 | /*! |
353 | This function is provided for derived classes to set the last |
354 | error to \a error. |
355 | |
356 | \sa lastError() |
357 | */ |
358 | |
359 | void QSqlResult::setLastError(const QSqlError &error) |
360 | { |
361 | Q_D(QSqlResult); |
362 | d->error = error; |
363 | } |
364 | |
365 | |
366 | /*! |
367 | Returns the last error associated with the result. |
368 | */ |
369 | |
370 | QSqlError QSqlResult::lastError() const |
371 | { |
372 | Q_D(const QSqlResult); |
373 | return d->error; |
374 | } |
375 | |
376 | /*! |
377 | \fn int QSqlResult::size() |
378 | |
379 | Returns the size of the \c SELECT result, or -1 if it cannot be |
380 | determined or if the query is not a \c SELECT statement. |
381 | |
382 | \sa numRowsAffected() |
383 | */ |
384 | |
385 | /*! |
386 | \fn int QSqlResult::numRowsAffected() |
387 | |
388 | Returns the number of rows affected by the last query executed, or |
389 | -1 if it cannot be determined or if the query is a \c SELECT |
390 | statement. |
391 | |
392 | \sa size() |
393 | */ |
394 | |
395 | /*! |
396 | \fn QVariant QSqlResult::data(int index) |
397 | |
398 | Returns the data for field \a index in the current row as |
399 | a QVariant. This function is only called if the result is in |
400 | an active state and is positioned on a valid record and \a index is |
401 | non-negative. Derived classes must reimplement this function and |
402 | return the value of field \a index, or QVariant() if it cannot be |
403 | determined. |
404 | */ |
405 | |
406 | /*! |
407 | \fn bool QSqlResult::reset(const QString &query) |
408 | |
409 | Sets the result to use the SQL statement \a query for subsequent |
410 | data retrieval. |
411 | |
412 | Derived classes must reimplement this function and apply the \a |
413 | query to the database. This function is only called after the |
414 | result is set to an inactive state and is positioned before the |
415 | first record of the new result. Derived classes should return |
416 | true if the query was successful and ready to be used, or false |
417 | otherwise. |
418 | |
419 | \sa setQuery() |
420 | */ |
421 | |
422 | /*! |
423 | \fn bool QSqlResult::fetch(int index) |
424 | |
425 | Positions the result to an arbitrary (zero-based) row \a index. |
426 | |
427 | This function is only called if the result is in an active state. |
428 | Derived classes must reimplement this function and position the |
429 | result to the row \a index, and call setAt() with an appropriate |
430 | value. Return true to indicate success, or false to signify |
431 | failure. |
432 | |
433 | \sa isActive(), fetchFirst(), fetchLast(), fetchNext(), fetchPrevious() |
434 | */ |
435 | |
436 | /*! |
437 | \fn bool QSqlResult::fetchFirst() |
438 | |
439 | Positions the result to the first record (row 0) in the result. |
440 | |
441 | This function is only called if the result is in an active state. |
442 | Derived classes must reimplement this function and position the |
443 | result to the first record, and call setAt() with an appropriate |
444 | value. Return true to indicate success, or false to signify |
445 | failure. |
446 | |
447 | \sa fetch(), fetchLast() |
448 | */ |
449 | |
450 | /*! |
451 | \fn bool QSqlResult::fetchLast() |
452 | |
453 | Positions the result to the last record (last row) in the result. |
454 | |
455 | This function is only called if the result is in an active state. |
456 | Derived classes must reimplement this function and position the |
457 | result to the last record, and call setAt() with an appropriate |
458 | value. Return true to indicate success, or false to signify |
459 | failure. |
460 | |
461 | \sa fetch(), fetchFirst() |
462 | */ |
463 | |
464 | /*! |
465 | Positions the result to the next available record (row) in the |
466 | result. |
467 | |
468 | This function is only called if the result is in an active |
469 | state. The default implementation calls fetch() with the next |
470 | index. Derived classes can reimplement this function and position |
471 | the result to the next record in some other way, and call setAt() |
472 | with an appropriate value. Return true to indicate success, or |
473 | false to signify failure. |
474 | |
475 | \sa fetch(), fetchPrevious() |
476 | */ |
477 | |
478 | bool QSqlResult::fetchNext() |
479 | { |
480 | return fetch(i: at() + 1); |
481 | } |
482 | |
483 | /*! |
484 | Positions the result to the previous record (row) in the result. |
485 | |
486 | This function is only called if the result is in an active state. |
487 | The default implementation calls fetch() with the previous index. |
488 | Derived classes can reimplement this function and position the |
489 | result to the next record in some other way, and call setAt() |
490 | with an appropriate value. Return true to indicate success, or |
491 | false to signify failure. |
492 | */ |
493 | |
494 | bool QSqlResult::fetchPrevious() |
495 | { |
496 | return fetch(i: at() - 1); |
497 | } |
498 | |
499 | /*! |
500 | Returns \c true if you can only scroll forward through the result |
501 | set; otherwise returns \c false. |
502 | |
503 | \sa setForwardOnly() |
504 | */ |
505 | bool QSqlResult::isForwardOnly() const |
506 | { |
507 | Q_D(const QSqlResult); |
508 | return d->forwardOnly; |
509 | } |
510 | |
511 | /*! |
512 | Sets forward only mode to \a forward. If \a forward is true, only |
513 | fetchNext() is allowed for navigating the results. Forward only |
514 | mode needs much less memory since results do not have to be |
515 | cached. By default, this feature is disabled. |
516 | |
517 | Setting forward only to false is a suggestion to the database engine, |
518 | which has the final say on whether a result set is forward only or |
519 | scrollable. isForwardOnly() will always return the correct status of |
520 | the result set. |
521 | |
522 | \note Calling setForwardOnly after execution of the query will result |
523 | in unexpected results at best, and crashes at worst. |
524 | |
525 | \note To make sure the forward-only query completed successfully, |
526 | the application should check lastError() for an error not only after |
527 | executing the query, but also after navigating the query results. |
528 | |
529 | \warning PostgreSQL: While navigating the query results in forward-only |
530 | mode, do not execute any other SQL command on the same database |
531 | connection. This will cause the query results to be lost. |
532 | |
533 | \sa isForwardOnly(), fetchNext(), QSqlQuery::setForwardOnly() |
534 | */ |
535 | void QSqlResult::setForwardOnly(bool forward) |
536 | { |
537 | Q_D(QSqlResult); |
538 | d->forwardOnly = forward; |
539 | } |
540 | |
541 | /*! |
542 | Prepares the given \a query, using the underlying database |
543 | functionality where possible. Returns \c true if the query is |
544 | prepared successfully; otherwise returns \c false. |
545 | |
546 | Note: This method should have been called "safePrepare()". |
547 | |
548 | \sa prepare() |
549 | */ |
550 | bool QSqlResult::savePrepare(const QString& query) |
551 | { |
552 | Q_D(QSqlResult); |
553 | if (!driver()) |
554 | return false; |
555 | d->clear(); |
556 | d->sql = query; |
557 | if (!driver()->hasFeature(f: QSqlDriver::PreparedQueries)) |
558 | return prepare(query); |
559 | |
560 | // parse the query to memorize parameter location |
561 | d->executedQuery = d->namedToPositionalBinding(query); |
562 | |
563 | if (driver()->hasFeature(f: QSqlDriver::NamedPlaceholders)) |
564 | d->executedQuery = d->positionalToNamedBinding(query); |
565 | |
566 | return prepare(query: d->executedQuery); |
567 | } |
568 | |
569 | /*! |
570 | Prepares the given \a query for execution; the query will normally |
571 | use placeholders so that it can be executed repeatedly. Returns |
572 | true if the query is prepared successfully; otherwise returns \c false. |
573 | |
574 | \sa exec() |
575 | */ |
576 | bool QSqlResult::prepare(const QString& query) |
577 | { |
578 | Q_D(QSqlResult); |
579 | d->sql = query; |
580 | if (d->holders.isEmpty()) { |
581 | // parse the query to memorize parameter location |
582 | d->namedToPositionalBinding(query); |
583 | } |
584 | return true; // fake prepares should always succeed |
585 | } |
586 | |
587 | bool QSqlResultPrivate::isVariantNull(const QVariant &variant) |
588 | { |
589 | if (variant.isNull()) |
590 | return true; |
591 | |
592 | switch (variant.typeId()) { |
593 | case qMetaTypeId<QString>(): |
594 | return static_cast<const QString*>(variant.constData())->isNull(); |
595 | case qMetaTypeId<QByteArray>(): |
596 | return static_cast<const QByteArray*>(variant.constData())->isNull(); |
597 | case qMetaTypeId<QDateTime>(): |
598 | // We treat invalid date-time as null, since its ISODate would be empty. |
599 | return !static_cast<const QDateTime*>(variant.constData())->isValid(); |
600 | case qMetaTypeId<QDate>(): |
601 | return static_cast<const QDate*>(variant.constData())->isNull(); |
602 | case qMetaTypeId<QTime>(): |
603 | // As for QDateTime, QTime can be invalid without being null. |
604 | return !static_cast<const QTime*>(variant.constData())->isValid(); |
605 | case qMetaTypeId<QUuid>(): |
606 | return static_cast<const QUuid*>(variant.constData())->isNull(); |
607 | default: |
608 | break; |
609 | } |
610 | |
611 | return false; |
612 | } |
613 | |
614 | /*! |
615 | Executes the query, returning true if successful; otherwise returns |
616 | false. |
617 | |
618 | \sa prepare() |
619 | */ |
620 | bool QSqlResult::exec() |
621 | { |
622 | Q_D(QSqlResult); |
623 | bool ret; |
624 | // fake preparation - just replace the placeholders.. |
625 | QString query = lastQuery(); |
626 | if (d->binds == NamedBinding) { |
627 | for (qsizetype i = d->holders.size() - 1; i >= 0; --i) { |
628 | const QString &holder = d->holders.at(i).holderName; |
629 | const QVariant val = d->values.value(i: d->indexes.value(key: holder).value(i: 0,defaultValue: -1)); |
630 | QSqlField f(""_L1 , val.metaType()); |
631 | if (QSqlResultPrivate::isVariantNull(variant: val)) |
632 | f.setValue(QVariant()); |
633 | else |
634 | f.setValue(val); |
635 | query = query.replace(i: d->holders.at(i).holderPos, |
636 | len: holder.size(), after: driver()->formatValue(field: f)); |
637 | } |
638 | } else { |
639 | qsizetype i = 0; |
640 | for (const QVariant &var : std::as_const(t&: d->values)) { |
641 | i = query.indexOf(c: u'?', from: i); |
642 | if (i == -1) |
643 | continue; |
644 | QSqlField f(""_L1 , var.metaType()); |
645 | if (QSqlResultPrivate::isVariantNull(variant: var)) |
646 | f.clear(); |
647 | else |
648 | f.setValue(var); |
649 | const QString val = driver()->formatValue(field: f); |
650 | query = query.replace(i, len: 1, after: val); |
651 | i += val.size(); |
652 | } |
653 | } |
654 | |
655 | // have to retain the original query with placeholders |
656 | QString orig = lastQuery(); |
657 | ret = reset(sqlquery: query); |
658 | d->executedQuery = query; |
659 | setQuery(orig); |
660 | d->resetBindCount(); |
661 | return ret; |
662 | } |
663 | |
664 | /*! |
665 | Binds the value \a val of parameter type \a paramType to position \a index |
666 | in the current record (row). |
667 | |
668 | \sa addBindValue() |
669 | */ |
670 | void QSqlResult::bindValue(int index, const QVariant& val, QSql::ParamType paramType) |
671 | { |
672 | Q_D(QSqlResult); |
673 | d->binds = PositionalBinding; |
674 | QList<int> &indexes = d->indexes[d->fieldSerial(i: index)]; |
675 | if (!indexes.contains(t: index)) |
676 | indexes.append(t: index); |
677 | if (d->values.size() <= index) |
678 | d->values.resize(size: index + 1); |
679 | d->values[index] = val; |
680 | if (paramType != QSql::In || !d->types.isEmpty()) |
681 | d->types[index] = paramType; |
682 | } |
683 | |
684 | /*! |
685 | \overload |
686 | |
687 | Binds the value \a val of parameter type \a paramType to the \a |
688 | placeholder name in the current record (row). |
689 | |
690 | \note Binding an undefined placeholder will result in undefined behavior. |
691 | |
692 | \sa QSqlQuery::bindValue() |
693 | */ |
694 | void QSqlResult::bindValue(const QString& placeholder, const QVariant& val, |
695 | QSql::ParamType paramType) |
696 | { |
697 | Q_D(QSqlResult); |
698 | d->binds = NamedBinding; |
699 | // if the index has already been set when doing emulated named |
700 | // bindings - don't reset it |
701 | const QList<int> indexes = d->indexes.value(key: placeholder); |
702 | for (int idx : indexes) { |
703 | if (d->values.size() <= idx) |
704 | d->values.resize(size: idx + 1); |
705 | d->values[idx] = val; |
706 | if (paramType != QSql::In || !d->types.isEmpty()) |
707 | d->types[idx] = paramType; |
708 | } |
709 | } |
710 | |
711 | /*! |
712 | Binds the value \a val of parameter type \a paramType to the next |
713 | available position in the current record (row). |
714 | |
715 | \sa bindValue() |
716 | */ |
717 | void QSqlResult::addBindValue(const QVariant& val, QSql::ParamType paramType) |
718 | { |
719 | Q_D(QSqlResult); |
720 | d->binds = PositionalBinding; |
721 | bindValue(index: d->bindCount, val, paramType); |
722 | ++d->bindCount; |
723 | } |
724 | |
725 | /*! |
726 | Returns the value bound at position \a index in the current record |
727 | (row). |
728 | |
729 | \sa bindValue(), boundValues() |
730 | */ |
731 | QVariant QSqlResult::boundValue(int index) const |
732 | { |
733 | Q_D(const QSqlResult); |
734 | return d->values.value(i: index); |
735 | } |
736 | |
737 | /*! |
738 | \overload |
739 | |
740 | Returns the value bound by the given \a placeholder name in the |
741 | current record (row). |
742 | |
743 | \sa bindValueType() |
744 | */ |
745 | QVariant QSqlResult::boundValue(const QString& placeholder) const |
746 | { |
747 | Q_D(const QSqlResult); |
748 | const QList<int> indexes = d->indexes.value(key: placeholder); |
749 | return d->values.value(i: indexes.value(i: 0,defaultValue: -1)); |
750 | } |
751 | |
752 | /*! |
753 | Returns the parameter type for the value bound at position \a index. |
754 | |
755 | \sa boundValue() |
756 | */ |
757 | QSql::ParamType QSqlResult::bindValueType(int index) const |
758 | { |
759 | Q_D(const QSqlResult); |
760 | return d->types.value(key: index, defaultValue: QSql::In); |
761 | } |
762 | |
763 | /*! |
764 | \overload |
765 | |
766 | Returns the parameter type for the value bound with the given \a |
767 | placeholder name. |
768 | */ |
769 | QSql::ParamType QSqlResult::bindValueType(const QString& placeholder) const |
770 | { |
771 | Q_D(const QSqlResult); |
772 | return d->types.value(key: d->indexes.value(key: placeholder).value(i: 0,defaultValue: -1), defaultValue: QSql::In); |
773 | } |
774 | |
775 | /*! |
776 | Returns the number of bound values in the result. |
777 | |
778 | \sa boundValues() |
779 | */ |
780 | int QSqlResult::boundValueCount() const |
781 | { |
782 | Q_D(const QSqlResult); |
783 | return d->values.size(); |
784 | } |
785 | |
786 | /*! |
787 | Returns a list of the result's bound values for the current |
788 | record (row). |
789 | |
790 | \sa boundValueCount() |
791 | */ |
792 | QVariantList QSqlResult::boundValues(QT6_IMPL_NEW_OVERLOAD) const |
793 | { |
794 | Q_D(const QSqlResult); |
795 | return d->values; |
796 | } |
797 | |
798 | /*! |
799 | \overload |
800 | |
801 | Returns a mutable reference to the list of the result's bound values |
802 | for the current record (row). |
803 | |
804 | \sa boundValueCount() |
805 | */ |
806 | QVariantList &QSqlResult::boundValues(QT6_IMPL_NEW_OVERLOAD) |
807 | { |
808 | Q_D(QSqlResult); |
809 | return d->values; |
810 | } |
811 | |
812 | |
813 | /*! |
814 | Returns the binding syntax used by prepared queries. |
815 | */ |
816 | QSqlResult::BindingSyntax QSqlResult::bindingSyntax() const |
817 | { |
818 | Q_D(const QSqlResult); |
819 | return d->binds; |
820 | } |
821 | |
822 | /*! |
823 | Clears the entire result set and releases any associated |
824 | resources. |
825 | */ |
826 | void QSqlResult::clear() |
827 | { |
828 | Q_D(QSqlResult); |
829 | d->clear(); |
830 | } |
831 | |
832 | /*! |
833 | Returns the query that was actually executed. This may differ from |
834 | the query that was passed, for example if bound values were used |
835 | with a prepared query and the underlying database doesn't support |
836 | prepared queries. |
837 | |
838 | \sa exec(), setQuery() |
839 | */ |
840 | QString QSqlResult::executedQuery() const |
841 | { |
842 | Q_D(const QSqlResult); |
843 | return d->executedQuery; |
844 | } |
845 | |
846 | /*! |
847 | Resets the number of bind parameters. |
848 | */ |
849 | void QSqlResult::resetBindCount() |
850 | { |
851 | Q_D(QSqlResult); |
852 | d->resetBindCount(); |
853 | } |
854 | |
855 | /*! |
856 | Returns the names of all bound values. |
857 | |
858 | \sa boundValue(), boundValueName() |
859 | */ |
860 | QStringList QSqlResult::boundValueNames() const |
861 | { |
862 | Q_D(const QSqlResult); |
863 | QList<QString> ret; |
864 | for (const QHolder &holder : std::as_const(t: d->holders)) |
865 | ret.push_back(t: holder.holderName); |
866 | return ret; |
867 | } |
868 | |
869 | /*! |
870 | Returns the name of the bound value at position \a index in the |
871 | current record (row). |
872 | |
873 | \sa boundValue(), boundValueNames() |
874 | */ |
875 | QString QSqlResult::boundValueName(int index) const |
876 | { |
877 | Q_D(const QSqlResult); |
878 | return d->holderAt(index); |
879 | } |
880 | |
881 | /*! |
882 | Returns \c true if at least one of the query's bound values is a \c |
883 | QSql::Out or a QSql::InOut; otherwise returns \c false. |
884 | |
885 | \sa bindValueType() |
886 | */ |
887 | bool QSqlResult::hasOutValues() const |
888 | { |
889 | Q_D(const QSqlResult); |
890 | if (d->types.isEmpty()) |
891 | return false; |
892 | QHash<int, QSql::ParamType>::ConstIterator it; |
893 | for (it = d->types.constBegin(); it != d->types.constEnd(); ++it) { |
894 | if (it.value() != QSql::In) |
895 | return true; |
896 | } |
897 | return false; |
898 | } |
899 | |
900 | /*! |
901 | Returns the current record if the query is active; otherwise |
902 | returns an empty QSqlRecord. |
903 | |
904 | The default implementation always returns an empty QSqlRecord. |
905 | |
906 | \sa isActive() |
907 | */ |
908 | QSqlRecord QSqlResult::record() const |
909 | { |
910 | return QSqlRecord(); |
911 | } |
912 | |
913 | /*! |
914 | Returns the object ID of the most recent inserted row if the |
915 | database supports it. |
916 | An invalid QVariant will be returned if the query did not |
917 | insert any value or if the database does not report the id back. |
918 | If more than one row was touched by the insert, the behavior is |
919 | undefined. |
920 | |
921 | Note that for Oracle databases the row's ROWID will be returned, |
922 | while for MySQL databases the row's auto-increment field will |
923 | be returned. |
924 | |
925 | \sa QSqlDriver::hasFeature() |
926 | */ |
927 | QVariant QSqlResult::lastInsertId() const |
928 | { |
929 | return QVariant(); |
930 | } |
931 | |
932 | /*! \internal |
933 | */ |
934 | void QSqlResult::virtual_hook(int, void *) |
935 | { |
936 | } |
937 | |
938 | /*! \internal |
939 | \since 4.2 |
940 | |
941 | Executes a prepared query in batch mode if the driver supports it, |
942 | otherwise emulates a batch execution using bindValue() and exec(). |
943 | QSqlDriver::hasFeature() can be used to find out whether a driver |
944 | supports batch execution. |
945 | |
946 | Batch execution can be faster for large amounts of data since it |
947 | reduces network roundtrips. |
948 | |
949 | For batch executions, bound values have to be provided as lists |
950 | of variants (QVariantList). |
951 | |
952 | Each list must contain values of the same type. All lists must |
953 | contain equal amount of values (rows). |
954 | |
955 | NULL values are passed in as typed QVariants, for example |
956 | \c {QVariant(QMetaType::fromType<int>())} for an integer NULL value. |
957 | |
958 | Example: |
959 | |
960 | \snippet code/src_sql_kernel_qsqlresult.cpp 0 |
961 | |
962 | Here, we insert two rows into a SQL table, with each row containing three values. |
963 | |
964 | \sa exec(), QSqlDriver::hasFeature() |
965 | */ |
966 | bool QSqlResult::execBatch(bool arrayBind) |
967 | { |
968 | Q_UNUSED(arrayBind); |
969 | Q_D(QSqlResult); |
970 | |
971 | const QList<QVariant> values = d->values; |
972 | if (values.size() == 0) |
973 | return false; |
974 | const qsizetype batchCount = values.at(i: 0).toList().size(); |
975 | const qsizetype valueCount = values.size(); |
976 | for (qsizetype i = 0; i < batchCount; ++i) { |
977 | for (qsizetype j = 0; j < valueCount; ++j) |
978 | bindValue(index: j, val: values.at(i: j).toList().at(i), paramType: QSql::In); |
979 | if (!exec()) |
980 | return false; |
981 | } |
982 | return true; |
983 | } |
984 | |
985 | /*! \internal |
986 | */ |
987 | void QSqlResult::detachFromResultSet() |
988 | { |
989 | } |
990 | |
991 | /*! \internal |
992 | */ |
993 | void QSqlResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy) |
994 | { |
995 | Q_D(QSqlResult); |
996 | d->precisionPolicy = policy; |
997 | } |
998 | |
999 | /*! \internal |
1000 | */ |
1001 | QSql::NumericalPrecisionPolicy QSqlResult::numericalPrecisionPolicy() const |
1002 | { |
1003 | Q_D(const QSqlResult); |
1004 | return d->precisionPolicy; |
1005 | } |
1006 | |
1007 | /*! \internal |
1008 | */ |
1009 | bool QSqlResult::nextResult() |
1010 | { |
1011 | return false; |
1012 | } |
1013 | |
1014 | /*! |
1015 | Returns the low-level database handle for this result set |
1016 | wrapped in a QVariant or an invalid QVariant if there is no handle. |
1017 | |
1018 | \warning Use this with uttermost care and only if you know what you're doing. |
1019 | |
1020 | \warning The handle returned here can become a stale pointer if the result |
1021 | is modified (for example, if you clear it). |
1022 | |
1023 | \warning The handle can be NULL if the result was not executed yet. |
1024 | |
1025 | \warning PostgreSQL: in forward-only mode, the handle of QSqlResult can change |
1026 | after calling fetch(), fetchFirst(), fetchLast(), fetchNext(), fetchPrevious(), |
1027 | nextResult(). |
1028 | |
1029 | The handle returned here is database-dependent, you should query the type |
1030 | name of the variant before accessing it. |
1031 | |
1032 | This example retrieves the handle for a sqlite result: |
1033 | |
1034 | \snippet code/src_sql_kernel_qsqlresult.cpp 1 |
1035 | |
1036 | This snippet returns the handle for PostgreSQL or MySQL: |
1037 | |
1038 | \snippet code/src_sql_kernel_qsqlresult_snippet.cpp 2 |
1039 | |
1040 | \sa QSqlDriver::handle() |
1041 | */ |
1042 | QVariant QSqlResult::handle() const |
1043 | { |
1044 | return QVariant(); |
1045 | } |
1046 | |
1047 | QT_END_NAMESPACE |
1048 | |