File indexing completed on 2024-04-28 15:58:54
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this program; see the file COPYING. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "SqliteCursor.h" 0021 0022 #include "SqliteConnection.h" 0023 #include "SqliteConnection_p.h" 0024 #include "sqlite_debug.h" 0025 0026 #include "KDbDriver.h" 0027 #include "KDbError.h" 0028 #include "KDbRecordData.h" 0029 #include "KDbUtils.h" 0030 0031 #include <QVector> 0032 #include <QDateTime> 0033 #include <QByteArray> 0034 0035 //! safer interpretations of boolean values for SQLite 0036 static bool sqliteStringToBool(const QString& s) 0037 { 0038 return 0 == s.compare(QLatin1String("yes"), Qt::CaseInsensitive) 0039 || (0 != s.compare(QLatin1String("no"), Qt::CaseInsensitive) && s != QLatin1String("0")); 0040 } 0041 0042 //---------------------------------------------------- 0043 0044 class SqliteCursorData : public SqliteConnectionInternal 0045 { 0046 public: 0047 explicit SqliteCursorData(SqliteConnection* conn) 0048 : SqliteConnectionInternal(conn) 0049 , prepared_st_handle(nullptr) 0050 , utail(nullptr) 0051 , curr_coldata(nullptr) 0052 , curr_colname(nullptr) 0053 , cols_pointers_mem_size(0) 0054 { 0055 data_owned = false; 0056 } 0057 0058 /* 0059 void fetchRowDataIfNeeded() 0060 { 0061 if (!rowDataReadyToFetch) 0062 return true; 0063 rowDataReadyToFetch = false; 0064 m_fieldCount = sqlite3_data_count(data); 0065 for (int i=0; i<m_fieldCount; i++) { 0066 0067 } 0068 } 0069 */ 0070 0071 sqlite3_stmt *prepared_st_handle; 0072 0073 char *utail; 0074 const char **curr_coldata; 0075 const char **curr_colname; 0076 int cols_pointers_mem_size; //!< size of record's array of pointers to values 0077 QVector<const char**> records; //!< buffer data 0078 0079 inline QVariant getValue(KDbField *f, int i) { 0080 int type = sqlite3_column_type(prepared_st_handle, i); 0081 if (type == SQLITE_NULL) { 0082 return QVariant(); 0083 } else if (!f || type == SQLITE_TEXT) { 0084 //! @todo support for UTF-16 0085 QString text(QString::fromUtf8( 0086 (const char*)sqlite3_column_text(prepared_st_handle, i), 0087 sqlite3_column_bytes(prepared_st_handle, i))); 0088 if (!f) { 0089 return text; 0090 } 0091 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive 0092 if (KDbField::isTextType(t)) { 0093 return text; 0094 } else if (t == KDbField::Date) { 0095 return QDate::fromString(text, Qt::ISODate); 0096 } else if (t == KDbField::Time) { 0097 //QDateTime - a hack needed because QVariant(QTime) has broken isNull() 0098 return KDbUtils::stringToHackedQTime(text); 0099 } else if (t == KDbField::DateTime) { 0100 if (text.length() > 10) { 0101 text[10] = QLatin1Char('T'); //for ISODate compatibility 0102 } 0103 return KDbUtils::dateTimeFromISODateStringWithMs(text); 0104 } else if (t == KDbField::Boolean) { 0105 return sqliteStringToBool(text); 0106 } else { 0107 return QVariant(); //!< @todo 0108 } 0109 } else if (type == SQLITE_INTEGER) { 0110 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive 0111 if (t == KDbField::BigInteger) { 0112 return QVariant(qint64(sqlite3_column_int64(prepared_st_handle, i))); 0113 } else if (KDbField::isIntegerType(t)) { 0114 const int intVal = sqlite3_column_int(prepared_st_handle, i); 0115 return f->isUnsigned() ? QVariant(static_cast<uint>(intVal)) : QVariant(intVal); 0116 } else if (t == KDbField::Boolean) { 0117 return sqlite3_column_int(prepared_st_handle, i) != 0; 0118 } else if (KDbField::isFPNumericType(t)) { //WEIRD, YEAH? 0119 return QVariant(double(sqlite3_column_int(prepared_st_handle, i))); 0120 } else { 0121 return QVariant(); //!< @todo 0122 } 0123 } else if (type == SQLITE_FLOAT) { 0124 const KDbField::Type t = f->type(); // cache: evaluating type of expressions can be expensive 0125 if (KDbField::isFPNumericType(t)) { 0126 return QVariant(sqlite3_column_double(prepared_st_handle, i)); 0127 } else if (t == KDbField::BigInteger) { 0128 return QVariant(qint64(sqlite3_column_int64(prepared_st_handle, i))); 0129 } else if (KDbField::isIntegerType(t)) { 0130 const double doubleVal = sqlite3_column_double(prepared_st_handle, i); 0131 return f->isUnsigned() ? QVariant(static_cast<uint>(doubleVal)) : QVariant(static_cast<int>(doubleVal)); 0132 } else { 0133 return QVariant(); //!< @todo 0134 } 0135 } else if (type == SQLITE_BLOB) { 0136 if (f && f->type() == KDbField::BLOB) { 0137 //! @todo efficient enough? 0138 return QByteArray((const char*)sqlite3_column_blob(prepared_st_handle, i), 0139 sqlite3_column_bytes(prepared_st_handle, i)); 0140 } else 0141 return QVariant(); //!< @todo 0142 } 0143 return QVariant(); 0144 } 0145 Q_DISABLE_COPY(SqliteCursorData) 0146 }; 0147 0148 SqliteCursor::SqliteCursor(SqliteConnection* conn, const KDbEscapedString& sql, 0149 Options options) 0150 : KDbCursor(conn, sql, options) 0151 , d(new SqliteCursorData(conn)) 0152 { 0153 d->data = static_cast<SqliteConnection*>(conn)->d->data; 0154 } 0155 0156 SqliteCursor::SqliteCursor(SqliteConnection* conn, KDbQuerySchema* query, Options options) 0157 : KDbCursor(conn, query, options) 0158 , d(new SqliteCursorData(conn)) 0159 { 0160 d->data = static_cast<SqliteConnection*>(conn)->d->data; 0161 } 0162 0163 SqliteCursor::~SqliteCursor() 0164 { 0165 close(); 0166 delete d; 0167 } 0168 0169 bool SqliteCursor::drv_open(const KDbEscapedString& sql) 0170 { 0171 //! @todo decode 0172 if (! d->data) { 0173 // this may as example be the case if SqliteConnection::drv_useDatabase() 0174 // wasn't called before. Normaly sqlite_compile/sqlite3_prepare 0175 // should handle it, but it crashes in in sqlite3SafetyOn at util.c:786 0176 sqliteWarning() << "Missing database handle"; 0177 return false; 0178 } 0179 0180 int res = sqlite3_prepare( 0181 d->data, /* Database handle */ 0182 sql.constData(), /* SQL statement, UTF-8 encoded */ 0183 sql.length(), /* Length of zSql in bytes. */ 0184 &d->prepared_st_handle, /* OUT: Statement handle */ 0185 nullptr/*const char **pzTail*/ /* OUT: Pointer to unused portion of zSql */ 0186 ); 0187 if (res != SQLITE_OK) { 0188 m_result.setServerErrorCode(res); 0189 storeResult(); 0190 return false; 0191 } 0192 if (isBuffered()) { 0193 //! @todo manage size dynamically 0194 d->records.resize(128); 0195 } 0196 0197 return true; 0198 } 0199 0200 bool SqliteCursor::drv_close() 0201 { 0202 int res = sqlite3_finalize(d->prepared_st_handle); 0203 if (res != SQLITE_OK) { 0204 m_result.setServerErrorCode(res); 0205 storeResult(); 0206 return false; 0207 } 0208 return true; 0209 } 0210 0211 void SqliteCursor::drv_getNextRecord() 0212 { 0213 int res = sqlite3_step(d->prepared_st_handle); 0214 if (res == SQLITE_ROW) { 0215 m_fetchResult = FetchResult::Ok; 0216 m_fieldCount = sqlite3_data_count(d->prepared_st_handle); 0217 //#else //for SQLITE3 data fetching is delayed. Now we even do not take field count information 0218 // // -- just set a flag that we've a data not fetched but available 0219 m_fieldsToStoreInRecord = m_fieldCount; 0220 } 0221 else { 0222 if (res == SQLITE_DONE) { 0223 m_fetchResult = FetchResult::End; 0224 } else { 0225 m_result.setServerErrorCode(res); 0226 m_fetchResult = FetchResult::Error; 0227 } 0228 } 0229 0230 //debug 0231 /* 0232 if ((int)m_result == (int)FetchResult::Ok && d->curr_coldata) { 0233 for (int i=0;i<m_fieldCount;i++) { 0234 sqliteDebug()<<"col."<< i<<": "<< d->curr_colname[i]<<" "<< d->curr_colname[m_fieldCount+i] 0235 << " = " << (d->curr_coldata[i] ? QString::fromLocal8Bit(d->curr_coldata[i]) : "(NULL)"); 0236 } 0237 // sqliteDebug() << m_fieldCount << "col(s) fetched"; 0238 }*/ 0239 } 0240 0241 void SqliteCursor::drv_appendCurrentRecordToBuffer() 0242 { 0243 // sqliteDebug(); 0244 if (!d->curr_coldata) 0245 return; 0246 if (!d->cols_pointers_mem_size) 0247 d->cols_pointers_mem_size = m_fieldCount * sizeof(char*); 0248 const char **record = (const char**)malloc(d->cols_pointers_mem_size); 0249 const char **src_col = d->curr_coldata; 0250 const char **dest_col = record; 0251 for (int i = 0; i < m_fieldCount; i++, src_col++, dest_col++) { 0252 // sqliteDebug() << i <<": '" << *src_col << "'"; 0253 // sqliteDebug() << "src_col: " << src_col; 0254 *dest_col = *src_col ? strdup(*src_col) : nullptr; 0255 } 0256 d->records[m_records_in_buf] = record; 0257 // sqliteDebug() << "ok."; 0258 } 0259 0260 void SqliteCursor::drv_bufferMovePointerNext() 0261 { 0262 d->curr_coldata++; //move to next record in the buffer 0263 } 0264 0265 void SqliteCursor::drv_bufferMovePointerPrev() 0266 { 0267 d->curr_coldata--; //move to prev record in the buffer 0268 } 0269 0270 //compute a place in the buffer that contain next record's data 0271 //and move internal buffer pointer to that place 0272 void SqliteCursor::drv_bufferMovePointerTo(qint64 at) 0273 { 0274 d->curr_coldata = d->records.at(at); 0275 } 0276 0277 void SqliteCursor::drv_clearBuffer() 0278 { 0279 if (d->cols_pointers_mem_size > 0) { 0280 const int records_in_buf = m_records_in_buf; 0281 const char ***r_ptr = d->records.data(); 0282 for (int i = 0; i < records_in_buf; i++, r_ptr++) { 0283 const char **field_data = *r_ptr; 0284 for (int col = 0; col < m_fieldCount; col++, field_data++) { 0285 free((void*)*field_data); //free field memory 0286 } 0287 free(*r_ptr); //free pointers to fields array 0288 } 0289 } 0290 m_records_in_buf = 0; 0291 d->cols_pointers_mem_size = 0; 0292 d->records.clear(); 0293 } 0294 0295 //! @todo 0296 /* 0297 const char *** SqliteCursor::bufferData() 0298 { 0299 if (!isBuffered()) 0300 return 0; 0301 return m_records.data(); 0302 }*/ 0303 0304 const char ** SqliteCursor::recordData() const 0305 { 0306 return d->curr_coldata; 0307 } 0308 0309 bool SqliteCursor::drv_storeCurrentRecord(KDbRecordData* data) const 0310 { 0311 if (!m_visibleFieldsExpanded) {//simple version: without types 0312 for (int i = 0; i < m_fieldCount; i++) { 0313 (*data)[i] = QString::fromUtf8( 0314 (const char*)sqlite3_column_text(d->prepared_st_handle, i), 0315 sqlite3_column_bytes(d->prepared_st_handle, i)); 0316 } 0317 return true; 0318 } 0319 for (int i = 0; i < m_fieldCount; ++i) { 0320 KDbField *f = m_visibleFieldsExpanded->at(i)->field(); 0321 // sqliteDebug() << "col=" << (col ? *col : 0); 0322 (*data)[i] = d->getValue(f, i); 0323 } 0324 return true; 0325 } 0326 0327 QVariant SqliteCursor::value(int i) 0328 { 0329 if (i < 0 || i > (m_fieldCount - 1)) //range checking 0330 return QVariant(); 0331 //! @todo allow disable range checking! - performance reasons 0332 KDbField *f = (m_visibleFieldsExpanded && i < m_visibleFieldsExpanded->count()) 0333 ? m_visibleFieldsExpanded->at(i)->field() : nullptr; 0334 return d->getValue(f, i); //, i==m_logicalFieldCount/*ROWID*/); 0335 } 0336 0337 QString SqliteCursor::serverResultName() const 0338 { 0339 return SqliteConnectionInternal::serverResultName(m_result.serverErrorCode()); 0340 } 0341 0342 void SqliteCursor::storeResult() 0343 { 0344 d->storeResult(&m_result); 0345 }