File indexing completed on 2024-04-28 15:58:53
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 "SqliteConnection_p.h" 0021 0022 SqliteConnectionInternal::SqliteConnectionInternal(KDbConnection *connection) 0023 : KDbConnectionInternal(connection) 0024 , data(nullptr) 0025 , data_owned(true) 0026 , m_extensionsLoadingEnabled(false) 0027 { 0028 } 0029 0030 SqliteConnectionInternal::~SqliteConnectionInternal() 0031 { 0032 if (data_owned && data) { 0033 sqlite3_close(data); 0034 data = nullptr; 0035 } 0036 } 0037 0038 static const char* const serverResultNames[] = { 0039 "SQLITE_OK", // 0 0040 "SQLITE_ERROR", 0041 "SQLITE_INTERNAL", 0042 "SQLITE_PERM", 0043 "SQLITE_ABORT", 0044 "SQLITE_BUSY", 0045 "SQLITE_LOCKED", 0046 "SQLITE_NOMEM", 0047 "SQLITE_READONLY", 0048 "SQLITE_INTERRUPT", 0049 "SQLITE_IOERR", 0050 "SQLITE_CORRUPT", 0051 "SQLITE_NOTFOUND", 0052 "SQLITE_FULL", 0053 "SQLITE_CANTOPEN", 0054 "SQLITE_PROTOCOL", 0055 "SQLITE_EMPTY", 0056 "SQLITE_SCHEMA", 0057 "SQLITE_TOOBIG", 0058 "SQLITE_CONSTRAINT", 0059 "SQLITE_MISMATCH", 0060 "SQLITE_MISUSE", 0061 "SQLITE_NOLFS", 0062 "SQLITE_AUTH", 0063 "SQLITE_FORMAT", 0064 "SQLITE_RANGE", 0065 "SQLITE_NOTADB", // 26 0066 }; 0067 0068 // static 0069 QString SqliteConnectionInternal::serverResultName(int serverResultCode) 0070 { 0071 if (serverResultCode >= 0 && serverResultCode <= SQLITE_NOTADB) 0072 return QString::fromLatin1(serverResultNames[serverResultCode]); 0073 else if (serverResultCode == SQLITE_ROW) 0074 return QLatin1String("SQLITE_ROW"); 0075 else if (serverResultCode == SQLITE_DONE) 0076 return QLatin1String("SQLITE_DONE"); 0077 return QString(); 0078 } 0079 0080 void SqliteConnectionInternal::storeResult(KDbResult *result) 0081 { 0082 result->setServerMessage( 0083 (data && result->isError()) ? QString::fromUtf8(sqlite3_errmsg(data)) 0084 : QString()); 0085 } 0086 0087 bool SqliteConnectionInternal::extensionsLoadingEnabled() const 0088 { 0089 return m_extensionsLoadingEnabled; 0090 } 0091 0092 void SqliteConnectionInternal::setExtensionsLoadingEnabled(bool set) 0093 { 0094 if (set == m_extensionsLoadingEnabled) 0095 return; 0096 sqlite3_enable_load_extension(data, set); 0097 m_extensionsLoadingEnabled = set; 0098 } 0099 0100 //static 0101 KDbField::Type SqliteSqlResult::type(int sqliteType) 0102 { 0103 KDbField::Type t; 0104 switch(sqliteType) { 0105 case SQLITE_INTEGER: t = KDbField::Integer; break; 0106 case SQLITE_FLOAT: t = KDbField::Double; break; 0107 case SQLITE_BLOB: t = KDbField::BLOB; break; 0108 case SQLITE_NULL: t = KDbField::Null; break; 0109 case SQLITE_TEXT: t = KDbField::LongText; break; 0110 default: t = KDbField::InvalidType; break; 0111 } 0112 return t; 0113 } 0114 0115 bool SqliteSqlResult::setConstraints(const QString &tableName, KDbField* field) 0116 { 0117 Q_ASSERT(field); 0118 if (!cacheFieldInfo(tableName)) { 0119 return false; 0120 } 0121 SqliteSqlFieldInfo* info = cachedFieldInfos.value(field->name()); 0122 if (info) { 0123 info->setConstraints(field); 0124 return true; 0125 } else { 0126 return false; 0127 } 0128 } 0129 0130 void SqliteSqlFieldInfo::setConstraints(KDbField* field) 0131 { 0132 field->setDefaultValue(KDbField::convertToType(defaultValue, field->type())); 0133 field->setNotNull(isNotNull); 0134 field->setPrimaryKey(isPrimaryKey); 0135 } 0136 0137 bool SqliteSqlResult::cacheFieldInfo(const QString &tableName) 0138 { 0139 if (!cachedFieldInfos.isEmpty()) { 0140 return true; 0141 } 0142 QSharedPointer<KDbSqlResult> tableInfoResult = conn->prepareSql( 0143 KDbEscapedString("PRAGMA table_info(%1)").arg(conn->escapeIdentifier(tableName))); 0144 if (!tableInfoResult) { 0145 return false; 0146 } 0147 // Forward-compatible approach: find columns of table_info that we need 0148 const int columns = tableInfoResult->fieldsCount(); 0149 enum TableInfoColumns { 0150 TableInfoFieldName, 0151 TableInfoNotNull, 0152 TableInfoDefault, 0153 TableInfoPK 0154 }; 0155 QVector<int> columnIndex(TableInfoPK + 1); 0156 int found = 0; 0157 for(int col = 0; col < columns; ++col) { 0158 QScopedPointer<KDbSqlField> f(tableInfoResult->field(col)); 0159 if (!f) { 0160 return false; 0161 } 0162 if (f->name() == QLatin1String("name")) { 0163 columnIndex[TableInfoFieldName] = col; 0164 ++found; 0165 } else if (f->name() == QLatin1String("notnull")) { 0166 columnIndex[TableInfoNotNull] = col; 0167 ++found; 0168 } else if (f->name() == QLatin1String("dflt_value")) { 0169 columnIndex[TableInfoDefault] = col; 0170 ++found; 0171 } else if (f->name() == QLatin1String("pk")) { 0172 columnIndex[TableInfoPK] = col; 0173 ++found; 0174 } 0175 } 0176 if (found != TableInfoPK + 1) { // not all columns found 0177 return false; 0178 } 0179 0180 bool ok = true; 0181 Q_FOREVER { 0182 QSharedPointer<KDbSqlRecord> record = tableInfoResult->fetchRecord(); 0183 if (!record) { 0184 ok = !tableInfoResult->lastResult().isError(); 0185 break; 0186 } 0187 QScopedPointer<SqliteSqlFieldInfo> info(new SqliteSqlFieldInfo); 0188 const QString name = record->stringValue(columnIndex[TableInfoFieldName]); 0189 if (name.isEmpty()) { 0190 ok = false; 0191 break; 0192 } 0193 info->defaultValue = record->stringValue(columnIndex[TableInfoDefault]); 0194 info->isNotNull 0195 = record->cstringValue(columnIndex[TableInfoNotNull]).rawDataToByteArray() == "1"; 0196 //! @todo Support composite primary keys: 0197 //! The "pk" column in the result set is zero for columns that are not part of 0198 //! the primary key, and is the index of the column in the primary key for columns 0199 //! that are part of the primary key. 0200 //! https://www.sqlite.org/pragma.html#pragma_table_info 0201 info->isPrimaryKey 0202 = record->cstringValue(columnIndex[TableInfoPK]).rawDataToByteArray() != "0"; 0203 cachedFieldInfos.insert(name, info.take()); 0204 } 0205 if (!ok) { 0206 cachedFieldInfos.clear(); 0207 } 0208 return ok; 0209 } 0210 0211 KDbField *SqliteSqlResult::createField(const QString &tableName, int index) 0212 { 0213 QScopedPointer<SqliteSqlField> f(static_cast<SqliteSqlField*>(field(index))); 0214 if (!f) { 0215 return nullptr; 0216 } 0217 const QString caption(f->name()); 0218 QString realFieldName(KDb::stringToIdentifier(caption.toLower())); 0219 KDbField *kdbField = new KDbField(realFieldName, type(f->type())); 0220 kdbField->setCaption(caption); 0221 setConstraints(tableName, kdbField); 0222 return kdbField; 0223 }