File indexing completed on 2024-12-08 07:18:16
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004-2018 Jarosław Staniek <staniek@kde.org> 0003 Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com> 0004 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 0005 Copyright (c) 1997 Matthias Kalle Dalheimer <kalle@kde.org> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "KDb.h" 0024 #include "KDbConnection.h" 0025 #include "KDbConnectionData.h" 0026 #include "KDbCursor.h" 0027 #include "KDbDateTime.h" 0028 #include "KDbDriverBehavior.h" 0029 #include "KDbDriverManager.h" 0030 #include "KDbDriver_p.h" 0031 #include "KDbLookupFieldSchema.h" 0032 #include "KDbMessageHandler.h" 0033 #include "KDbNativeStatementBuilder.h" 0034 #include "KDbQuerySchema.h" 0035 #include "KDbRecordData.h" 0036 #include "KDbSqlResult.h" 0037 #include "KDbTableOrQuerySchema.h" 0038 #include "KDbVersionInfo.h" 0039 #include "KDb_p.h" 0040 #include "kdb_debug.h" 0041 #include "transliteration/transliteration_table.h" 0042 0043 #include <QMap> 0044 #include <QHash> 0045 #include <QBuffer> 0046 #include <QPixmap> 0047 #include <QSet> 0048 #include <QTimer> 0049 #include <QThread> 0050 #include <QProgressDialog> 0051 #include <QDomNode> 0052 #include <QApplication> 0053 #include <QDir> 0054 #include <QProcess> 0055 #include <QtDebug> 0056 0057 #include <limits> 0058 #include <memory> 0059 0060 Q_DECLARE_METATYPE(KDbField::Type) 0061 0062 class ConnectionTestDialog; 0063 0064 class ConnectionTestThread : public QThread 0065 { 0066 Q_OBJECT 0067 public: 0068 ConnectionTestThread(ConnectionTestDialog *dlg, const KDbConnectionData& connData); 0069 void run() override; 0070 Q_SIGNALS: 0071 void error(const QString& msg, const QString& details); 0072 protected: 0073 void emitError(const KDbResultable& KDbResultable); 0074 0075 ConnectionTestDialog* m_dlg; 0076 KDbConnectionData m_connData; 0077 KDbDriver *m_driver; 0078 private: 0079 Q_DISABLE_COPY(ConnectionTestThread) 0080 }; 0081 0082 class ConnectionTestDialog : public QProgressDialog // krazy:exclude=qclasses 0083 { 0084 Q_OBJECT 0085 public: 0086 ConnectionTestDialog(const KDbConnectionData& data, KDbMessageHandler* msgHandler, 0087 QWidget* parent = nullptr); 0088 ~ConnectionTestDialog() override; 0089 0090 int exec() override; 0091 0092 public Q_SLOTS: 0093 void error(const QString& msg, const QString& details); 0094 0095 protected Q_SLOTS: 0096 void slotTimeout(); 0097 void accept() override; 0098 void reject() override; 0099 0100 protected: 0101 void finish(); 0102 0103 QPointer<ConnectionTestThread> m_thread; 0104 KDbConnectionData m_connData; 0105 QTimer m_timer; 0106 KDbMessageHandler* m_msgHandler; 0107 int m_elapsedTime; 0108 bool m_error; 0109 QString m_msg; 0110 QString m_details; 0111 bool m_stopWaiting; 0112 0113 private: 0114 Q_DISABLE_COPY(ConnectionTestDialog) 0115 }; 0116 0117 ConnectionTestThread::ConnectionTestThread(ConnectionTestDialog* dlg, const KDbConnectionData& connData) 0118 : m_dlg(dlg), m_connData(connData) 0119 { 0120 connect(this, SIGNAL(error(QString,QString)), 0121 dlg, SLOT(error(QString,QString)), Qt::QueuedConnection); 0122 0123 // try to load driver now because it's not supported in different thread 0124 KDbDriverManager manager; 0125 m_driver = manager.driver(m_connData.driverId()); 0126 if (manager.result().isError()) { 0127 emitError(*manager.resultable()); 0128 m_driver = nullptr; 0129 } 0130 } 0131 0132 void ConnectionTestThread::emitError(const KDbResultable& KDbResultable) 0133 { 0134 QString msg; 0135 QString details; 0136 KDb::getHTMLErrorMesage(KDbResultable, &msg, &details); 0137 emit error(msg, details); 0138 } 0139 0140 void ConnectionTestThread::run() 0141 { 0142 if (!m_driver) { 0143 return; 0144 } 0145 QScopedPointer<KDbConnection> conn(m_driver->createConnection(m_connData)); 0146 if (conn.isNull() || m_driver->result().isError()) { 0147 emitError(*m_driver); 0148 return; 0149 } 0150 if (!conn->connect() || conn->result().isError()) { 0151 emitError(*conn); 0152 return; 0153 } 0154 // SQL database backends like PostgreSQL require executing "USE database" 0155 // if we really want to know connection to the server succeeded. 0156 QString tmpDbName; 0157 if (!conn->useTemporaryDatabaseIfNeeded(&tmpDbName)) { 0158 emitError(*conn); 0159 return; 0160 } 0161 if (!tmpDbName.isEmpty()) { 0162 if (!conn->closeDatabase()) { 0163 emitError(*conn); 0164 } 0165 } 0166 emitError(KDbResultable()); 0167 } 0168 0169 ConnectionTestDialog::ConnectionTestDialog(const KDbConnectionData& data, 0170 KDbMessageHandler* msgHandler, QWidget* parent) 0171 : QProgressDialog(parent) 0172 , m_thread(new ConnectionTestThread(this, data)) 0173 , m_connData(data) 0174 , m_msgHandler(msgHandler) 0175 , m_elapsedTime(0) 0176 , m_error(false) 0177 , m_stopWaiting(false) 0178 { 0179 setWindowTitle(tr("Test Connection", "Dialog's title: testing database connection")); 0180 setLabelText(tr("Testing connection to \"%1\" database server...") 0181 .arg(data.toUserVisibleString())); 0182 setModal(true); 0183 setRange(0, 0); //to show busy indicator 0184 connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout())); 0185 adjustSize(); 0186 resize(250, height()); 0187 } 0188 0189 ConnectionTestDialog::~ConnectionTestDialog() 0190 { 0191 if (m_thread->isRunning()) { 0192 m_thread->terminate(); 0193 } 0194 m_thread->deleteLater(); 0195 } 0196 0197 int ConnectionTestDialog::exec() 0198 { 0199 //kdbDebug() << "tid:" << QThread::currentThread() << "this_thread:" << thread(); 0200 m_timer.start(20); 0201 m_thread->start(); 0202 const int res = QProgressDialog::exec(); // krazy:exclude=qclasses 0203 m_thread->wait(); 0204 m_timer.stop(); 0205 return res; 0206 } 0207 0208 void ConnectionTestDialog::slotTimeout() 0209 { 0210 //kdbDebug() << "tid:" << QThread::currentThread() << "this_thread:" << thread(); 0211 //kdbDebug() << m_error; 0212 bool notResponding = false; 0213 if (m_elapsedTime >= 1000*5) {//5 seconds 0214 m_stopWaiting = true; 0215 notResponding = true; 0216 } 0217 //kdbDebug() << m_elapsedTime << m_stopWaiting << notResponding; 0218 if (m_stopWaiting) { 0219 m_timer.disconnect(this); 0220 m_timer.stop(); 0221 QString message; 0222 QString details; 0223 KDbMessageHandler::MessageType type; 0224 if (m_error) { 0225 reject(); 0226 //kdbDebug() << "after reject"; 0227 message = tr("Test connection to \"%1\" database server failed.") 0228 .arg(m_connData.toUserVisibleString()); 0229 details = m_msg; 0230 if (!m_details.isEmpty()) { 0231 details += QLatin1Char('\n') + m_details; 0232 } 0233 type = KDbMessageHandler::Sorry; 0234 m_error = false; 0235 } else if (notResponding) { 0236 reject(); 0237 //kdbDebug() << "after reject"; 0238 message = tr("Test connection to \"%1\" database server failed. The server is not responding.") 0239 .arg(m_connData.toUserVisibleString()); 0240 type = KDbMessageHandler::Sorry; 0241 } else { 0242 accept(); 0243 //kdbDebug() << "after accept"; 0244 message = tr("Test connection to \"%1\" database server established successfully.") 0245 .arg(m_connData.toUserVisibleString()), 0246 type = KDbMessageHandler::Information; 0247 } 0248 if (m_msgHandler) { 0249 m_msgHandler->showErrorMessage(type, message, details, tr("Test Connection")); 0250 } 0251 return; 0252 } 0253 m_elapsedTime += 20; 0254 setValue(m_elapsedTime); 0255 } 0256 0257 void ConnectionTestDialog::error(const QString& msg, const QString& details) 0258 { 0259 //kdbDebug() << "tid:" << QThread::currentThread() << "this_thread:" << thread(); 0260 //kdbDebug() << msg << details; 0261 m_stopWaiting = true; 0262 m_msg = msg; 0263 m_details = details; 0264 m_error = !msg.isEmpty() || !details.isEmpty(); 0265 if (m_error) { 0266 kdbDebug() << "Error:" << msg << details; 0267 } 0268 } 0269 0270 void ConnectionTestDialog::accept() 0271 { 0272 finish(); 0273 QProgressDialog::accept(); // krazy:exclude=qclasses 0274 } 0275 0276 void ConnectionTestDialog::reject() 0277 { 0278 finish(); 0279 QProgressDialog::reject(); // krazy:exclude=qclasses 0280 } 0281 0282 void ConnectionTestDialog::finish() 0283 { 0284 if (m_thread->isRunning()) { 0285 m_thread->terminate(); 0286 } 0287 m_timer.disconnect(this); 0288 m_timer.stop(); 0289 } 0290 0291 // ---- 0292 0293 //! @return hex digit converted to integer (0 to 15), 0xFF on failure 0294 inline static unsigned char hexDigitToInt(char digit) 0295 { 0296 if (digit >= '0' && digit <= '9') { 0297 return digit - '0'; 0298 } 0299 if (digit >= 'a' && digit <= 'f') { 0300 return digit - 'a' + 10; 0301 } 0302 if (digit >= 'A' && digit <= 'F') { 0303 return digit - 'A' + 10; 0304 } 0305 return 0xFF; 0306 } 0307 0308 //! Converts textual representation @a data of a hex number (@a length digits) to a byte array @a array 0309 //! @return true on success and false if @a data contains characters that are not hex digits. 0310 //! true is returned for empty @a data as well. 0311 inline static bool hexToByteArrayInternal(const char* data, int length, QByteArray *array) 0312 { 0313 Q_ASSERT(length >= 0); 0314 Q_ASSERT(data || length == 0); 0315 array->resize(length / 2 + length % 2); 0316 for(int i = 0; length > 0; --length, ++data, ++i) { 0317 unsigned char d1 = hexDigitToInt(data[0]); 0318 unsigned char d2; 0319 if (i == 0 && (length % 2) == 1) { // odd number of digits; no leading 0 0320 d2 = d1; 0321 d1 = 0; 0322 } 0323 else { 0324 --length; 0325 ++data; 0326 d2 = hexDigitToInt(data[0]); 0327 } 0328 if (d1 == 0xFF || d2 == 0xFF) { 0329 return false; 0330 } 0331 (*array)[i] = (d1 << 4) + d2; 0332 } 0333 return true; 0334 } 0335 0336 KDbVersionInfo KDb::version() 0337 { 0338 return KDbVersionInfo( 0339 KDB_VERSION_MAJOR, KDB_VERSION_MINOR, KDB_VERSION_PATCH); 0340 } 0341 0342 bool KDb::deleteRecords(KDbConnection* conn, const QString &tableName, 0343 const QString &keyname, KDbField::Type keytype, const QVariant &keyval) 0344 { 0345 return conn 0346 ? conn->executeSql(KDbEscapedString("DELETE FROM %1 WHERE %2=%3") 0347 .arg(conn->escapeIdentifier(tableName)) 0348 .arg(conn->escapeIdentifier(keyname)) 0349 .arg(conn->driver()->valueToSql(keytype, keyval))) 0350 : false; 0351 } 0352 0353 bool KDb::deleteRecords(KDbConnection* conn, const QString &tableName, 0354 const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1, 0355 const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2) 0356 { 0357 return conn 0358 ? conn->executeSql(KDbEscapedString("DELETE FROM %1 WHERE %2=%3 AND %4=%5") 0359 .arg(conn->escapeIdentifier(tableName)) 0360 .arg(conn->escapeIdentifier(keyname1)) 0361 .arg(conn->driver()->valueToSql(keytype1, keyval1)) 0362 .arg(conn->escapeIdentifier(keyname2)) 0363 .arg(conn->driver()->valueToSql(keytype2, keyval2))) 0364 : false; 0365 } 0366 0367 bool KDb::deleteRecords(KDbConnection* conn, const QString &tableName, 0368 const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1, 0369 const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2, 0370 const QString &keyname3, KDbField::Type keytype3, const QVariant& keyval3) 0371 { 0372 return conn 0373 ? conn->executeSql(KDbEscapedString("DELETE FROM %1 WHERE %2=%3 AND %4=%5 AND %6=%7") 0374 .arg(conn->escapeIdentifier(tableName)) 0375 .arg(conn->escapeIdentifier(keyname1)) 0376 .arg(conn->driver()->valueToSql(keytype1, keyval1)) 0377 .arg(conn->escapeIdentifier(keyname2)) 0378 .arg(conn->driver()->valueToSql(keytype2, keyval2)) 0379 .arg(conn->escapeIdentifier(keyname3)) 0380 .arg(conn->driver()->valueToSql(keytype3, keyval3))) 0381 : false; 0382 } 0383 0384 bool KDb::deleteAllRecords(KDbConnection* conn, const QString &tableName) 0385 { 0386 return conn 0387 ? conn->executeSql( 0388 KDbEscapedString("DELETE FROM %1").arg(conn->escapeIdentifier(tableName))) 0389 : false; 0390 } 0391 0392 KDB_EXPORT quint64 KDb::lastInsertedAutoIncValue(QSharedPointer<KDbSqlResult> result, 0393 const QString &autoIncrementFieldName, 0394 const QString &tableName, quint64 *recordId) 0395 { 0396 if (!result) { 0397 return std::numeric_limits<quint64>::max(); 0398 } 0399 const quint64 foundRecordId = result->lastInsertRecordId(); 0400 if (recordId) { 0401 *recordId = foundRecordId; 0402 } 0403 return KDb::lastInsertedAutoIncValue(result->connection(), 0404 foundRecordId, autoIncrementFieldName, tableName); 0405 } 0406 0407 KDB_EXPORT quint64 KDb::lastInsertedAutoIncValue(KDbConnection *conn, const quint64 recordId, 0408 const QString &autoIncrementFieldName, 0409 const QString &tableName) 0410 { 0411 const KDbDriverBehavior *behavior = KDbDriverPrivate::behavior(conn->driver()); 0412 if (behavior->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) { 0413 return recordId; 0414 } 0415 KDbRecordData rdata; 0416 if (recordId == std::numeric_limits<quint64>::max() 0417 || true != conn->querySingleRecord( 0418 KDbEscapedString("SELECT ") + escapeIdentifier(tableName) + '.' 0419 + escapeIdentifier(autoIncrementFieldName) 0420 + " FROM " + escapeIdentifier(tableName) 0421 + " WHERE " + behavior->ROW_ID_FIELD_NAME 0422 + '=' + KDbEscapedString::number(recordId), &rdata)) 0423 { 0424 return std::numeric_limits<quint64>::max(); 0425 } 0426 return rdata[0].toULongLong(); 0427 } 0428 0429 bool KDb::isEmptyValue(KDbField::Type type, const QVariant &value) 0430 { 0431 if (KDbField::isTextType(type)) { 0432 return value.toString().isEmpty() && !value.toString().isNull(); 0433 } 0434 else if (type == KDbField::BLOB) { 0435 return value.toByteArray().isEmpty() && !value.toByteArray().isNull(); 0436 } 0437 return value.isNull(); 0438 } 0439 0440 KDbEscapedString KDb::sqlWhere(KDbDriver *drv, KDbField::Type t, 0441 const QString& fieldName, const QVariant& value) 0442 { 0443 if (value.isNull()) 0444 return KDbEscapedString(fieldName) + " IS NULL"; 0445 return KDbEscapedString(fieldName) + '=' + drv->valueToSql(t, value); 0446 } 0447 0448 //! Cache 0449 struct TypeCache { 0450 TypeCache() { 0451 for (KDbField::Type t = KDbField::InvalidType; t <= KDbField::LastType; t = KDbField::Type(int(t) + 1)) { 0452 const KDbField::TypeGroup tg = KDbField::typeGroup(t); 0453 QList<KDbField::Type> list; 0454 QStringList name_list, str_list; 0455 if (tlist.contains(tg)) { 0456 list = tlist.value(tg); 0457 name_list = nlist.value(tg); 0458 str_list = slist.value(tg); 0459 } 0460 list += t; 0461 name_list += KDbField::typeName(t); 0462 str_list += KDbField::typeString(t); 0463 tlist[ tg ] = list; 0464 nlist[ tg ] = name_list; 0465 slist[ tg ] = str_list; 0466 } 0467 0468 def_tlist[ KDbField::InvalidGroup ] = KDbField::InvalidType; 0469 def_tlist[ KDbField::TextGroup ] = KDbField::Text; 0470 def_tlist[ KDbField::IntegerGroup ] = KDbField::Integer; 0471 def_tlist[ KDbField::FloatGroup ] = KDbField::Double; 0472 def_tlist[ KDbField::BooleanGroup ] = KDbField::Boolean; 0473 def_tlist[ KDbField::DateTimeGroup ] = KDbField::Date; 0474 def_tlist[ KDbField::BLOBGroup ] = KDbField::BLOB; 0475 } 0476 0477 QHash< KDbField::TypeGroup, QList<KDbField::Type> > tlist; 0478 QHash< KDbField::TypeGroup, QStringList > nlist; 0479 QHash< KDbField::TypeGroup, QStringList > slist; 0480 QHash< KDbField::TypeGroup, KDbField::Type > def_tlist; 0481 }; 0482 0483 Q_GLOBAL_STATIC(TypeCache, KDb_typeCache) 0484 0485 const QList<KDbField::Type> KDb::fieldTypesForGroup(KDbField::TypeGroup typeGroup) 0486 { 0487 return KDb_typeCache->tlist.value(typeGroup); 0488 } 0489 0490 QStringList KDb::fieldTypeNamesForGroup(KDbField::TypeGroup typeGroup) 0491 { 0492 return KDb_typeCache->nlist.value(typeGroup); 0493 } 0494 0495 QStringList KDb::fieldTypeStringsForGroup(KDbField::TypeGroup typeGroup) 0496 { 0497 return KDb_typeCache->slist.value(typeGroup); 0498 } 0499 0500 KDbField::Type KDb::defaultFieldTypeForGroup(KDbField::TypeGroup typeGroup) 0501 { 0502 return (typeGroup <= KDbField::LastTypeGroup) ? KDb_typeCache->def_tlist.value(typeGroup) : KDbField::InvalidType; 0503 } 0504 0505 void KDb::getHTMLErrorMesage(const KDbResultable& resultable, QString *msg, QString *details) 0506 { 0507 if (!msg) { 0508 kdbWarning() << "Missing 'msg' parameter"; 0509 return; 0510 } 0511 if (!details) { 0512 kdbWarning() << "Missing 'details' parameter"; 0513 return; 0514 } 0515 const KDbResult result(resultable.result()); 0516 if (!result.isError()) 0517 return; 0518 //lower level message is added to the details, if there is alread message specified 0519 if (!result.messageTitle().isEmpty()) 0520 *msg += QLatin1String("<p>") + result.messageTitle(); 0521 0522 if (msg->isEmpty()) 0523 *msg = QLatin1String("<p>") + result.message(); 0524 else 0525 *details += QLatin1String("<p>") + result.message(); 0526 0527 if (!result.serverMessage().isEmpty()) 0528 *details += QLatin1String("<p><b>") + kdb::tr("Message from server:") 0529 + QLatin1String("</b> ") + result.serverMessage(); 0530 if (!result.recentSqlString().isEmpty()) 0531 *details += QLatin1String("<p><b>") + kdb::tr("SQL statement:") 0532 + QString::fromLatin1("</b> <tt>%1</tt>").arg(result.recentSqlString().toString()); 0533 int serverErrorCode = 0; 0534 QString serverResultName; 0535 if (result.isError()) { 0536 serverErrorCode = result.serverErrorCode(); 0537 serverResultName = resultable.serverResultName(); 0538 } 0539 if ( !details->isEmpty() 0540 && ( !result.serverMessage().isEmpty() 0541 || !result.recentSqlString().isEmpty() 0542 || !serverResultName.isEmpty() 0543 || serverErrorCode != 0) 0544 ) 0545 { 0546 *details += (QLatin1String("<p><b>") + kdb::tr("Server result code:") 0547 + QLatin1String("</b> ") + QString::number(serverErrorCode)); 0548 if (!serverResultName.isEmpty()) { 0549 *details += QString::fromLatin1(" (%1)").arg(serverResultName); 0550 } 0551 } 0552 else { 0553 if (!serverResultName.isEmpty()) { 0554 *details += (QLatin1String("<p><b>") + kdb::tr("Server result:") 0555 + QLatin1String("</b> ") + serverResultName); 0556 } 0557 } 0558 0559 if (!details->isEmpty() && !details->startsWith(QLatin1String("<qt>"))) { 0560 if (!details->startsWith(QLatin1String("<p>"))) 0561 details->prepend(QLatin1String("<p>")); 0562 } 0563 } 0564 0565 void KDb::getHTMLErrorMesage(const KDbResultable& resultable, QString *msg) 0566 { 0567 getHTMLErrorMesage(resultable, msg, msg); 0568 } 0569 0570 void KDb::getHTMLErrorMesage(const KDbResultable& resultable, KDbResultInfo *info) 0571 { 0572 if (!info) { 0573 kdbWarning() << "Missing 'info' parameter"; 0574 return; 0575 } 0576 getHTMLErrorMesage(resultable, &info->message, &info->description); 0577 } 0578 0579 tristate KDb::idForObjectName(KDbConnection* conn, int *id, const QString& objName, int objType) 0580 { 0581 return conn 0582 ? conn->querySingleNumber( 0583 KDbEscapedString("SELECT o_id FROM kexi__objects WHERE o_name=%1 AND o_type=%2") 0584 .arg(conn->escapeString(objName)) 0585 .arg(objType), 0586 id) 0587 : false; 0588 } 0589 0590 //----------------------------------------- 0591 0592 tristate KDb::showConnectionTestDialog(QWidget *parent, const KDbConnectionData &data, 0593 KDbMessageHandler *msgHandler) 0594 { 0595 ConnectionTestDialog dlg(data, msgHandler, parent); 0596 const int result = dlg.exec(); 0597 if (dlg.wasCanceled()) { 0598 return cancelled; 0599 } 0600 return result == QDialog::Accepted; 0601 } 0602 0603 bool KDb::splitToTableAndFieldParts(const QString& string, 0604 QString *tableName, QString *fieldName, 0605 SplitToTableAndFieldPartsOptions option) 0606 { 0607 if (!tableName || !fieldName) { 0608 return false; 0609 } 0610 const int id = string.indexOf(QLatin1Char('.')); 0611 if (option & SetFieldNameIfNoTableName && id == -1) { 0612 tableName->clear(); 0613 *fieldName = string; 0614 return !fieldName->isEmpty(); 0615 } 0616 if (id <= 0 || id == int(string.length() - 1)) 0617 return false; 0618 *tableName = string.left(id); 0619 *fieldName = string.mid(id + 1); 0620 return !tableName->isEmpty() && !fieldName->isEmpty(); 0621 } 0622 0623 bool KDb::supportsVisibleDecimalPlacesProperty(KDbField::Type type) 0624 { 0625 //! @todo add check for decimal type as well 0626 return KDbField::isFPNumericType(type); 0627 } 0628 0629 inline static QString numberToString(double value, int decimalPlaces, const QLocale *locale) 0630 { 0631 //! @todo round? 0632 QString result; 0633 if (decimalPlaces == 0) { 0634 result = locale ? locale->toString(qlonglong(value)) 0635 : QString::number(qlonglong(value)); 0636 } else { 0637 const int realDecimalPlaces = decimalPlaces < 0 ? 10 : decimalPlaces; 0638 result = locale ? locale->toString(value, 'f', realDecimalPlaces) 0639 : QString::number(value, 'f', realDecimalPlaces); 0640 if (decimalPlaces < 0) { // cut off zeros 0641 int i = result.length() - 1; 0642 while (i > 0 && result[i] == QLatin1Char('0')) { 0643 i--; 0644 } 0645 if (result[i].isDigit()) {// last digit 0646 ++i; 0647 } 0648 result.truncate(i); 0649 } 0650 } 0651 return result; 0652 } 0653 0654 QString KDb::numberToString(double value, int decimalPlaces) 0655 { 0656 return ::numberToString(value, decimalPlaces, nullptr); 0657 } 0658 0659 QString KDb::numberToLocaleString(double value, int decimalPlaces) 0660 { 0661 QLocale defaultLocale; 0662 return ::numberToString(value, decimalPlaces, &defaultLocale); 0663 } 0664 0665 QString KDb::numberToLocaleString(double value, int decimalPlaces, const QLocale &locale) 0666 { 0667 return ::numberToString(value, decimalPlaces, &locale); 0668 } 0669 0670 KDbField::Type KDb::intToFieldType(int type) 0671 { 0672 if (type < int(KDbField::InvalidType) || type > int(KDbField::LastType)) { 0673 return KDbField::InvalidType; 0674 } 0675 return static_cast<KDbField::Type>(type); 0676 } 0677 0678 KDbField::TypeGroup KDb::intToFieldTypeGroup(int typeGroup) 0679 { 0680 if (typeGroup < int(KDbField::InvalidGroup) || typeGroup > int(KDbField::LastTypeGroup)) { 0681 return KDbField::InvalidGroup; 0682 } 0683 return static_cast<KDbField::TypeGroup>(typeGroup); 0684 } 0685 0686 static bool setIntToFieldType(KDbField *field, const QVariant& value) 0687 { 0688 Q_ASSERT(field); 0689 bool ok; 0690 const int intType = value.toInt(&ok); 0691 if (!ok) {//for sanity 0692 kdbWarning() << "Could not convert value" << value << "to field type"; 0693 return false; 0694 } 0695 if (KDbField::InvalidType == KDb::intToFieldType(intType)) {//for sanity 0696 kdbWarning() << "Invalid field type" << intType; 0697 return false; 0698 } 0699 field->setType((KDbField::Type)intType); 0700 return true; 0701 } 0702 0703 //! @internal for KDb::isBuiltinTableFieldProperty() 0704 struct KDb_BuiltinFieldProperties { 0705 KDb_BuiltinFieldProperties() { 0706 #define ADD(name) set.insert(name) 0707 ADD("type"); 0708 ADD("primaryKey"); 0709 ADD("indexed"); 0710 ADD("autoIncrement"); 0711 ADD("unique"); 0712 ADD("notNull"); 0713 ADD("allowEmpty"); 0714 ADD("unsigned"); 0715 ADD("name"); 0716 ADD("caption"); 0717 ADD("description"); 0718 ADD("maxLength"); 0719 ADD("maxLengthIsDefault"); 0720 ADD("precision"); 0721 ADD("defaultValue"); 0722 ADD("defaultWidth"); 0723 ADD("visibleDecimalPlaces"); 0724 //! @todo always update this when new builtins appear! 0725 #undef ADD 0726 } 0727 QSet<QByteArray> set; 0728 }; 0729 0730 //! for KDb::isBuiltinTableFieldProperty() 0731 Q_GLOBAL_STATIC(KDb_BuiltinFieldProperties, KDb_builtinFieldProperties) 0732 0733 0734 bool KDb::isBuiltinTableFieldProperty(const QByteArray& propertyName) 0735 { 0736 return KDb_builtinFieldProperties->set.contains(propertyName); 0737 } 0738 0739 static QVariant visibleColumnValue(const KDbLookupFieldSchema *lookup) 0740 { 0741 if (!lookup || lookup->visibleColumns().count() == 1) { 0742 if (lookup) { 0743 const QList<int> visibleColumns = lookup->visibleColumns(); 0744 if (!visibleColumns.isEmpty()) { 0745 return visibleColumns.first(); 0746 } 0747 } 0748 return QVariant(); 0749 } 0750 QList<QVariant> variantList; 0751 const QList<int> visibleColumns(lookup->visibleColumns()); 0752 for(int column : visibleColumns) { 0753 variantList.append(column); 0754 } 0755 return variantList; 0756 } 0757 0758 void KDb::getProperties(const KDbLookupFieldSchema *lookup, QMap<QByteArray, QVariant> *values) 0759 { 0760 if (!values) { 0761 return; 0762 } 0763 KDbLookupFieldSchemaRecordSource recordSource; 0764 if (lookup) { 0765 recordSource = lookup->recordSource(); 0766 } 0767 values->insert("rowSource", lookup ? recordSource.name() : QVariant()); 0768 values->insert("rowSourceType", lookup ? recordSource.typeName() : QVariant()); 0769 values->insert("rowSourceValues", 0770 (lookup && !recordSource.values().isEmpty()) ? recordSource.values() : QVariant()); 0771 values->insert("boundColumn", lookup ? lookup->boundColumn() : QVariant()); 0772 values->insert("visibleColumn", visibleColumnValue(lookup)); 0773 QList<QVariant> variantList; 0774 if (lookup) { 0775 const QList<int> columnWidths = lookup->columnWidths(); 0776 for(const QVariant& variant : columnWidths) { 0777 variantList.append(variant); 0778 } 0779 } 0780 values->insert("columnWidths", lookup ? variantList : QVariant()); 0781 values->insert("showColumnHeaders", lookup ? lookup->columnHeadersVisible() : QVariant()); 0782 values->insert("listRows", lookup ? lookup->maxVisibleRecords() : QVariant()); 0783 values->insert("limitToList", lookup ? lookup->limitToList() : QVariant()); 0784 values->insert("displayWidget", lookup ? int(lookup->displayWidget()) : QVariant()); 0785 } 0786 0787 void KDb::getFieldProperties(const KDbField &field, QMap<QByteArray, QVariant> *values) 0788 { 0789 if (!values) { 0790 return; 0791 } 0792 values->clear(); 0793 // normal values 0794 values->insert("type", field.type()); 0795 const KDbField::Constraints constraints = field.constraints(); 0796 values->insert("primaryKey", constraints.testFlag(KDbField::PrimaryKey)); 0797 values->insert("indexed", constraints.testFlag(KDbField::Indexed)); 0798 values->insert("autoIncrement", KDbField::isAutoIncrementAllowed(field.type()) 0799 && constraints.testFlag(KDbField::AutoInc)); 0800 values->insert("unique", constraints.testFlag(KDbField::Unique)); 0801 values->insert("notNull", constraints.testFlag(KDbField::NotNull)); 0802 values->insert("allowEmpty", !constraints.testFlag(KDbField::NotEmpty)); 0803 const KDbField::Options options = field.options(); 0804 values->insert("unsigned", options.testFlag(KDbField::Unsigned)); 0805 values->insert("name", field.name()); 0806 values->insert("caption", field.caption()); 0807 values->insert("description", field.description()); 0808 values->insert("maxLength", field.maxLength()); 0809 values->insert("maxLengthIsDefault", field.maxLengthStrategy() & KDbField::DefaultMaxLength); 0810 values->insert("precision", field.precision()); 0811 values->insert("defaultValue", field.defaultValue()); 0812 //! @todo IMPORTANT: values->insert("defaultWidth", field.defaultWidth()); 0813 if (KDb::supportsVisibleDecimalPlacesProperty(field.type())) { 0814 values->insert("visibleDecimalPlaces", field.defaultValue()); 0815 } 0816 // insert lookup-related values 0817 const KDbLookupFieldSchema *lookup = field.table()->lookupFieldSchema(field); 0818 KDb::getProperties(lookup, values); 0819 } 0820 0821 static bool containsLookupFieldSchemaProperties(const QMap<QByteArray, QVariant>& values) 0822 { 0823 for (QMap<QByteArray, QVariant>::ConstIterator it(values.constBegin()); 0824 it != values.constEnd(); ++it) 0825 { 0826 if (KDb::isLookupFieldSchemaProperty(it.key())) { 0827 return true; 0828 } 0829 } 0830 return false; 0831 } 0832 0833 bool KDb::setFieldProperties(KDbField *field, const QMap<QByteArray, QVariant>& values) 0834 { 0835 if (!field) { 0836 return false; 0837 } 0838 QMap<QByteArray, QVariant>::ConstIterator it; 0839 if ((it = values.find("type")) != values.constEnd()) { 0840 if (!setIntToFieldType(field, *it)) 0841 return false; 0842 } 0843 0844 #define SET_BOOLEAN_FLAG(flag, value) { \ 0845 constraints |= KDbField::flag; \ 0846 if (!value) \ 0847 constraints ^= KDbField::flag; \ 0848 } 0849 0850 KDbField::Constraints constraints = field->constraints(); 0851 bool ok = true; 0852 if ((it = values.find("primaryKey")) != values.constEnd()) 0853 SET_BOOLEAN_FLAG(PrimaryKey, (*it).toBool()); 0854 if ((it = values.find("indexed")) != values.constEnd()) 0855 SET_BOOLEAN_FLAG(Indexed, (*it).toBool()); 0856 if ((it = values.find("autoIncrement")) != values.constEnd() 0857 && KDbField::isAutoIncrementAllowed(field->type())) 0858 SET_BOOLEAN_FLAG(AutoInc, (*it).toBool()); 0859 if ((it = values.find("unique")) != values.constEnd()) 0860 SET_BOOLEAN_FLAG(Unique, (*it).toBool()); 0861 if ((it = values.find("notNull")) != values.constEnd()) 0862 SET_BOOLEAN_FLAG(NotNull, (*it).toBool()); 0863 if ((it = values.find("allowEmpty")) != values.constEnd()) 0864 SET_BOOLEAN_FLAG(NotEmpty, !(*it).toBool()); 0865 field->setConstraints(constraints); 0866 0867 KDbField::Options options; 0868 if ((it = values.find("unsigned")) != values.constEnd()) { 0869 options |= KDbField::Unsigned; 0870 if (!(*it).toBool()) 0871 options ^= KDbField::Unsigned; 0872 } 0873 field->setOptions(options); 0874 0875 if ((it = values.find("name")) != values.constEnd()) 0876 field->setName((*it).toString()); 0877 if ((it = values.find("caption")) != values.constEnd()) 0878 field->setCaption((*it).toString()); 0879 if ((it = values.find("description")) != values.constEnd()) 0880 field->setDescription((*it).toString()); 0881 if ((it = values.find("maxLength")) != values.constEnd()) 0882 field->setMaxLength((*it).isNull() ? 0/*default*/ : (*it).toInt(&ok)); 0883 if (!ok) 0884 return false; 0885 if ((it = values.find("maxLengthIsDefault")) != values.constEnd() 0886 && (*it).toBool()) 0887 { 0888 field->setMaxLengthStrategy(KDbField::DefaultMaxLength); 0889 } 0890 if ((it = values.find("precision")) != values.constEnd()) 0891 field->setPrecision((*it).isNull() ? 0/*default*/ : (*it).toInt(&ok)); 0892 if (!ok) 0893 return false; 0894 if ((it = values.find("defaultValue")) != values.constEnd()) 0895 field->setDefaultValue(*it); 0896 //! @todo IMPORTANT: defaultWidth 0897 #if 0 0898 if ((it = values.find("defaultWidth")) != values.constEnd()) 0899 field.setDefaultWidth((*it).isNull() ? 0/*default*/ : (*it).toInt(&ok)); 0900 if (!ok) 0901 return false; 0902 #endif 0903 0904 // -- extended properties 0905 if ((it = values.find("visibleDecimalPlaces")) != values.constEnd() 0906 && KDb::supportsVisibleDecimalPlacesProperty(field->type())) 0907 field->setVisibleDecimalPlaces((*it).isNull() ? -1/*default*/ : (*it).toInt(&ok)); 0908 if (!ok) 0909 return false; 0910 0911 if (field->table() && containsLookupFieldSchemaProperties(values)) { 0912 KDbLookupFieldSchema *lookup = field->table()->lookupFieldSchema(*field); 0913 QScopedPointer<KDbLookupFieldSchema> createdLookup; 0914 if (!lookup) { // create lookup if needed 0915 createdLookup.reset(lookup = new KDbLookupFieldSchema()); 0916 } 0917 if (lookup->setProperties(values)) { 0918 if (createdLookup) { 0919 if (field->table()->setLookupFieldSchema(field->name(), lookup)) { 0920 createdLookup.take(); // ownership passed 0921 lookup = nullptr; 0922 } 0923 } 0924 } 0925 } 0926 0927 return true; 0928 #undef SET_BOOLEAN_FLAG 0929 } 0930 0931 //! @internal for isExtendedTableProperty() 0932 struct KDb_ExtendedProperties { 0933 KDb_ExtendedProperties() { 0934 #define ADD(name) set.insert( name ) 0935 ADD("visibledecimalplaces"); 0936 ADD("rowsource"); 0937 ADD("rowsourcetype"); 0938 ADD("rowsourcevalues"); 0939 ADD("boundcolumn"); 0940 ADD("visiblecolumn"); 0941 ADD("columnwidths"); 0942 ADD("showcolumnheaders"); 0943 ADD("listrows"); 0944 ADD("limittolist"); 0945 ADD("displaywidget"); 0946 #undef ADD 0947 } 0948 QSet<QByteArray> set; 0949 }; 0950 0951 //! for isExtendedTableProperty() 0952 Q_GLOBAL_STATIC(KDb_ExtendedProperties, KDb_extendedProperties) 0953 0954 bool KDb::isExtendedTableFieldProperty(const QByteArray& propertyName) 0955 { 0956 return KDb_extendedProperties->set.contains(QByteArray(propertyName).toLower()); 0957 } 0958 0959 //! @internal for isLookupFieldSchemaProperty() 0960 struct KDb_LookupFieldSchemaProperties { 0961 KDb_LookupFieldSchemaProperties() { 0962 QMap<QByteArray, QVariant> tmp; 0963 KDb::getProperties(nullptr, &tmp); 0964 for (QMap<QByteArray, QVariant>::ConstIterator it(tmp.constBegin()); 0965 it != tmp.constEnd(); ++it) 0966 { 0967 set.insert(it.key().toLower()); 0968 } 0969 } 0970 QSet<QByteArray> set; 0971 }; 0972 0973 //! for isLookupFieldSchemaProperty() 0974 Q_GLOBAL_STATIC(KDb_LookupFieldSchemaProperties, KDb_lookupFieldSchemaProperties) 0975 0976 bool KDb::isLookupFieldSchemaProperty(const QByteArray& propertyName) 0977 { 0978 return KDb_lookupFieldSchemaProperties->set.contains(propertyName.toLower()); 0979 } 0980 0981 bool KDb::setFieldProperty(KDbField *field, const QByteArray& propertyName, const QVariant& value) 0982 { 0983 if (!field) { 0984 return false; 0985 } 0986 #define SET_BOOLEAN_FLAG(flag, value) { \ 0987 constraints |= KDbField::flag; \ 0988 if (!value) \ 0989 constraints ^= KDbField::flag; \ 0990 field->setConstraints( constraints ); \ 0991 return true; \ 0992 } 0993 #define GET_INT(method) { \ 0994 const int ival = value.toInt(&ok); \ 0995 if (!ok) \ 0996 return false; \ 0997 field->method( ival ); \ 0998 return true; \ 0999 } 1000 1001 if (propertyName.isEmpty()) 1002 return false; 1003 1004 bool ok; 1005 if (KDb::isExtendedTableFieldProperty(propertyName)) { 1006 //a little speedup: identify extended property in O(1) 1007 if ("visibleDecimalPlaces" == propertyName 1008 && KDb::supportsVisibleDecimalPlacesProperty(field->type())) { 1009 GET_INT(setVisibleDecimalPlaces); 1010 } 1011 else if (KDb::isLookupFieldSchemaProperty(propertyName)) { 1012 if (!field->table()) { 1013 kdbWarning() << "Could not set" << propertyName << "property - no table assigned for field"; 1014 } else { 1015 KDbLookupFieldSchema *lookup = field->table()->lookupFieldSchema(*field); 1016 const bool createLookup = !lookup; 1017 if (createLookup) // create lookup if needed 1018 lookup = new KDbLookupFieldSchema(); 1019 if (lookup->setProperty(propertyName, value)) { 1020 if (createLookup) 1021 field->table()->setLookupFieldSchema(field->name(), lookup); 1022 return true; 1023 } 1024 if (createLookup) 1025 delete lookup; // not set, delete 1026 } 1027 } 1028 } else {//non-extended 1029 if ("type" == propertyName) 1030 return setIntToFieldType(field, value); 1031 1032 KDbField::Constraints constraints = field->constraints(); 1033 if ("primaryKey" == propertyName) 1034 SET_BOOLEAN_FLAG(PrimaryKey, value.toBool()); 1035 if ("indexed" == propertyName) 1036 SET_BOOLEAN_FLAG(Indexed, value.toBool()); 1037 if ("autoIncrement" == propertyName 1038 && KDbField::isAutoIncrementAllowed(field->type())) 1039 SET_BOOLEAN_FLAG(AutoInc, value.toBool()); 1040 if ("unique" == propertyName) 1041 SET_BOOLEAN_FLAG(Unique, value.toBool()); 1042 if ("notNull" == propertyName) 1043 SET_BOOLEAN_FLAG(NotNull, value.toBool()); 1044 if ("allowEmpty" == propertyName) 1045 SET_BOOLEAN_FLAG(NotEmpty, !value.toBool()); 1046 1047 KDbField::Options options; 1048 if ("unsigned" == propertyName) { 1049 options |= KDbField::Unsigned; 1050 if (!value.toBool()) 1051 options ^= KDbField::Unsigned; 1052 field->setOptions(options); 1053 return true; 1054 } 1055 1056 if ("name" == propertyName) { 1057 if (value.toString().isEmpty()) 1058 return false; 1059 field->setName(value.toString()); 1060 return true; 1061 } 1062 if ("caption" == propertyName) { 1063 field->setCaption(value.toString()); 1064 return true; 1065 } 1066 if ("description" == propertyName) { 1067 field->setDescription(value.toString()); 1068 return true; 1069 } 1070 if ("maxLength" == propertyName) 1071 GET_INT(setMaxLength); 1072 if ("maxLengthIsDefault" == propertyName) { 1073 field->setMaxLengthStrategy(KDbField::DefaultMaxLength); 1074 } 1075 if ("precision" == propertyName) 1076 GET_INT(setPrecision); 1077 if ("defaultValue" == propertyName) { 1078 field->setDefaultValue(value); 1079 return true; 1080 } 1081 1082 //! @todo IMPORTANT: defaultWidth 1083 #if 0 1084 if ("defaultWidth" == propertyName) 1085 GET_INT(setDefaultWidth); 1086 #endif 1087 // last chance that never fails: custom field property 1088 field->setCustomProperty(propertyName, value); 1089 } 1090 1091 kdbWarning() << "Field property" << propertyName << "not found!"; 1092 return false; 1093 #undef SET_BOOLEAN_FLAG 1094 #undef GET_INT 1095 } 1096 1097 int KDb::loadIntPropertyValueFromDom(const QDomNode& node, bool* ok) 1098 { 1099 QByteArray valueType = node.nodeName().toLatin1(); 1100 if (valueType.isEmpty() || valueType != "number") { 1101 if (ok) 1102 *ok = false; 1103 return 0; 1104 } 1105 const QString text(QDomNode(node).toElement().text()); 1106 int val = text.toInt(ok); 1107 return val; 1108 } 1109 1110 QString KDb::loadStringPropertyValueFromDom(const QDomNode& node, bool* ok) 1111 { 1112 QByteArray valueType = node.nodeName().toLatin1(); 1113 if (valueType != "string") { 1114 if (ok) 1115 *ok = false; 1116 return QString(); 1117 } 1118 if (ok) 1119 *ok = true; 1120 return QDomNode(node).toElement().text(); 1121 } 1122 1123 QVariant KDb::loadPropertyValueFromDom(const QDomNode& node, bool* ok) 1124 { 1125 QByteArray valueType = node.nodeName().toLatin1(); 1126 if (valueType.isEmpty()) { 1127 if (ok) 1128 *ok = false; 1129 return QVariant(); 1130 } 1131 if (ok) 1132 *ok = true; 1133 const QString text(QDomNode(node).toElement().text()); 1134 bool _ok; 1135 if (valueType == "string") { 1136 return text; 1137 } 1138 else if (valueType == "cstring") { 1139 return text.toLatin1(); 1140 } 1141 else if (valueType == "number") { // integer or double 1142 if (text.indexOf(QLatin1Char('.')) != -1) { 1143 double val = text.toDouble(&_ok); 1144 if (_ok) 1145 return val; 1146 } 1147 else { 1148 const int val = text.toInt(&_ok); 1149 if (_ok) 1150 return val; 1151 const qint64 valLong = text.toLongLong(&_ok); 1152 if (_ok) 1153 return valLong; 1154 } 1155 } 1156 else if (valueType == "bool") { 1157 return text.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0 1158 || text == QLatin1String("1"); 1159 } 1160 else { 1161 //! @todo add more QVariant types 1162 kdbWarning() << "Unknown property type" << valueType; 1163 } 1164 if (ok) 1165 *ok = false; 1166 return QVariant(); 1167 } 1168 1169 QDomElement KDb::saveNumberElementToDom(QDomDocument *doc, QDomElement *parentEl, 1170 const QString& elementName, int value) 1171 { 1172 if (!doc || !parentEl || elementName.isEmpty()) { 1173 return QDomElement(); 1174 } 1175 QDomElement el(doc->createElement(elementName)); 1176 parentEl->appendChild(el); 1177 QDomElement numberEl(doc->createElement(QLatin1String("number"))); 1178 el.appendChild(numberEl); 1179 numberEl.appendChild(doc->createTextNode(QString::number(value))); 1180 return el; 1181 } 1182 1183 QDomElement KDb::saveBooleanElementToDom(QDomDocument *doc, QDomElement *parentEl, 1184 const QString& elementName, bool value) 1185 { 1186 if (!doc || !parentEl || elementName.isEmpty()) { 1187 return QDomElement(); 1188 } 1189 QDomElement el(doc->createElement(elementName)); 1190 parentEl->appendChild(el); 1191 QDomElement numberEl(doc->createElement(QLatin1String("bool"))); 1192 el.appendChild(numberEl); 1193 numberEl.appendChild(doc->createTextNode( 1194 value ? QLatin1String("true") : QLatin1String("false"))); 1195 return el; 1196 } 1197 1198 //! @internal Used in KDb::emptyValueForFieldType() 1199 struct KDb_EmptyValueForFieldTypeCache { 1200 KDb_EmptyValueForFieldTypeCache() 1201 : values(int(KDbField::LastType) + 1) { 1202 #define ADD(t, value) values.insert(t, value); 1203 ADD(KDbField::Byte, 0); 1204 ADD(KDbField::ShortInteger, 0); 1205 ADD(KDbField::Integer, 0); 1206 ADD(KDbField::BigInteger, 0); 1207 ADD(KDbField::Boolean, false); 1208 ADD(KDbField::Float, 0.0); 1209 ADD(KDbField::Double, 0.0); 1210 //! @todo ok? we have no better defaults 1211 ADD(KDbField::Text, QLatin1String(" ")); 1212 ADD(KDbField::LongText, QLatin1String(" ")); 1213 ADD(KDbField::BLOB, QByteArray()); 1214 #undef ADD 1215 } 1216 QVector<QVariant> values; 1217 }; 1218 1219 //! Used in KDb::emptyValueForFieldType() 1220 Q_GLOBAL_STATIC(KDb_EmptyValueForFieldTypeCache, KDb_emptyValueForFieldTypeCache) 1221 1222 QVariant KDb::emptyValueForFieldType(KDbField::Type type) 1223 { 1224 const QVariant val(KDb_emptyValueForFieldTypeCache->values.at( 1225 (type <= KDbField::LastType) ? type : KDbField::InvalidType)); 1226 if (!val.isNull()) 1227 return val; 1228 else { //special cases 1229 if (type == KDbField::Date) 1230 return QDate::currentDate(); 1231 if (type == KDbField::DateTime) 1232 return QDateTime::currentDateTime(); 1233 if (type == KDbField::Time) 1234 return QTime::currentTime(); 1235 } 1236 kdbWarning() << "No empty value for field type" << KDbField::typeName(type); 1237 return QVariant(); 1238 } 1239 1240 //! @internal Used in KDb::notEmptyValueForFieldType() 1241 struct KDb_NotEmptyValueForFieldTypeCache { 1242 KDb_NotEmptyValueForFieldTypeCache() 1243 : values(int(KDbField::LastType) + 1) { 1244 #define ADD(t, value) values.insert(t, value); 1245 // copy most of the values 1246 for (int i = int(KDbField::InvalidType) + 1; i <= KDbField::LastType; i++) { 1247 if (i == KDbField::Date || i == KDbField::DateTime || i == KDbField::Time) 1248 continue; //'current' value will be returned 1249 if (i == KDbField::Text || i == KDbField::LongText) { 1250 ADD(i, QVariant(QLatin1String(""))); 1251 continue; 1252 } 1253 if (i == KDbField::BLOB) { 1254 //! @todo blobs will contain other MIME types too 1255 QByteArray ba; 1256 //! @todo port to Qt4 1257 #if 0 1258 QBuffer buffer(&ba); 1259 buffer.open(QIODevice::WriteOnly); 1260 QPixmap pm(SmallIcon("document-new")); 1261 pm.save(&buffer, "PNG"/*! @todo default? */); 1262 #endif 1263 ADD(i, ba); 1264 continue; 1265 } 1266 ADD(i, KDb::emptyValueForFieldType((KDbField::Type)i)); 1267 } 1268 #undef ADD 1269 } 1270 QVector<QVariant> values; 1271 }; 1272 //! Used in KDb::notEmptyValueForFieldType() 1273 Q_GLOBAL_STATIC(KDb_NotEmptyValueForFieldTypeCache, KDb_notEmptyValueForFieldTypeCache) 1274 1275 QVariant KDb::notEmptyValueForFieldType(KDbField::Type type) 1276 { 1277 const QVariant val(KDb_notEmptyValueForFieldTypeCache->values.at( 1278 (type <= KDbField::LastType) ? type : KDbField::InvalidType)); 1279 if (!val.isNull()) 1280 return val; 1281 else { //special cases 1282 if (type == KDbField::Date) 1283 return QDate::currentDate(); 1284 if (type == KDbField::DateTime) 1285 return QDateTime::currentDateTime(); 1286 if (type == KDbField::Time) 1287 return QTime::currentTime(); 1288 } 1289 kdbWarning() << "No non-empty value for field type" << KDbField::typeName(type); 1290 return QVariant(); 1291 } 1292 1293 //! @internal @return nestimated new length after escaping of string @a string 1294 template<typename T> 1295 inline static int estimatedNewLength(const T &string, bool addQuotes) 1296 { 1297 if (string.length() < 10) 1298 return string.length() * 2 + (addQuotes ? 2 : 0); 1299 return string.length() * 3 / 2; 1300 } 1301 1302 //! @internal @return @a string string with applied KDbSQL identifier escaping. 1303 //! If @a addQuotes is true, '"' characer is prepended and appended. 1304 template<typename T, typename Latin1StringType, typename Latin1CharType, typename CharType> 1305 inline static T escapeIdentifier(const T& string, bool addQuotes) 1306 { 1307 const Latin1CharType quote('"'); 1308 // create 1309 Latin1StringType escapedQuote("\"\""); 1310 T newString; 1311 newString.reserve(estimatedNewLength(string, addQuotes)); 1312 if (addQuotes) { 1313 newString.append(quote); 1314 } 1315 for (int i = 0; i < string.length(); i++) { 1316 const CharType c = string.at(i); 1317 if (c == quote) 1318 newString.append(escapedQuote); 1319 else 1320 newString.append(c); 1321 } 1322 if (addQuotes) { 1323 newString.append(quote); 1324 } 1325 newString.squeeze(); 1326 return newString; 1327 } 1328 1329 static bool shouldAddQuotesToIdentifier(const QByteArray& string) 1330 { 1331 return !string.isEmpty() && (!KDb::isIdentifier(string) || KDb::isKDbSqlKeyword(string)); 1332 } 1333 1334 QString KDb::escapeIdentifier(const QString& string) 1335 { 1336 return ::escapeIdentifier<QString, QLatin1String, QLatin1Char, QChar>( 1337 string, shouldAddQuotesToIdentifier(string.toLatin1())); 1338 } 1339 1340 QByteArray KDb::escapeIdentifier(const QByteArray& string) 1341 { 1342 return ::escapeIdentifier<QByteArray, QByteArray, char, char>( 1343 string, shouldAddQuotesToIdentifier(string)); 1344 } 1345 1346 QString KDb::escapeIdentifierAndAddQuotes(const QString& string) 1347 { 1348 return ::escapeIdentifier<QString, QLatin1String, QLatin1Char, QChar>(string, true); 1349 } 1350 1351 QByteArray KDb::escapeIdentifierAndAddQuotes(const QByteArray& string) 1352 { 1353 return ::escapeIdentifier<QByteArray, QByteArray, char, char>(string, true); 1354 } 1355 1356 QString KDb::escapeString(const QString& string) 1357 { 1358 const QLatin1Char quote('\''); 1359 // find out the length ot the destination string 1360 // create 1361 QString newString(quote); 1362 newString.reserve(estimatedNewLength(string, true)); 1363 for (int i = 0; i < string.length(); i++) { 1364 const QChar c = string.at(i); 1365 const ushort unicode = c.unicode(); 1366 if (unicode == quote) 1367 newString.append(QLatin1String("''")); 1368 else if (unicode == '\t') 1369 newString.append(QLatin1String("\\t")); 1370 else if (unicode == '\\') 1371 newString.append(QLatin1String("\\\\")); 1372 else if (unicode == '\n') 1373 newString.append(QLatin1String("\\n")); 1374 else if (unicode == '\r') 1375 newString.append(QLatin1String("\\r")); 1376 else if (unicode == '\0') 1377 newString.append(QLatin1String("\\0")); 1378 else 1379 newString.append(c); 1380 } 1381 newString.append(QLatin1Char(quote)); 1382 return newString; 1383 } 1384 1385 KDbEscapedString KDb::escapeString(KDbDriver *drv, const QString& string) 1386 { 1387 return drv ? drv->escapeString(string) : KDbEscapedString(KDb::escapeString(string)); 1388 } 1389 1390 KDbEscapedString KDb::escapeString(KDbConnection *conn, const QString& string) 1391 { 1392 return conn ? conn->escapeString(string) : KDbEscapedString(KDb::escapeString(string)); 1393 } 1394 1395 //! @see handleHex() 1396 const int CODE_POINT_DIGITS = std::numeric_limits<int>::max(); 1397 //! @see handleHex() 1398 const int MAX_CODE_POINT_VALUE = 0x10FFFF; 1399 1400 //! @internal Decodes hex of length @a digits for handleXhh(), handleUxxxx() and handleUcodePoint() 1401 //! If @a digits is CODE_POINT_DIGITS, any number of hex digits is decoded until '}' character 1402 //! is found (error if not found), and the function succeeds when the resulting number 1403 //! is not larger than MAX_CODE_POINT_VALUE. 1404 //! If @a digits is smaller than CODE_POINT_DIGITS the function succeeds only if exactly @a digits 1405 //! number of digits has been found. 1406 //! @return -1 on error (when invalid character found or on missing character 1407 //! or if the resulting number is too large) 1408 //! @see KDb::unescapeString() 1409 static int handleHex(QString *result, int *from, int stringLen, int *errorPosition, int digits) 1410 { 1411 int digit = 0; 1412 for (int i=0; i<digits; ++i) { 1413 if ((*from + 1) >= stringLen) { // unfinished 1414 if (errorPosition) { 1415 *errorPosition = *from; 1416 } 1417 return -1; 1418 } 1419 ++(*from); 1420 if (digits == CODE_POINT_DIGITS && (*result)[*from] == QLatin1Char('}')) { 1421 // special case: code point character decoded 1422 if (i == 0) { 1423 if (errorPosition) { 1424 *errorPosition = *from; 1425 } 1426 return -1; 1427 } 1428 return digit; 1429 } 1430 const unsigned char d = hexDigitToInt((*result)[*from].toLatin1()); 1431 if (d == 0xFF) { // unfinished or wrong character 1432 if (errorPosition) { 1433 *errorPosition = *from; 1434 } 1435 return -1; 1436 } 1437 digit = (digit << 4) + d; 1438 if (digits == CODE_POINT_DIGITS) { 1439 if (digit > MAX_CODE_POINT_VALUE) { // special case: exceeded limit of code point 1440 if (errorPosition) { 1441 *errorPosition = *from; 1442 } 1443 return -1; 1444 } 1445 } 1446 } 1447 return digit; 1448 } 1449 1450 //! @internal Handles \xhh format for handleEscape() 1451 //! Assumption: the @a *from points to "x" in the "\x" 1452 //! @see KDb::unescapeString() 1453 static bool handleXhh(QString *result, int *from, int to, int stringLen, int *errorPosition) 1454 { 1455 const int intDigit = handleHex(result, from, stringLen, errorPosition, 2); 1456 if (intDigit == -1) { 1457 return false; 1458 } 1459 (*result)[to] = QChar(static_cast<unsigned char>(intDigit), 0); 1460 return true; 1461 } 1462 1463 //! @internal Handles \uxxxx format for handleEscape() 1464 //! Assumption: the @a *from points to the "u" in the "\u". 1465 //! @see KDb::unescapeString() 1466 static bool handleUxxxx(QString *result, int *from, int to, int stringLen, int *errorPosition) 1467 { 1468 const int intDigit = handleHex(result, from, stringLen, errorPosition, 4); 1469 if (intDigit == -1) { 1470 return false; 1471 } 1472 (*result)[to] = QChar(static_cast<unsigned short>(intDigit)); 1473 return true; 1474 } 1475 1476 //! @internal Handles \u{xxxxxx} format for handleEscape() 1477 //! Assumption: the @a *from points to the "{" in the "\u{". 1478 //! @see KDb::unescapeString() 1479 static bool handleUcodePoint(QString *result, int *from, int to, int stringLen, int *errorPosition) 1480 { 1481 const int intDigit = handleHex(result, from, stringLen, errorPosition, CODE_POINT_DIGITS); 1482 if (intDigit == -1) { 1483 return false; 1484 } 1485 (*result)[to] = QChar(intDigit); 1486 return true; 1487 } 1488 1489 //! @internal Handles escaped character @a c2 for KDb::unescapeString() 1490 //! Updates @a result 1491 //! @return true on success 1492 static bool handleEscape(QString *result, int *from, int *to, int stringLen, int *errorPosition) 1493 { 1494 const QCharRef c2 = (*result)[*from]; 1495 if (c2 == QLatin1Char('x')) { // \xhh 1496 if (!handleXhh(result, from, *to, stringLen, errorPosition)) { 1497 return false; 1498 } 1499 } else if (c2 == QLatin1Char('u')) { // \u 1500 if ((*from + 1) >= stringLen) { // unfinished 1501 if (errorPosition) { 1502 *errorPosition = *from; 1503 } 1504 return false; 1505 } 1506 ++(*from); 1507 const QCharRef c3 = (*result)[*from]; 1508 if (c3 == QLatin1Char('{')) { // \u{ 1509 if (!handleUcodePoint(result, from, *to, stringLen, errorPosition)) { 1510 return false; 1511 } 1512 } else { 1513 --(*from); 1514 if (!handleUxxxx(result, from, *to, stringLen, errorPosition)) { 1515 return false; 1516 } 1517 } 1518 #define _RULE(in, out) \ 1519 } else if (c2 == QLatin1Char(in)) { \ 1520 (*result)[*to] = QLatin1Char(out); 1521 _RULE('0', '\0') _RULE('b', '\b') _RULE('f', '\f') _RULE('n', '\n') 1522 _RULE('r', '\r') _RULE('t', '\t') _RULE('v', '\v') 1523 #undef _RULE 1524 } else { // \ ' " ? % _ and any other without special meaning can be escaped: just skip "\" 1525 (*result)[*to] = c2; 1526 } 1527 return true; 1528 } 1529 1530 QString KDb::unescapeString(const QString& string, char quote, int *errorPosition) 1531 { 1532 if (quote != '\'' && quote != '\"') { 1533 if (errorPosition) { 1534 *errorPosition = 0; 1535 } 1536 return QString(); 1537 } 1538 const QLatin1Char quoteChar(quote); 1539 if (string.isEmpty() 1540 || (!string.contains(QLatin1Char('\\')) && !string.contains(quoteChar))) 1541 { 1542 if (errorPosition) { 1543 *errorPosition = -1; 1544 } 1545 return string; // optimization: there are no escapes and quotes 1546 } 1547 QString result(string); 1548 const int stringLen = string.length(); 1549 int from = 0; 1550 int to = 0; 1551 bool doubleQuoteExpected = false; 1552 while (from < stringLen) { 1553 const QCharRef c = result[from]; 1554 if (doubleQuoteExpected) { 1555 if (c == quoteChar) { 1556 result[to] = c; 1557 doubleQuoteExpected = false; 1558 } else { 1559 // error: missing second quote 1560 if (errorPosition) { 1561 *errorPosition = from - 1; // -1 because error is at prev. char 1562 } 1563 return QString(); 1564 } 1565 } else if (c == quoteChar) { 1566 doubleQuoteExpected = true; 1567 ++from; 1568 continue; 1569 } else if (c == QLatin1Char('\\')) { // escaping 1570 if ((from + 1) >= stringLen) { // ignore unfinished '\' 1571 break; 1572 } 1573 ++from; 1574 if (!handleEscape(&result, &from, &to, stringLen, errorPosition)) { 1575 return QString(); 1576 } 1577 } else { // normal character: skip 1578 result[to] = result[from]; 1579 } 1580 ++from; 1581 ++to; 1582 } 1583 if (doubleQuoteExpected) { // error: string ends with a single quote 1584 if (errorPosition) { 1585 *errorPosition = from - 1; 1586 } 1587 return QString(); 1588 } 1589 if (errorPosition) { 1590 *errorPosition = -1; 1591 } 1592 result.truncate(to); 1593 return result; 1594 } 1595 1596 //! @return hex digit '0'..'F' for integer number 0..15 1597 inline static char intToHexDigit(unsigned char val) 1598 { 1599 return (val < 10) ? ('0' + val) : ('A' + (val - 10)); 1600 } 1601 1602 QString KDb::escapeBLOB(const QByteArray& array, BLOBEscapingType type) 1603 { 1604 const int size = array.size(); 1605 if (size == 0 && type == BLOBEscapingType::ZeroXHex) 1606 return QString(); 1607 int escaped_length = size * 2; 1608 if (type == BLOBEscapingType::ZeroXHex || type == BLOBEscapingType::Octal) 1609 escaped_length += 2/*0x or X'*/; 1610 else if (type == BLOBEscapingType::XHex) 1611 escaped_length += 3; //X' + ' 1612 else if (type == BLOBEscapingType::ByteaHex) 1613 escaped_length += (4 + 8); // E'\x + '::bytea 1614 1615 QString str; 1616 str.reserve(escaped_length); 1617 if (str.capacity() < escaped_length) { 1618 kdbWarning() << "Not enough memory (cannot allocate" << escaped_length << "characters)"; 1619 return QString(); 1620 } 1621 if (type == BLOBEscapingType::XHex) 1622 str = QString::fromLatin1("X'"); 1623 else if (type == BLOBEscapingType::ZeroXHex) 1624 str = QString::fromLatin1("0x"); 1625 else if (type == BLOBEscapingType::Octal) 1626 str = QString::fromLatin1("'"); 1627 else if (type == BLOBEscapingType::ByteaHex) 1628 str = QString::fromLatin1("E'\\\\x"); 1629 1630 if (type == BLOBEscapingType::Octal) { 1631 // only escape nonprintable characters as in Table 8-7: 1632 // https://www.postgresql.org/docs/8.1/interactive/datatype-binary.html 1633 // i.e. escape for bytes: < 32, >= 127, 39 ('), 92(\). 1634 for (int i = 0; i < size; i++) { 1635 const unsigned char val = array[i]; 1636 if (val < 32 || val >= 127 || val == 39 || val == 92) { 1637 str.append(QLatin1Char('\\')); 1638 str.append(QLatin1Char('\\')); 1639 str.append(QChar::fromLatin1('0' + val / 64)); 1640 str.append(QChar::fromLatin1('0' + (val % 64) / 8)); 1641 str.append(QChar::fromLatin1('0' + val % 8)); 1642 } else { 1643 str.append(QChar::fromLatin1(val)); 1644 } 1645 } 1646 } else { 1647 for (int i = 0; i < size; i++) { 1648 const unsigned char val = array[i]; 1649 str.append(QChar::fromLatin1(intToHexDigit(val / 16))); 1650 str.append(QChar::fromLatin1(intToHexDigit(val % 16))); 1651 } 1652 } 1653 if (type == BLOBEscapingType::XHex || type == BLOBEscapingType::Octal) { 1654 str.append(QLatin1Char('\'')); 1655 } else if (type == BLOBEscapingType::ByteaHex) { 1656 str.append(QLatin1String("\'::bytea")); 1657 } 1658 return str; 1659 } 1660 1661 QByteArray KDb::pgsqlByteaToByteArray(const char* data, int length) 1662 { 1663 if (!data) { 1664 return QByteArray(); 1665 } 1666 QByteArray array; 1667 int output = 0; 1668 if (length < 0) { 1669 length = qstrlen(data); 1670 } 1671 for (int pass = 0; pass < 2; pass++) {//2 passes to avoid allocating buffer twice: 1672 // 0: count #of chars; 1: copy data 1673 const char* s = data; 1674 const char* end = s + length; 1675 if (pass == 1) { 1676 //kdbDebug() << "processBinaryData(): real size == " << output; 1677 array.resize(output); 1678 output = 0; 1679 } 1680 for (int input = 0; s < end; output++) { 1681 // kdbDebug()<<(int)s[0]<<" "<<(int)s[1]<<" "<<(int)s[2]<<" "<<(int)s[3]<<" "<<(int)s[4]; 1682 if (s[0] == '\\' && (s + 1) < end) { 1683 //special cases as in https://www.postgresql.org/docs/8.1/interactive/datatype-binary.html 1684 if (s[1] == '\'') {// \' 1685 if (pass == 1) 1686 array[output] = '\''; 1687 s += 2; 1688 } else if (s[1] == '\\') { // 2 backslashes 1689 if (pass == 1) 1690 array[output] = '\\'; 1691 s += 2; 1692 } else if ((input + 3) < length) {// \\xyz where xyz are 3 octal digits 1693 if (pass == 1) 1694 array[output] = char((int(s[1] - '0') * 8 + int(s[2] - '0')) * 8 + int(s[3] - '0')); 1695 s += 4; 1696 } else { 1697 kdbWarning() << "Missing octal value after backslash"; 1698 s++; 1699 } 1700 } else { 1701 if (pass == 1) 1702 array[output] = s[0]; 1703 s++; 1704 } 1705 // kdbDebug()<<output<<": "<<(int)array[output]; 1706 } 1707 } 1708 return array; 1709 } 1710 1711 QByteArray KDb::xHexToByteArray(const char* data, int length, bool *ok) 1712 { 1713 if (length < 0) { 1714 length = qstrlen(data); 1715 } 1716 if (length < 3 || data[0] != 'X' || data[1] != '\'' || data[length-1] != '\'') { // must be at least X'' 1717 if (ok) { 1718 *ok = false; 1719 } 1720 return QByteArray(); 1721 } 1722 data += 2; // eat X' 1723 length -= 3; // eax X' and ' 1724 QByteArray array; 1725 if (!hexToByteArrayInternal(data, length, &array)) { 1726 if (ok) { 1727 *ok = false; 1728 } 1729 array.clear(); 1730 } 1731 if (ok) { 1732 *ok = true; 1733 } 1734 return array; 1735 } 1736 1737 /*! \return byte array converted from \a data of length \a length. 1738 \a data is escaped in format 0x*, where * is one or more bytes in hexadecimal format. 1739 See BLOBEscapingType::ZeroXHex. */ 1740 QByteArray KDb::zeroXHexToByteArray(const char* data, int length, bool *ok) 1741 { 1742 if (length < 0) { 1743 length = qstrlen(data); 1744 } 1745 if (length < 3 || data[0] != '0' || data[1] != 'x') { // must be at least 0xD 1746 if (ok) { 1747 *ok = false; 1748 } 1749 return QByteArray(); 1750 } 1751 data += 2; // eat 0x 1752 length -= 2; 1753 QByteArray array; 1754 if (!hexToByteArrayInternal(data, length, &array)) { 1755 if (ok) { 1756 *ok = false; 1757 } 1758 array.clear(); 1759 } 1760 if (ok) { 1761 *ok = true; 1762 } 1763 return array; 1764 } 1765 1766 QList<int> KDb::stringListToIntList(const QStringList &list, bool *ok) 1767 { 1768 QList<int> result; 1769 foreach (const QString &item, list) { 1770 int val = item.toInt(ok); 1771 if (ok && !*ok) { 1772 return QList<int>(); 1773 } 1774 result.append(val); 1775 } 1776 if (ok) { 1777 *ok = true; 1778 } 1779 return result; 1780 } 1781 1782 // Based on KConfigGroupPrivate::serializeList() from kconfiggroup.cpp (kdelibs 4) 1783 QString KDb::serializeList(const QStringList &list) 1784 { 1785 QString value; 1786 1787 if (!list.isEmpty()) { 1788 QStringList::ConstIterator it = list.constBegin(); 1789 const QStringList::ConstIterator end = list.constEnd(); 1790 1791 value = QString(*it).replace(QLatin1Char('\\'), QLatin1String("\\\\")) 1792 .replace(QLatin1Char(','), QLatin1String("\\,")); 1793 1794 while (++it != end) { 1795 // In the loop, so it is not done when there is only one element. 1796 // Doing it repeatedly is a pretty cheap operation. 1797 value.reserve(4096); 1798 1799 value += QLatin1Char(',') 1800 + QString(*it).replace(QLatin1Char('\\'), QLatin1String("\\\\")) 1801 .replace(QLatin1Char(','), QLatin1String("\\,")); 1802 } 1803 1804 // To be able to distinguish an empty list from a list with one empty element. 1805 if (value.isEmpty()) 1806 value = QLatin1String("\\0"); 1807 } 1808 1809 return value; 1810 } 1811 1812 // Based on KConfigGroupPrivate::deserializeList() from kconfiggroup.cpp (kdelibs 4) 1813 QStringList KDb::deserializeList(const QString &data) 1814 { 1815 if (data.isEmpty()) 1816 return QStringList(); 1817 if (data == QLatin1String("\\0")) 1818 return QStringList(QString()); 1819 QStringList value; 1820 QString val; 1821 val.reserve(data.size()); 1822 bool quoted = false; 1823 for (int p = 0; p < data.length(); p++) { 1824 if (quoted) { 1825 val += data[p]; 1826 quoted = false; 1827 } else if (data[p].unicode() == QLatin1Char('\\')) { 1828 quoted = true; 1829 } else if (data[p].unicode() == QLatin1Char(',')) { 1830 val.squeeze(); // release any unused memory 1831 value.append(val); 1832 val.clear(); 1833 val.reserve(data.size() - p); 1834 } else { 1835 val += data[p]; 1836 } 1837 } 1838 value.append(val); 1839 return value; 1840 } 1841 1842 QList<int> KDb::deserializeIntList(const QString &data, bool *ok) 1843 { 1844 return KDb::stringListToIntList( 1845 KDb::deserializeList(data), ok); 1846 } 1847 1848 QString KDb::variantToString(const QVariant& v) 1849 { 1850 if (v.type() == QVariant::ByteArray) { 1851 return KDb::escapeBLOB(v.toByteArray(), KDb::BLOBEscapingType::Hex); 1852 } 1853 else if (v.type() == QVariant::StringList) { 1854 return serializeList(v.toStringList()); 1855 } 1856 return v.toString(); 1857 } 1858 1859 QVariant KDb::stringToVariant(const QString& s, QVariant::Type type, bool* ok) 1860 { 1861 if (s.isNull()) { 1862 if (ok) 1863 *ok = true; 1864 return QVariant(); 1865 } 1866 switch (type) { 1867 case QVariant::Invalid: 1868 if (ok) 1869 *ok = false; 1870 return QVariant(); 1871 case QVariant::ByteArray: {//special case: hex string 1872 const int len = s.length(); 1873 QByteArray ba; 1874 ba.resize(len / 2 + len % 2); 1875 for (int i = 0; i < (len - 1); i += 2) { 1876 bool _ok; 1877 int c = s.midRef(i, 2).toInt(&_ok, 16); 1878 if (!_ok) { 1879 if (ok) 1880 *ok = _ok; 1881 kdbWarning() << "Error in digit" << i; 1882 return QVariant(); 1883 } 1884 ba[i/2] = (char)c; 1885 } 1886 if (ok) 1887 *ok = true; 1888 return ba; 1889 } 1890 case QVariant::StringList: 1891 *ok = true; 1892 return KDb::deserializeList(s); 1893 default:; 1894 } 1895 1896 QVariant result(s); 1897 if (!result.convert(type)) { 1898 if (ok) 1899 *ok = false; 1900 return QVariant(); 1901 } 1902 if (ok) 1903 *ok = true; 1904 return result; 1905 } 1906 1907 bool KDb::isDefaultValueAllowed(const KDbField &field) 1908 { 1909 return !field.isUniqueKey(); 1910 } 1911 1912 void KDb::getLimitsForFieldType(KDbField::Type type, qlonglong *minValue, qlonglong *maxValue, 1913 Signedness signedness) 1914 { 1915 if (!minValue || !maxValue) { 1916 return; 1917 } 1918 switch (type) { 1919 case KDbField::Byte: 1920 //! @todo always ok? 1921 *minValue = signedness == KDb::Signed ? -0x80 : 0; 1922 *maxValue = signedness == KDb::Signed ? 0x7F : 0xFF; 1923 break; 1924 case KDbField::ShortInteger: 1925 *minValue = signedness == KDb::Signed ? -0x8000 : 0; 1926 *maxValue = signedness == KDb::Signed ? 0x7FFF : 0xFFFF; 1927 break; 1928 case KDbField::Integer: 1929 case KDbField::BigInteger: //!< @todo cannot return anything larger? 1930 default: 1931 *minValue = signedness == KDb::Signed ? qlonglong(-0x07FFFFFFF) : qlonglong(0); 1932 *maxValue = signedness == KDb::Signed ? qlonglong(0x07FFFFFFF) : qlonglong(0x0FFFFFFFF); 1933 } 1934 } 1935 1936 KDbField::Type KDb::maximumForIntegerFieldTypes(KDbField::Type t1, KDbField::Type t2) 1937 { 1938 if (!KDbField::isIntegerType(t1) || !KDbField::isIntegerType(t2)) 1939 return KDbField::InvalidType; 1940 if (t1 == t2) 1941 return t2; 1942 if (t1 == KDbField::ShortInteger && t2 != KDbField::Integer && t2 != KDbField::BigInteger) 1943 return t1; 1944 if (t1 == KDbField::Integer && t2 != KDbField::BigInteger) 1945 return t1; 1946 if (t1 == KDbField::BigInteger) 1947 return t1; 1948 return KDb::maximumForIntegerFieldTypes(t2, t1); //swap 1949 } 1950 1951 QString KDb::simplifiedFieldTypeName(KDbField::Type type) 1952 { 1953 if (KDbField::isNumericType(type)) 1954 return KDbField::tr("Number"); //simplify 1955 else if (type == KDbField::BLOB) 1956 //! @todo support names of other BLOB subtypes 1957 return KDbField::tr("Image"); //simplify 1958 1959 return KDbField::typeGroupName(KDbField::typeGroup(type)); 1960 } 1961 1962 QString KDb::defaultFileBasedDriverMimeType() 1963 { 1964 return QLatin1String("application/x-kexiproject-sqlite3"); 1965 } 1966 1967 QString KDb::defaultFileBasedDriverId() 1968 { 1969 return QLatin1String("org.kde.kdb.sqlite"); 1970 } 1971 1972 // Try to convert from string to type T 1973 template <typename T> 1974 QVariant convert(T (QString::*ConvertToT)(bool*,int) const, const char *data, int size, 1975 qlonglong minValue, qlonglong maxValue, bool *ok) 1976 { 1977 T v = (QString::fromLatin1(data, size).*ConvertToT)(ok, 10); 1978 if (*ok) { 1979 *ok = minValue <= v && v <= maxValue; 1980 } 1981 return KDb::iif(*ok, QVariant(v)); 1982 } 1983 1984 QVariant KDb::cstringToVariant(const char* data, KDbField::Type type, bool *ok, int length, 1985 KDb::Signedness signedness) 1986 { 1987 bool tempOk; 1988 bool *thisOk = ok ? ok : &tempOk; 1989 if (type < KDbField::Byte || type > KDbField::LastType) { 1990 *thisOk = false; 1991 return QVariant(); 1992 } 1993 if (!data) { // NULL value 1994 *thisOk = true; 1995 return QVariant(); 1996 } 1997 // from most to least frequently used types: 1998 1999 if (KDbField::isTextType(type)) { 2000 *thisOk = true; 2001 //! @todo use KDbDriverBehavior::TEXT_TYPE_MAX_LENGTH for Text type? 2002 return QString::fromUtf8(data, length); 2003 } 2004 if (KDbField::isIntegerType(type)) { 2005 qlonglong minValue, maxValue; 2006 const bool isUnsigned = signedness == KDb::Unsigned; 2007 KDb::getLimitsForFieldType(type, &minValue, &maxValue, signedness); 2008 switch (type) { 2009 case KDbField::Byte: // Byte here too, minValue/maxValue will take care of limits 2010 case KDbField::ShortInteger: 2011 return isUnsigned ? 2012 convert(&QString::toUShort, data, length, minValue, maxValue, thisOk) 2013 : convert(&QString::toShort, data, length, minValue, maxValue, thisOk); 2014 case KDbField::Integer: 2015 return isUnsigned ? 2016 convert(&QString::toUInt, data, length, minValue, maxValue, thisOk) 2017 : convert(&QString::toInt, data, length, minValue, maxValue, thisOk); 2018 case KDbField::BigInteger: 2019 return convert(&QString::toLongLong, data, length, minValue, maxValue, thisOk); 2020 default: 2021 qFatal("Unsupported integer type %d", type); 2022 } 2023 } 2024 if (KDbField::isFPNumericType(type)) { 2025 const QVariant result(QString::fromLatin1(data, length).toDouble(thisOk)); 2026 return KDb::iif(*thisOk, result); 2027 } 2028 if (type == KDbField::BLOB) { 2029 *thisOk = length >= 0; 2030 return *thisOk ? QVariant(QByteArray(data, length)) : QVariant(); 2031 } 2032 // the default 2033 //! @todo date/time? 2034 QVariant result(QString::fromUtf8(data, length)); 2035 if (!result.convert(KDbField::variantType(type))) { 2036 *thisOk = false; 2037 return QVariant(); 2038 } 2039 *thisOk = true; 2040 return result; 2041 } 2042 2043 QStringList KDb::libraryPaths() 2044 { 2045 QStringList result; 2046 foreach (const QString& path, qApp->libraryPaths()) { 2047 const QString dir(path + QLatin1Char('/') + QLatin1String(KDB_BASE_NAME_LOWER)); 2048 if (QDir(dir).exists() && QDir(dir).isReadable()) { 2049 result += dir; 2050 } 2051 } 2052 return result; 2053 } 2054 2055 QString KDb::temporaryTableName(KDbConnection *conn, const QString &baseName) 2056 { 2057 if (!conn) { 2058 return QString(); 2059 } 2060 while (true) { 2061 QString name = QLatin1String("tmp__") + baseName; 2062 for (int i = 0; i < 10; ++i) { 2063 name += QString::number(int(double(qrand()) / RAND_MAX * 0x10), 16); 2064 } 2065 const tristate res = conn->containsTable(name); 2066 if (~res) { 2067 return QString(); 2068 } else if (res == false) { 2069 return name; 2070 } 2071 } 2072 } 2073 2074 QString KDb::sqlite3ProgramPath() 2075 { 2076 QString path = KDbUtils::findExe(QLatin1String("sqlite3")); 2077 if (path.isEmpty()) { 2078 kdbWarning() << "Could not find program \"sqlite3\""; 2079 } 2080 return path; 2081 } 2082 2083 bool KDb::importSqliteFile(const QString &inputFileName, const QString &outputFileName) 2084 { 2085 const QString sqlite_app = KDb::sqlite3ProgramPath(); 2086 if (sqlite_app.isEmpty()) { 2087 return false; 2088 } 2089 2090 QFileInfo fi(inputFileName); 2091 if (!fi.isReadable()) { 2092 kdbWarning() << "No readable input file" << fi.absoluteFilePath(); 2093 return false; 2094 } 2095 QFileInfo fo(outputFileName); 2096 if (QFile(fo.absoluteFilePath()).exists()) { 2097 if (!QFile::remove(fo.absoluteFilePath())) { 2098 kdbWarning() << "Could not remove output file" << fo.absoluteFilePath(); 2099 return false; 2100 } 2101 } 2102 kdbDebug() << inputFileName << fi.absoluteDir().path() << fo.absoluteFilePath(); 2103 2104 QProcess p; 2105 p.start(sqlite_app, QStringList() << fo.absoluteFilePath()); 2106 if (!p.waitForStarted()) { 2107 kdbWarning() << "Failed to start program" << sqlite_app; 2108 return false; 2109 } 2110 QByteArray line(".read " + QFile::encodeName(fi.absoluteFilePath())); 2111 if (p.write(line) != line.length() || !p.waitForBytesWritten()) { 2112 kdbWarning() << "Failed to send \".read\" command to program" << sqlite_app; 2113 return false; 2114 } 2115 p.closeWriteChannel(); 2116 if (!p.waitForFinished()) { 2117 kdbWarning() << "Failed to finish program" << sqlite_app; 2118 return false; 2119 } 2120 return true; 2121 } 2122 2123 //--------- 2124 2125 bool KDb::isIdentifier(const QString& s) 2126 { 2127 int i; 2128 const int sLength = s.length(); 2129 for (i = 0; i < sLength; i++) { 2130 const char c = s.at(i).toLower().toLatin1(); 2131 if (c == 0 || !(c == '_' || (c >= 'a' && c <= 'z') || (i > 0 && c >= '0' && c <= '9'))) 2132 break; 2133 } 2134 return i > 0 && i == sLength; 2135 } 2136 2137 bool KDb::isIdentifier(const QByteArray& s) 2138 { 2139 int i; 2140 const int sLength = s.length(); 2141 for (i = 0; i < sLength; i++) { 2142 const char c = s.at(i); 2143 if (c == 0 || !(c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (i > 0 && c >= '0' && c <= '9'))) { 2144 break; 2145 } 2146 } 2147 return i > 0 && i == sLength; 2148 } 2149 2150 static inline QString charToIdentifier(const QChar& c) 2151 { 2152 if (c.unicode() >= TRANSLITERATION_TABLE_SIZE) 2153 return QLatin1String("_"); 2154 const char *const s = transliteration_table[c.unicode()]; 2155 return s ? QString::fromLatin1(s) : QLatin1String("_"); 2156 } 2157 2158 QString KDb::stringToIdentifier(const QString &s) 2159 { 2160 if (s.isEmpty()) 2161 return QString(); 2162 QString r, id = s.simplified(); 2163 if (id.isEmpty()) 2164 return QString(); 2165 r.reserve(id.length()); 2166 id.replace(QLatin1Char(' '), QLatin1String("_")); 2167 const QChar c = id[0]; 2168 const char ch = c.toLatin1(); 2169 QString add; 2170 bool wasUnderscore = false; 2171 2172 if (ch >= '0' && ch <= '9') { 2173 r += QLatin1Char('_') + c; 2174 } else { 2175 add = charToIdentifier(c); 2176 r += add; 2177 wasUnderscore = add == QLatin1String("_"); 2178 } 2179 2180 const int idLength = id.length(); 2181 for (int i = 1; i < idLength; i++) { 2182 add = charToIdentifier(id.at(i)); 2183 if (wasUnderscore && add == QLatin1String("_")) 2184 continue; 2185 wasUnderscore = add == QLatin1String("_"); 2186 r += add; 2187 } 2188 return r; 2189 } 2190 2191 QString KDb::identifierExpectedMessage(const QString &valueName, const QVariant& v) 2192 { 2193 return QLatin1String("<p>") + kdb::tr("Value of \"%1\" field must be an identifier.") 2194 .arg(valueName) 2195 + QLatin1String("</p><p>") 2196 + kdb::tr("\"%1\" is not a valid identifier.").arg(v.toString()) + QLatin1String("</p>"); 2197 } 2198 2199 //--------- 2200 2201 KDbEscapedString KDb::valueToSql(KDbField::Type ftype, const QVariant& v) 2202 { 2203 return valueToSqlInternal(nullptr, ftype, v); 2204 } 2205 2206 static QByteArray dateToSqlInternal(const QVariant& v, bool allowInvalidKDbDate) 2207 { 2208 QByteArray result(QByteArrayLiteral("<INVALID_DATE>")); 2209 if (v.canConvert<KDbDate>()) { 2210 const KDbDate date(v.value<KDbDate>()); 2211 if (date.isValid() || allowInvalidKDbDate) { 2212 result = date.toString(); // OK even if invalid or null 2213 } 2214 } else if (v.canConvert<QDate>()) { 2215 const QDate date(v.toDate()); 2216 if (date.isValid()) { 2217 result = date.toString(Qt::ISODate).toLatin1(); 2218 } 2219 } 2220 return result; 2221 } 2222 2223 KDbEscapedString KDb::dateToSql(const QVariant& v) 2224 { 2225 return KDbEscapedString('#') + dateToSqlInternal(v, true) + '#'; 2226 } 2227 2228 static QByteArray timeToSqlInternal(const QVariant& v, bool allowInvalidKDbTime) 2229 { 2230 QByteArray result(QByteArrayLiteral("<INVALID_TIME>")); 2231 if (v.canConvert<KDbTime>()) { 2232 const KDbTime time(v.value<KDbTime>()); 2233 if (time.isValid() || allowInvalidKDbTime) { 2234 result = time.toString(); // OK even if invalid or null 2235 } 2236 } else if (v.canConvert<QTime>()) { 2237 const QTime time(v.toTime()); 2238 if (time.isValid()) { 2239 if (time.msec() == 0) { 2240 result = time.toString(Qt::ISODate).toLatin1(); 2241 } else { 2242 result = KDbUtils::toISODateStringWithMs(time).toLatin1(); 2243 } 2244 } 2245 } 2246 return result; 2247 } 2248 2249 KDbEscapedString KDb::timeToSql(const QVariant& v) 2250 { 2251 return KDbEscapedString('#') + timeToSqlInternal(v, true) + '#'; 2252 } 2253 2254 static QByteArray dateTimeToSqlInternal(const QVariant& v, char separator, bool allowInvalidKDbDateTime) 2255 { 2256 QByteArray result(QByteArrayLiteral("<INVALID_DATETIME>")); 2257 if (v.canConvert<KDbDateTime>()) { 2258 const KDbDateTime dateTime(v.value<KDbDateTime>()); 2259 if (dateTime.isValid() || allowInvalidKDbDateTime) { 2260 result = dateTime.toString(); // OK even if invalid or null 2261 } 2262 } else if (v.canConvert<QDateTime>()) { 2263 const QDateTime dateTime(v.toDateTime()); 2264 if (dateTime.isValid()) { 2265 result = dateTime.date().toString(Qt::ISODate).toLatin1() + separator; 2266 const QTime time(dateTime.time()); 2267 if (time.msec() == 0) { 2268 result += time.toString(Qt::ISODate).toLatin1(); 2269 } else { 2270 result += KDbUtils::toISODateStringWithMs(time).toLatin1(); 2271 } 2272 } 2273 } 2274 return result; 2275 } 2276 2277 KDbEscapedString KDb::dateTimeToSql(const QVariant& v) 2278 { 2279 return KDbEscapedString('#') + dateTimeToSqlInternal(v, ' ', true) + '#'; 2280 } 2281 2282 KDbEscapedString KDb::dateTimeToSql(const QDateTime& v) 2283 { 2284 return KDb::dateTimeToIsoString(v); 2285 } 2286 2287 KDbEscapedString KDb::dateToIsoString(const QVariant& v) 2288 { 2289 return KDbEscapedString('\'') + dateToSqlInternal(v, false) + KDbEscapedString('\''); 2290 } 2291 2292 KDbEscapedString KDb::timeToIsoString(const QVariant& v) 2293 { 2294 return KDbEscapedString('\'') + timeToSqlInternal(v, false) + KDbEscapedString('\''); 2295 } 2296 2297 KDbEscapedString KDb::dateTimeToIsoString(const QVariant& v) 2298 { 2299 return KDbEscapedString('\'') + dateTimeToSqlInternal(v, 'T', false) + KDbEscapedString('\''); 2300 } 2301 2302 //-------------------------------------------------------------------------------- 2303 2304 #ifdef KDB_DEBUG_GUI 2305 2306 static KDb::DebugGUIHandler s_debugGUIHandler = nullptr; 2307 2308 void KDb::setDebugGUIHandler(KDb::DebugGUIHandler handler) 2309 { 2310 s_debugGUIHandler = handler; 2311 } 2312 2313 void KDb::debugGUI(const QString& text) 2314 { 2315 if (s_debugGUIHandler) 2316 s_debugGUIHandler(text); 2317 } 2318 2319 static KDb::AlterTableActionDebugGUIHandler s_alterTableActionDebugHandler = nullptr; 2320 2321 void KDb::setAlterTableActionDebugHandler(KDb::AlterTableActionDebugGUIHandler handler) 2322 { 2323 s_alterTableActionDebugHandler = handler; 2324 } 2325 2326 void KDb::alterTableActionDebugGUI(const QString& text, int nestingLevel) 2327 { 2328 if (s_alterTableActionDebugHandler) 2329 s_alterTableActionDebugHandler(text, nestingLevel); 2330 } 2331 2332 #endif // KDB_DEBUG_GUI 2333 2334 #include "KDb.moc"