KDb

SqliteCursor.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "SqliteCursor.h"
21
22#include "SqliteConnection.h"
23#include "SqliteConnection_p.h"
24#include "sqlite_debug.h"
25
26#include "KDbDriver.h"
27#include "KDbError.h"
28#include "KDbRecordData.h"
29#include "KDbUtils.h"
30
31#include <QVector>
32#include <QDateTime>
33#include <QByteArray>
34
35//! safer interpretations of boolean values for SQLite
36static bool sqliteStringToBool(const QString& s)
37{
38 return 0 == s.compare(QLatin1String("yes"), Qt::CaseInsensitive)
39 || (0 != s.compare(QLatin1String("no"), Qt::CaseInsensitive) && s != QLatin1String("0"));
40}
41
42//----------------------------------------------------
43
44class SqliteCursorData : public SqliteConnectionInternal
45{
46public:
47 explicit SqliteCursorData(SqliteConnection* conn)
48 : SqliteConnectionInternal(conn)
49 , prepared_st_handle(nullptr)
50 , utail(nullptr)
51 , curr_coldata(nullptr)
52 , curr_colname(nullptr)
53 , cols_pointers_mem_size(0)
54 {
55 data_owned = false;
56 }
57
58 /*
59 void fetchRowDataIfNeeded()
60 {
61 if (!rowDataReadyToFetch)
62 return true;
63 rowDataReadyToFetch = false;
64 m_fieldCount = sqlite3_data_count(data);
65 for (int i=0; i<m_fieldCount; i++) {
66
67 }
68 }
69 */
70
71 sqlite3_stmt *prepared_st_handle;
72
73 char *utail;
74 const char **curr_coldata;
75 const char **curr_colname;
76 int cols_pointers_mem_size; //!< size of record's array of pointers to values
77 QVector<const char**> records; //!< buffer data
78
79 inline QVariant getValue(KDbField *f, int i) {
80 int type = sqlite3_column_type(prepared_st_handle, i);
81 if (type == SQLITE_NULL) {
82 return QVariant();
83 } else if (!f || type == SQLITE_TEXT) {
84//! @todo support for UTF-16
86 (const char*)sqlite3_column_text(prepared_st_handle, i),
87 sqlite3_column_bytes(prepared_st_handle, i)));
88 if (!f) {
89 return text;
90 }
91 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive
92 if (KDbField::isTextType(t)) {
93 return text;
94 } else if (t == KDbField::Date) {
95 return QDate::fromString(text, Qt::ISODate);
96 } else if (t == KDbField::Time) {
97 //QDateTime - a hack needed because QVariant(QTime) has broken isNull()
98 return KDbUtils::stringToHackedQTime(text);
99 } else if (t == KDbField::DateTime) {
100 if (text.length() > 10) {
101 text[10] = QLatin1Char('T'); //for ISODate compatibility
102 }
103 return KDbUtils::dateTimeFromISODateStringWithMs(text);
104 } else if (t == KDbField::Boolean) {
105 return sqliteStringToBool(text);
106 } else {
107 return QVariant(); //!< @todo
108 }
109 } else if (type == SQLITE_INTEGER) {
110 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive
111 if (t == KDbField::BigInteger) {
112 return QVariant(qint64(sqlite3_column_int64(prepared_st_handle, i)));
113 } else if (KDbField::isIntegerType(t)) {
114 const int intVal = sqlite3_column_int(prepared_st_handle, i);
115 return f->isUnsigned() ? QVariant(static_cast<uint>(intVal)) : QVariant(intVal);
116 } else if (t == KDbField::Boolean) {
117 return sqlite3_column_int(prepared_st_handle, i) != 0;
118 } else if (KDbField::isFPNumericType(t)) { //WEIRD, YEAH?
119 return QVariant(double(sqlite3_column_int(prepared_st_handle, i)));
120 } else {
121 return QVariant(); //!< @todo
122 }
123 } else if (type == SQLITE_FLOAT) {
124 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive
126 return QVariant(sqlite3_column_double(prepared_st_handle, i));
127 } else if (t == KDbField::BigInteger) {
128 return QVariant(qint64(sqlite3_column_int64(prepared_st_handle, i)));
129 } else if (KDbField::isIntegerType(t)) {
130 const double doubleVal = sqlite3_column_double(prepared_st_handle, i);
131 return f->isUnsigned() ? QVariant(static_cast<uint>(doubleVal)) : QVariant(static_cast<int>(doubleVal));
132 } else {
133 return QVariant(); //!< @todo
134 }
135 } else if (type == SQLITE_BLOB) {
136 if (f && f->type() == KDbField::BLOB) {
137//! @todo efficient enough?
138 return QByteArray((const char*)sqlite3_column_blob(prepared_st_handle, i),
139 sqlite3_column_bytes(prepared_st_handle, i));
140 } else
141 return QVariant(); //!< @todo
142 }
143 return QVariant();
144 }
145 Q_DISABLE_COPY(SqliteCursorData)
146};
147
148SqliteCursor::SqliteCursor(SqliteConnection* conn, const KDbEscapedString& sql,
149 Options options)
150 : KDbCursor(conn, sql, options)
151 , d(new SqliteCursorData(conn))
152{
153 d->data = static_cast<SqliteConnection*>(conn)->d->data;
154}
155
156SqliteCursor::SqliteCursor(SqliteConnection* conn, KDbQuerySchema* query, Options options)
157 : KDbCursor(conn, query, options)
158 , d(new SqliteCursorData(conn))
159{
160 d->data = static_cast<SqliteConnection*>(conn)->d->data;
161}
162
163SqliteCursor::~SqliteCursor()
164{
165 close();
166 delete d;
167}
168
169bool SqliteCursor::drv_open(const KDbEscapedString& sql)
170{
171 //! @todo decode
172 if (! d->data) {
173 // this may as example be the case if SqliteConnection::drv_useDatabase()
174 // wasn't called before. Normaly sqlite_compile/sqlite3_prepare
175 // should handle it, but it crashes in in sqlite3SafetyOn at util.c:786
176 sqliteWarning() << "Missing database handle";
177 return false;
178 }
179
180 int res = sqlite3_prepare(
181 d->data, /* Database handle */
182 sql.constData(), /* SQL statement, UTF-8 encoded */
183 sql.length(), /* Length of zSql in bytes. */
184 &d->prepared_st_handle, /* OUT: Statement handle */
185 nullptr/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */
186 );
187 if (res != SQLITE_OK) {
188 m_result.setServerErrorCode(res);
189 storeResult();
190 return false;
191 }
192 if (isBuffered()) {
193//! @todo manage size dynamically
194 d->records.resize(128);
195 }
196
197 return true;
198}
199
200bool SqliteCursor::drv_close()
201{
202 int res = sqlite3_finalize(d->prepared_st_handle);
203 if (res != SQLITE_OK) {
204 m_result.setServerErrorCode(res);
205 storeResult();
206 return false;
207 }
208 return true;
209}
210
211void SqliteCursor::drv_getNextRecord()
212{
213 int res = sqlite3_step(d->prepared_st_handle);
214 if (res == SQLITE_ROW) {
216 m_fieldCount = sqlite3_data_count(d->prepared_st_handle);
217//#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information
218// // -- just set a flag that we've a data not fetched but available
220 }
221 else {
222 if (res == SQLITE_DONE) {
224 } else {
225 m_result.setServerErrorCode(res);
227 }
228 }
229
230 //debug
231 /*
232 if ((int)m_result == (int)FetchResult::Ok && d->curr_coldata) {
233 for (int i=0;i<m_fieldCount;i++) {
234 sqliteDebug()<<"col."<< i<<": "<< d->curr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i]
235 << " = " << (d->curr_coldata[i] ? QString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)");
236 }
237 // sqliteDebug() << m_fieldCount << "col(s) fetched";
238 }*/
239}
240
241void SqliteCursor::drv_appendCurrentRecordToBuffer()
242{
243// sqliteDebug();
244 if (!d->curr_coldata)
245 return;
246 if (!d->cols_pointers_mem_size)
247 d->cols_pointers_mem_size = m_fieldCount * sizeof(char*);
248 const char **record = (const char**)malloc(d->cols_pointers_mem_size);
249 const char **src_col = d->curr_coldata;
250 const char **dest_col = record;
251 for (int i = 0; i < m_fieldCount; i++, src_col++, dest_col++) {
252// sqliteDebug() << i <<": '" << *src_col << "'";
253// sqliteDebug() << "src_col: " << src_col;
254 *dest_col = *src_col ? strdup(*src_col) : nullptr;
255 }
256 d->records[m_records_in_buf] = record;
257// sqliteDebug() << "ok.";
258}
259
260void SqliteCursor::drv_bufferMovePointerNext()
261{
262 d->curr_coldata++; //move to next record in the buffer
263}
264
265void SqliteCursor::drv_bufferMovePointerPrev()
266{
267 d->curr_coldata--; //move to prev record in the buffer
268}
269
270//compute a place in the buffer that contain next record's data
271//and move internal buffer pointer to that place
272void SqliteCursor::drv_bufferMovePointerTo(qint64 at)
273{
274 d->curr_coldata = d->records.at(at);
275}
276
277void SqliteCursor::drv_clearBuffer()
278{
279 if (d->cols_pointers_mem_size > 0) {
280 const int records_in_buf = m_records_in_buf;
281 const char ***r_ptr = d->records.data();
282 for (int i = 0; i < records_in_buf; i++, r_ptr++) {
283 const char **field_data = *r_ptr;
284 for (int col = 0; col < m_fieldCount; col++, field_data++) {
285 free((void*)*field_data); //free field memory
286 }
287 free(*r_ptr); //free pointers to fields array
288 }
289 }
291 d->cols_pointers_mem_size = 0;
292 d->records.clear();
293}
294
295//! @todo
296/*
297const char *** SqliteCursor::bufferData()
298{
299 if (!isBuffered())
300 return 0;
301 return m_records.data();
302}*/
303
304const char ** SqliteCursor::recordData() const
305{
306 return d->curr_coldata;
307}
308
309bool SqliteCursor::drv_storeCurrentRecord(KDbRecordData* data) const
310{
311 if (!m_visibleFieldsExpanded) {//simple version: without types
312 for (int i = 0; i < m_fieldCount; i++) {
313 (*data)[i] = QString::fromUtf8(
314 (const char*)sqlite3_column_text(d->prepared_st_handle, i),
315 sqlite3_column_bytes(d->prepared_st_handle, i));
316 }
317 return true;
318 }
319 for (int i = 0; i < m_fieldCount; ++i) {
320 KDbField *f = m_visibleFieldsExpanded->at(i)->field();
321// sqliteDebug() << "col=" << (col ? *col : 0);
322 (*data)[i] = d->getValue(f, i);
323 }
324 return true;
325}
326
327QVariant SqliteCursor::value(int i)
328{
329 if (i < 0 || i > (m_fieldCount - 1)) //range checking
330 return QVariant();
331//! @todo allow disable range checking! - performance reasons
333 ? m_visibleFieldsExpanded->at(i)->field() : nullptr;
334 return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/);
335}
336
337QString SqliteCursor::serverResultName() const
338{
339 return SqliteConnectionInternal::serverResultName(m_result.serverErrorCode());
340}
341
342void SqliteCursor::storeResult()
343{
344 d->storeResult(&m_result);
345}
Provides database cursor functionality.
Definition KDbCursor.h:69
bool isBuffered() const
@ End
at the end of data
@ Error
error of fetching
@ Ok
the data is fetched
int m_fieldsToStoreInRecord
Used by storeCurrentRecord(), reimplement if needed (e.g.
Definition KDbCursor.h:302
int m_fieldCount
cached field count information
Definition KDbCursor.h:301
int m_records_in_buf
number of records currently stored in the buffer
Definition KDbCursor.h:319
KDbQueryColumnInfo::Vector * m_visibleFieldsExpanded
Useful e.g. for value(int) method to obtain access to schema definition.
Definition KDbCursor.h:324
qint64 at() const
Definition KDbCursor.h:161
FetchResult m_fetchResult
result of a record fetching
Definition KDbCursor.h:316
virtual bool close()
Specialized string for escaping.
Meta-data for a field.
Definition KDbField.h:72
bool isTextType() const
Definition KDbField.h:353
@ Boolean
Definition KDbField.h:92
@ BigInteger
Definition KDbField.h:91
bool isFPNumericType() const
Definition KDbField.h:335
Type type() const
Definition KDbField.cpp:379
bool isIntegerType() const
Definition KDbField.h:326
bool isUnsigned() const
if the type has the unsigned attribute
Definition KDbField.h:515
KDbQuerySchema provides information about database query.
Structure for storing single record with type information.
void setServerErrorCode(int errorCode)
Sets an implementation-specific error code of server-side operation.
Definition KDbResult.cpp:74
SQLite-specific connection Following connection options are supported (see KDbConnectionOptions):
Type type(const QSqlDatabase &db)
std::optional< QSqlQuery > query(const QString &queryStatement)
QDate fromString(QStringView string, QStringView format, QCalendar cal)
const_reference at(qsizetype i) const const
qsizetype count() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromUtf8(QByteArrayView str)
CaseInsensitive
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.