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
47 QT_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
61 static const uint initial_cache_size = 128;
62
QSqlCachedResultPrivate(QSqlCachedResult * q,const QSqlDriver * drv)63 QSqlCachedResultPrivate::QSqlCachedResultPrivate(QSqlCachedResult *q, const QSqlDriver *drv)
64 : QSqlResultPrivate(q, drv),
65 rowCacheEnd(0),
66 colCount(0),
67 atEnd(false)
68 {
69 }
70
cleanup()71 void QSqlCachedResultPrivate::cleanup()
72 {
73 cache.clear();
74 atEnd = false;
75 colCount = 0;
76 rowCacheEnd = 0;
77 }
78
init(int count,bool fo)79 void 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(count);
87 rowCacheEnd = count;
88 } else {
89 cache.resize(initial_cache_size * count);
90 }
91 }
92
nextIndex()93 int QSqlCachedResultPrivate::nextIndex()
94 {
95 if (forwardOnly)
96 return 0;
97 int newIdx = rowCacheEnd;
98 if (newIdx + colCount > cache.size())
99 cache.resize(qMin(cache.size() * 2, cache.size() + 10000));
100 rowCacheEnd += colCount;
101
102 return newIdx;
103 }
104
canSeek(int i) const105 bool QSqlCachedResultPrivate::canSeek(int i) const
106 {
107 if (forwardOnly || i < 0)
108 return false;
109 return rowCacheEnd >= (i + 1) * colCount;
110 }
111
revertLast()112 void QSqlCachedResultPrivate::revertLast()
113 {
114 if (forwardOnly)
115 return;
116 rowCacheEnd -= colCount;
117 }
118
cacheCount() const119 inline int QSqlCachedResultPrivate::cacheCount() const
120 {
121 Q_ASSERT(!forwardOnly);
122 Q_ASSERT(colCount);
123 return rowCacheEnd / colCount;
124 }
125
126 //////////////
127
QSqlCachedResult(QSqlCachedResultPrivate & d)128 QSqlCachedResult::QSqlCachedResult(QSqlCachedResultPrivate &d)
129 : QSqlResult(d)
130 {
131 }
132
init(int colCount)133 void QSqlCachedResult::init(int colCount)
134 {
135 Q_D(QSqlCachedResult);
136 d->init(colCount, isForwardOnly());
137 }
138
fetch(int i)139 bool 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(d->cache, -1))
152 return false;
153 setAt(at() + 1);
154 }
155 if (!gotoNext(d->cache, 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
fetchNext()178 bool QSqlCachedResult::fetchNext()
179 {
180 Q_D(QSqlCachedResult);
181 if (d->canSeek(at() + 1)) {
182 setAt(at() + 1);
183 return true;
184 }
185 return cacheNext();
186 }
187
fetchPrevious()188 bool QSqlCachedResult::fetchPrevious()
189 {
190 return fetch(at() - 1);
191 }
192
fetchFirst()193 bool QSqlCachedResult::fetchFirst()
194 {
195 Q_D(QSqlCachedResult);
196 if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
197 return false;
198 }
199 if (d->canSeek(0)) {
200 setAt(0);
201 return true;
202 }
203 return cacheNext();
204 }
205
fetchLast()206 bool QSqlCachedResult::fetchLast()
207 {
208 Q_D(QSqlCachedResult);
209 if (d->atEnd) {
210 if (d->forwardOnly)
211 return false;
212 else
213 return fetch(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
data(int i)227 QVariant 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(idx);
235 }
236
isNull(int i)237 bool 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(idx).isNull();
245 }
246
cleanup()247 void QSqlCachedResult::cleanup()
248 {
249 Q_D(QSqlCachedResult);
250 setAt(QSql::BeforeFirstRow);
251 setActive(false);
252 d->cleanup();
253 }
254
clearValues()255 void QSqlCachedResult::clearValues()
256 {
257 Q_D(QSqlCachedResult);
258 setAt(QSql::BeforeFirstRow);
259 d->rowCacheEnd = 0;
260 d->atEnd = false;
261 }
262
cacheNext()263 bool QSqlCachedResult::cacheNext()
264 {
265 Q_D(QSqlCachedResult);
266 if (d->atEnd)
267 return false;
268
269 if(isForwardOnly()) {
270 d->cache.resize(d->colCount);
271 }
272
273 if (!gotoNext(d->cache, d->nextIndex())) {
274 d->revertLast();
275 d->atEnd = true;
276 return false;
277 }
278 setAt(at() + 1);
279 return true;
280 }
281
colCount() const282 int QSqlCachedResult::colCount() const
283 {
284 Q_D(const QSqlCachedResult);
285 return d->colCount;
286 }
287
cache()288 QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
289 {
290 Q_D(QSqlCachedResult);
291 return d->cache;
292 }
293
virtual_hook(int id,void * data)294 void QSqlCachedResult::virtual_hook(int id, void *data)
295 {
296 QSqlResult::virtual_hook(id, data);
297 }
298
detachFromResultSet()299 void QSqlCachedResult::detachFromResultSet()
300 {
301 cleanup();
302 }
303
setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)304 void QSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
305 {
306 QSqlResult::setNumericalPrecisionPolicy(policy);
307 cleanup();
308 }
309
310
311 QT_END_NAMESPACE
312