Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsqlcachedresult.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#include "private/qsqlcachedresult_p.h"
6
7#include <qvariant.h>
8#include <QtSql/private/qsqldriver_p.h>
9
11
12/*
13 QSqlCachedResult is a convenience class for databases that only allow
14 forward only fetching. It will cache all the results so we can iterate
15 backwards over the results again.
16
17 All you need to do is to inherit from QSqlCachedResult and reimplement
18 gotoNext(). gotoNext() will have a reference to the internal cache and
19 will give you an index where you can start filling in your data. Special
20 case: If the user actually wants a forward-only query, idx will be -1
21 to indicate that we are not interested in the actual values.
22*/
23
24static constexpr qsizetype initial_cache_size = 128;
25
26void QSqlCachedResultPrivate::cleanup()
27{
28 cache.clear();
29 atEnd = false;
30 colCount = 0;
31 rowCacheEnd = 0;
32}
33
34void QSqlCachedResultPrivate::init(int count, bool fo)
35{
36 Q_ASSERT(count);
37 cleanup();
38 forwardOnly = fo;
39 colCount = count;
40 if (fo) {
41 cache.resize(count);
42 rowCacheEnd = count;
43 } else {
44 cache.resize(initial_cache_size * count);
45 }
46}
47
48int QSqlCachedResultPrivate::nextIndex()
49{
50 if (forwardOnly)
51 return 0;
52 int newIdx = rowCacheEnd;
53 if (newIdx + colCount > cache.size())
54 cache.resize(qMin(cache.size() * 2, cache.size() + 10000));
55 rowCacheEnd += colCount;
56
57 return newIdx;
58}
59
60bool QSqlCachedResultPrivate::canSeek(int i) const
61{
62 if (forwardOnly || i < 0)
63 return false;
64 return rowCacheEnd >= (i + 1) * colCount;
65}
66
67void QSqlCachedResultPrivate::revertLast()
68{
69 if (forwardOnly)
70 return;
71 rowCacheEnd -= colCount;
72}
73
74inline int QSqlCachedResultPrivate::cacheCount() const
75{
76 Q_ASSERT(!forwardOnly);
77 Q_ASSERT(colCount);
78 return rowCacheEnd / colCount;
79}
80
81//////////////
82
83QSqlCachedResult::QSqlCachedResult(QSqlCachedResultPrivate &d)
84 : QSqlResult(d)
85{
86}
87
88void QSqlCachedResult::init(int colCount)
89{
90 Q_D(QSqlCachedResult);
91 d->init(colCount, isForwardOnly());
92}
93
94bool QSqlCachedResult::fetch(int i)
95{
96 Q_D(QSqlCachedResult);
97 if ((!isActive()) || (i < 0))
98 return false;
99 if (at() == i)
100 return true;
101 if (d->forwardOnly) {
102 // speed hack - do not copy values if not needed
103 if (at() > i || at() == QSql::AfterLastRow)
104 return false;
105 while(at() < i - 1) {
106 if (!gotoNext(d->cache, -1))
107 return false;
108 setAt(at() + 1);
109 }
110 if (!gotoNext(d->cache, 0))
111 return false;
112 setAt(at() + 1);
113 return true;
114 }
115 if (d->canSeek(i)) {
116 setAt(i);
117 return true;
118 }
119 if (d->rowCacheEnd > 0)
120 setAt(d->cacheCount());
121 while (at() < i + 1) {
122 if (!cacheNext()) {
123 if (d->canSeek(i))
124 break;
125 return false;
126 }
127 }
128 setAt(i);
129
130 return true;
131}
132
133bool QSqlCachedResult::fetchNext()
134{
135 Q_D(QSqlCachedResult);
136 if (d->canSeek(at() + 1)) {
137 setAt(at() + 1);
138 return true;
139 }
140 return cacheNext();
141}
142
143bool QSqlCachedResult::fetchPrevious()
144{
145 return fetch(at() - 1);
146}
147
148bool QSqlCachedResult::fetchFirst()
149{
150 Q_D(QSqlCachedResult);
151 if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
152 return false;
153 }
154 if (d->canSeek(0)) {
155 setAt(0);
156 return true;
157 }
158 return cacheNext();
159}
160
161bool QSqlCachedResult::fetchLast()
162{
163 Q_D(QSqlCachedResult);
164 if (d->atEnd) {
165 if (d->forwardOnly)
166 return false;
167 else
168 return fetch(d->cacheCount() - 1);
169 }
170
171 int i = at();
172 while (fetchNext())
173 ++i; /* brute force */
174 if (d->forwardOnly && at() == QSql::AfterLastRow) {
175 setAt(i);
176 return true;
177 } else {
178 return fetch(i);
179 }
180}
181
182QVariant QSqlCachedResult::data(int i)
183{
184 Q_D(const QSqlCachedResult);
185 int idx = d->forwardOnly ? i : at() * d->colCount + i;
186 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
187 return QVariant();
188
189 return d->cache.at(idx);
190}
191
192bool QSqlCachedResult::isNull(int i)
193{
194 Q_D(const QSqlCachedResult);
195 int idx = d->forwardOnly ? i : at() * d->colCount + i;
196 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
197 return true;
198
199 return d->cache.at(idx).isNull();
200}
201
202void QSqlCachedResult::cleanup()
203{
204 Q_D(QSqlCachedResult);
205 setAt(QSql::BeforeFirstRow);
206 setActive(false);
207 d->cleanup();
208}
209
210void QSqlCachedResult::clearValues()
211{
212 Q_D(QSqlCachedResult);
213 setAt(QSql::BeforeFirstRow);
214 d->rowCacheEnd = 0;
215 d->atEnd = false;
216}
217
218bool QSqlCachedResult::cacheNext()
219{
220 Q_D(QSqlCachedResult);
221 if (d->atEnd)
222 return false;
223
224 if (isForwardOnly()) {
225 d->cache.resize(d->colCount);
226 }
227
228 if (!gotoNext(d->cache, d->nextIndex())) {
229 d->revertLast();
230 d->atEnd = true;
231 return false;
232 }
233 setAt(at() + 1);
234 return true;
235}
236
237int QSqlCachedResult::colCount() const
238{
239 Q_D(const QSqlCachedResult);
240 return d->colCount;
241}
242
243QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
244{
245 Q_D(QSqlCachedResult);
246 return d->cache;
247}
248
249void QSqlCachedResult::virtual_hook(int id, void *data)
250{
251 QSqlResult::virtual_hook(id, data);
252}
253
254void QSqlCachedResult::detachFromResultSet()
255{
256 cleanup();
257}
258
259void QSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
260{
261 QSqlResult::setNumericalPrecisionPolicy(policy);
262 cleanup();
263}
264
265
266QT_END_NAMESPACE
static QT_BEGIN_NAMESPACE constexpr qsizetype initial_cache_size