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 <qdatetime.h>
43#include <qvariant.h>
44#include <QtSql/private/qsqldriver_p.h>
45
46QT_BEGIN_NAMESPACE
47
48/*
49 QSqlCachedResult is a convenience class for databases that only allow
50 forward only fetching. It will cache all the results so we can iterate
51 backwards over the results again.
52
53 All you need to do is to inherit from QSqlCachedResult and reimplement
54 gotoNext(). gotoNext() will have a reference to the internal cache and
55 will give you an index where you can start filling in your data. Special
56 case: If the user actually wants a forward-only query, idx will be -1
57 to indicate that we are not interested in the actual values.
58*/
59
60static const uint initial_cache_size = 128;
61
62void QSqlCachedResultPrivate::cleanup()
63{
64 cache.clear();
65 atEnd = false;
66 colCount = 0;
67 rowCacheEnd = 0;
68}
69
70void QSqlCachedResultPrivate::init(int count, bool fo)
71{
72 Q_ASSERT(count);
73 cleanup();
74 forwardOnly = fo;
75 colCount = count;
76 if (fo) {
77 cache.resize(count);
78 rowCacheEnd = count;
79 } else {
80 cache.resize(initial_cache_size * count);
81 }
82}
83
84int QSqlCachedResultPrivate::nextIndex()
85{
86 if (forwardOnly)
87 return 0;
88 int newIdx = rowCacheEnd;
89 if (newIdx + colCount > cache.size())
90 cache.resize(qMin(cache.size() * 2, cache.size() + 10000));
91 rowCacheEnd += colCount;
92
93 return newIdx;
94}
95
96bool QSqlCachedResultPrivate::canSeek(int i) const
97{
98 if (forwardOnly || i < 0)
99 return false;
100 return rowCacheEnd >= (i + 1) * colCount;
101}
102
103void QSqlCachedResultPrivate::revertLast()
104{
105 if (forwardOnly)
106 return;
107 rowCacheEnd -= colCount;
108}
109
110inline int QSqlCachedResultPrivate::cacheCount() const
111{
112 Q_ASSERT(!forwardOnly);
113 Q_ASSERT(colCount);
114 return rowCacheEnd / colCount;
115}
116
117//////////////
118
119QSqlCachedResult::QSqlCachedResult(QSqlCachedResultPrivate &d)
120 : QSqlResult(d)
121{
122}
123
124void QSqlCachedResult::init(int colCount)
125{
126 Q_D(QSqlCachedResult);
127 d->init(colCount, isForwardOnly());
128}
129
130bool QSqlCachedResult::fetch(int i)
131{
132 Q_D(QSqlCachedResult);
133 if ((!isActive()) || (i < 0))
134 return false;
135 if (at() == i)
136 return true;
137 if (d->forwardOnly) {
138 // speed hack - do not copy values if not needed
139 if (at() > i || at() == QSql::AfterLastRow)
140 return false;
141 while(at() < i - 1) {
142 if (!gotoNext(d->cache, -1))
143 return false;
144 setAt(at() + 1);
145 }
146 if (!gotoNext(d->cache, 0))
147 return false;
148 setAt(at() + 1);
149 return true;
150 }
151 if (d->canSeek(i)) {
152 setAt(i);
153 return true;
154 }
155 if (d->rowCacheEnd > 0)
156 setAt(d->cacheCount());
157 while (at() < i + 1) {
158 if (!cacheNext()) {
159 if (d->canSeek(i))
160 break;
161 return false;
162 }
163 }
164 setAt(i);
165
166 return true;
167}
168
169bool QSqlCachedResult::fetchNext()
170{
171 Q_D(QSqlCachedResult);
172 if (d->canSeek(at() + 1)) {
173 setAt(at() + 1);
174 return true;
175 }
176 return cacheNext();
177}
178
179bool QSqlCachedResult::fetchPrevious()
180{
181 return fetch(at() - 1);
182}
183
184bool QSqlCachedResult::fetchFirst()
185{
186 Q_D(QSqlCachedResult);
187 if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
188 return false;
189 }
190 if (d->canSeek(0)) {
191 setAt(0);
192 return true;
193 }
194 return cacheNext();
195}
196
197bool QSqlCachedResult::fetchLast()
198{
199 Q_D(QSqlCachedResult);
200 if (d->atEnd) {
201 if (d->forwardOnly)
202 return false;
203 else
204 return fetch(d->cacheCount() - 1);
205 }
206
207 int i = at();
208 while (fetchNext())
209 ++i; /* brute force */
210 if (d->forwardOnly && at() == QSql::AfterLastRow) {
211 setAt(i);
212 return true;
213 } else {
214 return fetch(i);
215 }
216}
217
218QVariant QSqlCachedResult::data(int i)
219{
220 Q_D(const QSqlCachedResult);
221 int idx = d->forwardOnly ? i : at() * d->colCount + i;
222 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
223 return QVariant();
224
225 return d->cache.at(idx);
226}
227
228bool QSqlCachedResult::isNull(int i)
229{
230 Q_D(const QSqlCachedResult);
231 int idx = d->forwardOnly ? i : at() * d->colCount + i;
232 if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
233 return true;
234
235 return d->cache.at(idx).isNull();
236}
237
238void QSqlCachedResult::cleanup()
239{
240 Q_D(QSqlCachedResult);
241 setAt(QSql::BeforeFirstRow);
242 setActive(false);
243 d->cleanup();
244}
245
246void QSqlCachedResult::clearValues()
247{
248 Q_D(QSqlCachedResult);
249 setAt(QSql::BeforeFirstRow);
250 d->rowCacheEnd = 0;
251 d->atEnd = false;
252}
253
254bool QSqlCachedResult::cacheNext()
255{
256 Q_D(QSqlCachedResult);
257 if (d->atEnd)
258 return false;
259
260 if(isForwardOnly()) {
261 d->cache.resize(d->colCount);
262 }
263
264 if (!gotoNext(d->cache, d->nextIndex())) {
265 d->revertLast();
266 d->atEnd = true;
267 return false;
268 }
269 setAt(at() + 1);
270 return true;
271}
272
273int QSqlCachedResult::colCount() const
274{
275 Q_D(const QSqlCachedResult);
276 return d->colCount;
277}
278
279QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
280{
281 Q_D(QSqlCachedResult);
282 return d->cache;
283}
284
285void QSqlCachedResult::virtual_hook(int id, void *data)
286{
287 QSqlResult::virtual_hook(id, data);
288}
289
290void QSqlCachedResult::detachFromResultSet()
291{
292 cleanup();
293}
294
295void QSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
296{
297 QSqlResult::setNumericalPrecisionPolicy(policy);
298 cleanup();
299}
300
301
302QT_END_NAMESPACE
303