1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "private/qsqlcachedresult_p.h"
41
42#include <qvariant.h>
43#include <qdatetime.h>
44#include <qvector.h>
45#include <QtSql/private/qsqldriver_p.h>
46
47QT_BEGIN_NAMESPACE
48
49/*
50 QSqlCachedResult is a convenience class for databases that only allow
51 forward only fetching. It will cache all the results so we can iterate
52 backwards over the results again.
53
54 All you need to do is to inherit from QSqlCachedResult and reimplement
55 gotoNext(). gotoNext() will have a reference to the internal cache and
56 will give you an index where you can start filling in your data. Special
57 case: If the user actually wants a forward-only query, idx will be -1
58 to indicate that we are not interested in the actual values.
59*/
60
61static const uint initial_cache_size = 128;
62
63QSqlCachedResultPrivate::QSqlCachedResultPrivate(QSqlCachedResult *q, const QSqlDriver *drv)
64 : QSqlResultPrivate(q, drv),
65 rowCacheEnd(0),
66 colCount(0),
67 atEnd(false)
68{
69}
70
71void QSqlCachedResultPrivate::cleanup()
72{
73 cache.clear();
74 atEnd = false;
75 colCount = 0;
76 rowCacheEnd = 0;
77}
78
79void QSqlCachedResultPrivate::init(int count, bool fo)
80{
81 Q_ASSERT(count);
82 cleanup();
83 forwardOnly = fo;
84 colCount = count;
85 if (fo) {
86 cache.resize(asize: count);
87 rowCacheEnd = count;
88 } else {
89 cache.resize(asize: initial_cache_size * count);
90 }
91}
92
93int QSqlCachedResultPrivate::nextIndex()
94{
95 if (forwardOnly)
96 return 0;
97 int newIdx = rowCacheEnd;
98 if (newIdx + colCount > cache.size())
99 cache.resize(asize: qMin(a: cache.size() * 2, b: cache.size() + 10000));
100 rowCacheEnd += colCount;
101
102 return newIdx;
103}
104
105bool QSqlCachedResultPrivate::canSeek(int i) const
106{
107 if (forwardOnly || i < 0)
108 return false;
109 return rowCacheEnd >= (i + 1) * colCount;
110}
111
112void QSqlCachedResultPrivate::revertLast()
113{
114 if (forwardOnly)
115 return;
116 rowCacheEnd -= colCount;
117}
118
119inline int QSqlCachedResultPrivate::cacheCount() const
120{
121 Q_ASSERT(!forwardOnly);
122 Q_ASSERT(colCount);
123 return rowCacheEnd / colCount;
124}
125
126//////////////
127
128QSqlCachedResult::QSqlCachedResult(QSqlCachedResultPrivate &d)
129 : QSqlResult(d)
130{
131}
132
133void QSqlCachedResult::init(int colCount)
134{
135 Q_D(QSqlCachedResult);
136 d->init(count: colCount, fo: isForwardOnly());
137}
138
139bool QSqlCachedResult::fetch(int i)
140{
141 Q_D(QSqlCachedResult);
142 if ((!isActive()) || (i < 0))
143 return false;
144 if (at() == i)
145 return true;
146 if (d->forwardOnly) {
147 // speed hack - do not copy values if not needed
148 if (at() > i || at() == QSql::AfterLastRow)
149 return false;
150 while(at() < i - 1) {
151 if (!gotoNext(values&: d->cache, index: -1))
152 return false;
153 setAt(at() + 1);
154 }
155 if (!gotoNext(values&: d->cache, index: 0))
156 return false;
157 setAt(at() + 1);
158 return true;
159 }
160 if (d->canSeek(i)) {
161 setAt(i);
162 return true;
163 }
164 if (d->rowCacheEnd > 0)
165 setAt(d->cacheCount());
166 while (at() < i + 1) {
167 if (!cacheNext()) {
168 if (d->canSeek(i))
169 break;
170 return false;
171 }
172 }
173 setAt(i);
174
175 return true;
176}
177
178bool QSqlCachedResult::fetchNext()
179{
180 Q_D(QSqlCachedResult);
181 if (d->canSeek(i: at() + 1)) {
182 setAt(at() + 1);
183 return true;
184 }
185 return cacheNext();
186}
187
188bool QSqlCachedResult::fetchPrevious()
189{
190 return fetch(i: at() - 1);
191}
192
193bool QSqlCachedResult::fetchFirst()
194{
195 Q_D(QSqlCachedResult);
196 if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
197 return false;
198 }
199 if (d->canSeek(i: 0)) {
200 setAt(0);
201 return true;
202 }
203 return cacheNext();
204}
205
206bool QSqlCachedResult::fetchLast()
207{
208 Q_D(QSqlCachedResult);
209 if (d->atEnd) {
210 if (d->forwardOnly)
211 return false;
212 else
213 return fetch(i: d->cacheCount() - 1);
214 }
215
216 int i = at();
217 while (fetchNext())
218 ++i; /* brute force */
219 if (d->forwardOnly && at() == QSql::AfterLastRow) {
220 setAt(i);
221 return true;
222 } else {
223 return fetch(i);
224 }
225}
226
227QVariant QSqlCachedResult::data(int i)
228{
229 Q_D(const QSqlCachedResult);
230 int idx = d->forwardOnly ? i : at() * d->colCount + i;
231 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
232 return QVariant();
233
234 return d->cache.at(i: idx);
235}
236
237bool QSqlCachedResult::isNull(int i)
238{
239 Q_D(const QSqlCachedResult);
240 int idx = d->forwardOnly ? i : at() * d->colCount + i;
241 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
242 return true;
243
244 return d->cache.at(i: idx).isNull();
245}
246
247void QSqlCachedResult::cleanup()
248{
249 Q_D(QSqlCachedResult);
250 setAt(QSql::BeforeFirstRow);
251 setActive(false);
252 d->cleanup();
253}
254
255void QSqlCachedResult::clearValues()
256{
257 Q_D(QSqlCachedResult);
258 setAt(QSql::BeforeFirstRow);
259 d->rowCacheEnd = 0;
260 d->atEnd = false;
261}
262
263bool QSqlCachedResult::cacheNext()
264{
265 Q_D(QSqlCachedResult);
266 if (d->atEnd)
267 return false;
268
269 if(isForwardOnly()) {
270 d->cache.resize(asize: d->colCount);
271 }
272
273 if (!gotoNext(values&: d->cache, index: d->nextIndex())) {
274 d->revertLast();
275 d->atEnd = true;
276 return false;
277 }
278 setAt(at() + 1);
279 return true;
280}
281
282int QSqlCachedResult::colCount() const
283{
284 Q_D(const QSqlCachedResult);
285 return d->colCount;
286}
287
288QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
289{
290 Q_D(QSqlCachedResult);
291 return d->cache;
292}
293
294void QSqlCachedResult::virtual_hook(int id, void *data)
295{
296 QSqlResult::virtual_hook(id, data);
297}
298
299void QSqlCachedResult::detachFromResultSet()
300{
301 cleanup();
302}
303
304void QSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
305{
306 QSqlResult::setNumericalPrecisionPolicy(policy);
307 cleanup();
308}
309
310
311QT_END_NAMESPACE
312

source code of qtbase/src/sql/kernel/qsqlcachedresult.cpp