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