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"