KDb

SqliteCursor.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2016 JarosÅ‚aw Staniek <[email protected]>
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
36 static 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 
44 class SqliteCursorData : public SqliteConnectionInternal
45 {
46 public:
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
125  if (KDbField::isFPNumericType(t)) {
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 
148 SqliteCursor::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 
156 SqliteCursor::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 
163 SqliteCursor::~SqliteCursor()
164 {
165  close();
166  delete d;
167 }
168 
169 bool 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 
200 bool 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 
211 void SqliteCursor::drv_getNextRecord()
212 {
213  int res = sqlite3_step(d->prepared_st_handle);
214  if (res == SQLITE_ROW) {
215  m_fetchResult = FetchResult::Ok;
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
219  m_fieldsToStoreInRecord = m_fieldCount;
220  }
221  else {
222  if (res == SQLITE_DONE) {
223  m_fetchResult = FetchResult::End;
224  } else {
225  m_result.setServerErrorCode(res);
226  m_fetchResult = FetchResult::Error;
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 
241 void 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 
260 void SqliteCursor::drv_bufferMovePointerNext()
261 {
262  d->curr_coldata++; //move to next record in the buffer
263 }
264 
265 void 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
272 void SqliteCursor::drv_bufferMovePointerTo(qint64 at)
273 {
274  d->curr_coldata = d->records.at(at);
275 }
276 
277 void 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  }
290  m_records_in_buf = 0;
291  d->cols_pointers_mem_size = 0;
292  d->records.clear();
293 }
294 
295 //! @todo
296 /*
297 const char *** SqliteCursor::bufferData()
298 {
299  if (!isBuffered())
300  return 0;
301  return m_records.data();
302 }*/
303 
304 const char ** SqliteCursor::recordData() const
305 {
306  return d->curr_coldata;
307 }
308 
309 bool 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 
327 QVariant 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
332  KDbField *f = (m_visibleFieldsExpanded && i < m_visibleFieldsExpanded->count())
333  ? m_visibleFieldsExpanded->at(i)->field() : nullptr;
334  return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/);
335 }
336 
337 QString SqliteCursor::serverResultName() const
338 {
339  return SqliteConnectionInternal::serverResultName(m_result.serverErrorCode());
340 }
341 
342 void SqliteCursor::storeResult()
343 {
344  d->storeResult(&m_result);
345 }
Provides database cursor functionality.
Definition: KDbCursor.h:68
QString fromUtf8(const char *str, int size)
CaseInsensitive
Type type(const QSqlDatabase &db)
Specialized string for escaping.
const QList< QKeySequence > & close()
bool isFPNumericType() const
Definition: KDbField.h:335
bool isUnsigned() const
if the type has the unsigned attribute
Definition: KDbField.h:515
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
SQLite-specific connection Following connection options are supported (see KDbConnectionOptions):
bool isTextType() const
Definition: KDbField.h:353
bool isIntegerType() const
Definition: KDbField.h:326
@ BigInteger
Definition: KDbField.h:91
Type type() const
Definition: KDbField.cpp:379
QDate fromString(const QString &string, Qt::DateFormat format)
@ Boolean
Definition: KDbField.h:92
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
KDbQuerySchema provides information about database query.
Meta-data for a field.
Definition: KDbField.h:71
int compare(const QString &other, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:34 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.