File indexing completed on 2024-04-21 04:40:07

0001 /* This file is part of the KDE project
0002    Copyright (C) 2015-2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KDbTest.h"
0021 
0022 #include <KDb>
0023 #include <KDbConnectionData>
0024 #include <KDbVersionInfo>
0025 
0026 #include <QRegularExpression>
0027 #include <QTest>
0028 
0029 QTEST_GUILESS_MAIN(KDbTest)
0030 
0031 void KDbTest::initTestCase()
0032 {
0033 }
0034 
0035 void KDbTest::testVersionInfo()
0036 {
0037     KDbVersionInfo info = KDb::version();
0038     KDbVersionInfo info2(KDb::version());
0039     QCOMPARE(info, info2);
0040     KDbVersionInfo info3(info.major(), info.minor(), info.release());
0041     QCOMPARE(info, info3);
0042     QVERIFY(KDbVersionInfo(0, 0, 0).isNull());
0043     QVERIFY(!info.isNull());
0044     QVERIFY(!info2.isNull());
0045     QVERIFY(!info3.isNull());
0046 }
0047 
0048 //! @todo add tests requiring connection
0049 #if 0
0050     //! @overload bool deleteRecord(KDbConnection*, const KDbTableSchema&, const QString &, KDbField::Type, const QVariant &)
0051     KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName,
0052                                   const QString &keyname, KDbField::Type keytype, const QVariant &keyval);
0053     //! Deletes records using one generic criteria.
0054     inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table,
0055                                   const QString &keyname, KDbField::Type keytype, const QVariant &keyval)
0056     //! @overload bool deleteRecords(KDbConnection*, const QString&, const QString&, KDbField::Type, const QVariant&);
0057     inline bool deleteRecords(KDbConnection* conn, const QString &tableName,
0058                                   const QString &keyname, const QString &keyval)
0059     //! @overload bool deleteRecords(KDbConnection*, const QString&, const QString&, const QString&);
0060     inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table,
0061                                   const QString &keyname, const QString &keyval)
0062     //! @overload bool deleteRecords(KDbConnection*, const KDbTableSchema&, const QString&, const QString&);
0063     inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table,
0064                                   const QString& keyname, int keyval)
0065     //! @overload bool deleteRecords(KDbConnection*, const KDbTableSchema&, const QString&, int);
0066     inline bool deleteRecords(KDbConnection* conn, const QString &tableName,
0067                                   const QString& keyname, int keyval)
0068     //! Deletes records with two generic criterias.
0069     KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName,
0070                                  const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1,
0071                                  const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2);
0072     //! Deletes records with three generic criterias.
0073     KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName,
0074                                  const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1,
0075                                  const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2,
0076                                  const QString &keyname3, KDbField::Type keytype3, const QVariant& keyval3);
0077     //! Deletes all records from table @a tableName.
0078     KDB_EXPORT bool deleteAllRecords(KDbConnection* conn, const QString &tableName);
0079     //! @overload bool deleteAllRecords(KDbConnection*, const QString&);
0080     inline bool deleteAllRecords(KDbConnection* conn, const KDbTableSchema &table)
0081 #endif
0082 
0083 void KDbTest::testFieldTypes()
0084 {
0085     QCOMPARE(KDbField::FirstType, KDbField::Byte);
0086     QCOMPARE(KDbField::LastType, KDbField::BLOB);
0087     QVERIFY(KDbField::FirstType < KDbField::LastType);
0088 }
0089 
0090 void KDbTest::testFieldTypesForGroup_data()
0091 {
0092     QTest::addColumn<KDbField::TypeGroup>("typeGroup");
0093     QTest::addColumn<QList<KDbField::Type>>("types");
0094 
0095     int c = 0;
0096     ++c; QTest::newRow("invalid") << KDbField::InvalidGroup
0097         << (QList<KDbField::Type>() << KDbField::InvalidType);
0098     ++c; QTest::newRow("text") << KDbField::TextGroup << (QList<KDbField::Type>()
0099         << KDbField::Text << KDbField::LongText);
0100     ++c; QTest::newRow("integer") << KDbField::IntegerGroup
0101         << (QList<KDbField::Type>()
0102         << KDbField::Byte << KDbField::ShortInteger << KDbField::Integer << KDbField::BigInteger);
0103     ++c; QTest::newRow("float") << KDbField::FloatGroup
0104         << (QList<KDbField::Type>() << KDbField::Float << KDbField::Double);
0105     ++c; QTest::newRow("boolean") << KDbField::BooleanGroup
0106         << (QList<KDbField::Type>() << KDbField::Boolean);
0107     ++c; QTest::newRow("datetime") << KDbField::DateTimeGroup
0108         << (QList<KDbField::Type>() << KDbField::Date << KDbField::DateTime << KDbField::Time);
0109     ++c; QTest::newRow("blob") << KDbField::BLOBGroup
0110         << (QList<KDbField::Type>() << KDbField::BLOB);
0111     QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything
0112 }
0113 
0114 void KDbTest::testFieldTypesForGroup()
0115 {
0116     QFETCH(KDbField::TypeGroup, typeGroup);
0117     QFETCH(QList<KDbField::Type>, types);
0118     QCOMPARE(KDb::fieldTypesForGroup(typeGroup), types);
0119 }
0120 
0121 void KDbTest::testFieldTypeNamesAndStringsForGroup_data()
0122 {
0123     QTest::addColumn<KDbField::TypeGroup>("typeGroup");
0124     QTest::addColumn<QList<QByteArray>>("typeNames");
0125     QTest::addColumn<QStringList>("typeStrings");
0126 
0127     int c = 0;
0128     ++c; QTest::newRow("invalid") << KDbField::InvalidGroup
0129         << (QList<QByteArray>() << "Invalid Type")
0130         << (QStringList() << "InvalidType");
0131     ++c; QTest::newRow("text") << KDbField::TextGroup << (QList<QByteArray>()
0132         << "Text" << "Long Text")
0133         << (QStringList() << "Text" << "LongText");
0134     ++c; QTest::newRow("integer") << KDbField::IntegerGroup
0135         << (QList<QByteArray>()
0136         << "Byte" << "Short Integer Number" << "Integer Number" << "Big Integer Number")
0137         << (QStringList() << "Byte" << "ShortInteger" << "Integer" << "BigInteger");
0138     ++c; QTest::newRow("float") << KDbField::FloatGroup
0139         << (QList<QByteArray>() << "Single Precision Number" << "Double Precision Number")
0140         << (QStringList() << "Float" << "Double");
0141     ++c; QTest::newRow("boolean") << KDbField::BooleanGroup
0142         << (QList<QByteArray>() << "Yes/No Value")
0143         << (QStringList() << "Boolean");
0144     ++c; QTest::newRow("datetime") << KDbField::DateTimeGroup
0145         << (QList<QByteArray>() << "Date" << "Date and Time" << "Time")
0146         << (QStringList() << "Date" << "DateTime" << "Time");
0147     ++c; QTest::newRow("blob") << KDbField::BLOBGroup
0148         << (QList<QByteArray>() << "Object")
0149         << (QStringList() << "BLOB");
0150     QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything
0151 }
0152 
0153 void KDbTest::testFieldTypeNamesAndStringsForGroup()
0154 {
0155     QFETCH(KDbField::TypeGroup, typeGroup);
0156     QFETCH(QList<QByteArray>, typeNames);
0157     QFETCH(QStringList, typeStrings);
0158     QStringList translatedNames;
0159     foreach(const QByteArray &name, typeNames) {
0160         translatedNames.append(KDbField::tr(name.constData()));
0161     }
0162     QCOMPARE(KDb::fieldTypeNamesForGroup(typeGroup), translatedNames);
0163     QCOMPARE(KDb::fieldTypeStringsForGroup(typeGroup), typeStrings);
0164 }
0165 
0166 void KDbTest::testDefaultFieldTypeForGroup()
0167 {
0168     int c = 0;
0169     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::InvalidGroup), KDbField::InvalidType);
0170     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::TextGroup), KDbField::Text);
0171     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::IntegerGroup), KDbField::Integer);
0172     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::FloatGroup), KDbField::Double);
0173     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::BooleanGroup), KDbField::Boolean);
0174     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::DateTimeGroup), KDbField::Date);
0175     ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::BLOBGroup), KDbField::BLOB);
0176     QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything
0177 }
0178 
0179 void KDbTest::testSimplifiedFieldTypeName()
0180 {
0181     int c = 0;
0182     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::InvalidType), KDbField::tr("Invalid Group"));
0183     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Byte), KDbField::tr("Number"));
0184     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::ShortInteger), KDbField::tr("Number"));
0185     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Integer), KDbField::tr("Number"));
0186     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::BigInteger), KDbField::tr("Number"));
0187     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Boolean), KDbField::tr("Yes/No"));
0188     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Date), KDbField::tr("Date/Time"));
0189     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::DateTime), KDbField::tr("Date/Time"));
0190     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Time), KDbField::tr("Date/Time"));
0191     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Float), KDbField::tr("Number"));
0192     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Double), KDbField::tr("Number"));
0193     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Text), KDbField::tr("Text"));
0194     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::LongText), KDbField::tr("Text"));
0195     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::BLOB), KDbField::tr("Image"));
0196     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Null), KDbField::tr("Invalid Group"));
0197     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Asterisk), KDbField::tr("Invalid Group"));
0198     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Enum), KDbField::tr("Invalid Group"));
0199     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Map), KDbField::tr("Invalid Group"));
0200     ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Tuple), KDbField::tr("Invalid Group"));
0201     QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount()); // make sure we're checking everything
0202 }
0203 
0204 void KDbTest::testIsEmptyValue_data()
0205 {
0206     QTest::addColumn<KDbField::Type>("type");
0207     QTest::addColumn<QVariant>("value");
0208     QTest::addColumn<bool>("result");
0209     QTest::addColumn<bool>("resultForNullValue");
0210     QTest::addColumn<bool>("resultForEmptyString");
0211 
0212     int c = 0;
0213     ++c; QTest::newRow("Invalid") << KDbField::InvalidType << QVariant("abc") << false << true << false;
0214     ++c; QTest::newRow("Byte") << KDbField::Byte << QVariant(17) << false << true << false;
0215     ++c; QTest::newRow("ShortInteger") << KDbField::ShortInteger << QVariant(1733) << false << true << false;
0216     ++c; QTest::newRow("Integer") << KDbField::Integer << QVariant(11733) << false << true << false;
0217     ++c; QTest::newRow("BigInteger") << KDbField::BigInteger << QVariant(0xffffff12) << false << true << false;
0218     ++c; QTest::newRow("Boolean") << KDbField::Boolean << QVariant(false) << false << true << false;
0219     ++c; QTest::newRow("Date") << KDbField::Date << QVariant(QDate(2015, 11, 07)) << false << true << false;
0220     ++c; QTest::newRow("DateTime") << KDbField::DateTime << QVariant(QDateTime(QDate(2015, 11, 07), QTime(12, 58, 17))) << false << true << false;
0221     ++c; QTest::newRow("Time") << KDbField::Time << QVariant(QTime(12, 58, 17)) << false << true << false;
0222     ++c; QTest::newRow("Float") << KDbField::Float << QVariant(3.14) << false << true << false;
0223     ++c; QTest::newRow("Double") << KDbField::Double << QVariant(3.1415) << false << true << false;
0224     ++c; QTest::newRow("Text") << KDbField::Text << QVariant(QLatin1String("abc")) << false << false << true;
0225     ++c; QTest::newRow("LongText") << KDbField::LongText << QVariant(QLatin1String("abc")) << false << false << true;
0226     ++c; QTest::newRow("BLOB") << KDbField::LongText << QVariant(QByteArray(5, 'X')) << false << false << true;
0227     ++c; QTest::newRow("Null") << KDbField::Null << QVariant(123) << false << true << false;
0228     ++c; QTest::newRow("Asterisk") << KDbField::Asterisk << QVariant(123) << false << true << false;
0229     ++c; QTest::newRow("Enum") << KDbField::Enum << QVariant(123) << false << true << false;
0230     ++c; QTest::newRow("Map") << KDbField::Map << QVariant(123) << false << true << false;
0231     ++c; QTest::newRow("Tuple") << KDbField::Tuple << QVariant(123) << false << true << false;
0232     QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount());
0233 }
0234 
0235 void KDbTest::testIsEmptyValue()
0236 {
0237     QFETCH(KDbField::Type, type);
0238     QFETCH(QVariant, value);
0239     QFETCH(bool, result);
0240     QFETCH(bool, resultForNullValue);
0241     QFETCH(bool, resultForEmptyString);
0242 
0243     QCOMPARE(KDb::isEmptyValue(type, QVariant()), resultForNullValue);
0244     QCOMPARE(KDb::isEmptyValue(type, QVariant(QString(""))), resultForEmptyString);
0245     QCOMPARE(KDb::isEmptyValue(type, value), result);
0246 }
0247 
0248 //! @todo add tests
0249 #if 0
0250 /*! Sets string pointed by @a msg to an error message retrieved from @a resultable,
0251  and string pointed by @a details to details of this error (server message and result number).
0252  Does nothing if @a result is empty. In this case @a msg and @a details strings are not overwritten.
0253  If the string pointed by @a msg is not empty, @a result message is appended to the string
0254  pointed by @a details.
0255  */
0256 KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, QString *msg, QString *details);
0257 
0258 /*! This methods works like above, but appends both a message and a description
0259  to string pointed by @a msg. */
0260 KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, QString *msg);
0261 
0262 /*! This methods works like above, but works on @a result's  members instead. */
0263 KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, KDbResultInfo *info);
0264 
0265 /*! Function useful for building WHERE parts of SQL statements.
0266  Constructs an SQL string like "fielname = value" for specific @a drv driver,
0267  field type @a t, @a fieldName and @a value. If @a value is null, "fieldname is NULL"
0268  string is returned. */
0269 KDB_EXPORT KDbEscapedString sqlWhere(KDbDriver *drv, KDbField::Type t,
0270                                         const QString& fieldName, const QVariant& value);
0271 
0272 /*! Find an identifier for object @a objName of type @a objType.
0273  On success true is returned and *id is set to the value of the identifier.
0274  On failure false is returned. If there is no such object, @c cancelled value is returned. */
0275 KDB_EXPORT tristate idForObjectName(KDbConnection* conn, int *id, const QString& objName,
0276                                     int objType);
0277 
0278 /*! @return a number of columns that can be retrieved from table or query schema.
0279  In case of query, expanded fields are counted. Can return -1 if @a tableOrQuery
0280  has neither table or query assigned. */
0281 KDB_EXPORT int fieldCount(KDbTableOrQuerySchema* tableOrQuery);
0282 
0283 /*! shows connection test dialog with a progress bar indicating connection testing
0284  (within a second thread).
0285  @a data is used to perform a (temporary) test connection. @a msgHandler is used to display errors.
0286  On successful connecting, a message is displayed. After testing, temporary connection is closed. */
0287 KDB_EXPORT void connectionTestDialog(QWidget* parent, const KDbConnectionData& data,
0288         KDbMessageHandler* msgHandler);
0289 
0290 //! Used in splitToTableAndFieldParts().
0291 enum SplitToTableAndFieldPartsOptions {
0292     FailIfNoTableOrFieldName = 0, //!< default value for splitToTableAndFieldParts()
0293     SetFieldNameIfNoTableName = 1 //!< see splitToTableAndFieldParts()
0294 };
0295 
0296 /*! Splits @a string like "table.field" into "table" and "field" parts.
0297  On success, a table name is passed to @a tableName and a field name is passed to @a fieldName.
0298  The function fails if either:
0299  - @a string is empty, or
0300  - @a string does not contain '.' character and @a option is FailIfNoTableOrFieldName
0301     (the default), or
0302  - '.' character is the first of last character of @a string (in this case table name
0303    or field name could become empty what is not allowed).
0304 
0305  If @a option is SetFieldNameIfNoTableName and @a string does not contain '.',
0306  @a string is passed to @a fieldName and @a tableName is set to QString()
0307  without failure.
0308 
0309  If function fails, @a tableName and @a fieldName remain unchanged.
0310  @return true on success. */
0311 KDB_EXPORT bool splitToTableAndFieldParts(const QString& string,
0312         QString *tableName, QString *fieldName,
0313         SplitToTableAndFieldPartsOptions option = FailIfNoTableOrFieldName);
0314 
0315 /*! @return true if @a type supports "visibleDecimalPlaces" property. */
0316 KDB_EXPORT bool supportsVisibleDecimalPlacesProperty(KDbField::Type type);
0317 
0318 //*! @return string constructed by converting @a value.
0319 * If @a decimalPlaces is < 0, all meaningful fractional digits are returned (up to 10).
0320 * If @a automatically is 0, just integer part is returned.
0321 * If @a automatically is > 0, fractional part should take exactly
0322   N digits: if the fractional part is shorter than N, additional zeros are appended.
0323   Examples:
0324   * numberToString(12.345, 6) == "12.345000"
0325   * numberToString(12.345, 0) == "12"
0326   * numberToString(12.345, -1) == "12.345"
0327   * numberToString(12.0, -1) == "12"
0328   * numberToString(0.0, -1) == "0"
0329 
0330 @note No rounding is performed
0331 @note No thousands group separator is used. Decimal symbol is '.'.
0332 
0333 @see KDb::numberToLocaleString() KDbField::visibleDecimalPlaces() */
0334 KDB_EXPORT QString numberToString(double value, int decimalPlaces);
0335 
0336 /*! Like KDb::numberToString() but formats the string using locale.toString().
0337 If @a locale if @c nullptr, desault QLocale is used.
0338 
0339 @see KDb::numberToString() KDbField::visibleDecimalPlaces() */
0340 KDB_EXPORT QString numberToLocaleString(double value, int decimalPlaces, const QLocale *locale = nullptr);
0341 
0342 //! @return true if @a propertyName is a builtin field property.
0343 KDB_EXPORT bool isBuiltinTableFieldProperty(const QByteArray& propertyName);
0344 
0345 //! @return true if @a propertyName is an extended field property.
0346 KDB_EXPORT bool isExtendedTableFieldProperty(const QByteArray& propertyName);
0347 
0348 //! @return true if @a propertyName is belongs to lookup field's schema.
0349 KDB_EXPORT bool isLookupFieldSchemaProperty(const QByteArray& propertyName);
0350 
0351 /*! @return type of field for integer value @a type.
0352  If @a type cannot be casted to KDbField::Type, KDbField::InvalidType is returned.
0353  This can be used when type information is deserialized from a string or QVariant. */
0354 KDB_EXPORT KDbField::Type intToFieldType(int type);
0355 
0356 /*! @return type group of field for integer value @a typeGroup.
0357  If @a typeGroup cannot be casted to KDbField::TypeGroup, KDbField::InvalidGroup is returned.
0358  This can be used when type information is deserialized from a string or QVariant. */
0359 KDB_EXPORT KDbField::TypeGroup intToFieldTypeGroup(int typeGroup);
0360 
0361 /*! Gets property values for the lookup schema @a lookup.
0362  @a values is cleared before filling. This function is used e.g. for altering table design. */
0363 KDB_EXPORT void getProperties(const KDbLookupFieldSchema *lookup, QMap<QByteArray, QVariant> *values);
0364 
0365 /*! Gets property values for @a field.
0366  Properties from extended schema are included. @a values is cleared before filling.
0367  The same number of properties in the same order is returned.
0368  This function is used e.g. for altering table design.
0369  */
0370 KDB_EXPORT void getFieldProperties(const KDbField &field, QMap<QByteArray, QVariant> *values);
0371 
0372 /*! Sets property values for @a field. @return true if all the values are valid and allowed.
0373  On failure contents of @a field is undefined.
0374  Properties from extended schema are also supported.
0375  This function is used e.g. by KDbAlterTableHandler when property information comes in form of text.
0376  */
0377 KDB_EXPORT bool setFieldProperties(KDbField *field, const QMap<QByteArray, QVariant>& values);
0378 
0379 /*! Sets property value for @a field. @return true if the property has been found and
0380  the value is valid for this property. On failure contents of @a field is undefined.
0381  Properties from extended schema are also supported as well as
0382    QVariant customProperty(const QString& propertyName) const;
0383 
0384  This function is used e.g. by KDbAlterTableHandler when property information comes in form of text.
0385  */
0386 KDB_EXPORT bool setFieldProperty(KDbField *field, const QByteArray& propertyName,
0387                                        const QVariant& value);
0388 
0389 /*! @return property value loaded from a DOM @a node, written in a QtDesigner-like
0390  notation: &lt;number&gt;int&lt;/number&gt; or &lt;bool&gt;bool&lt;/bool&gt;, etc. Supported types are
0391  "string", "cstring", "bool", "number". For invalid values null QVariant is returned.
0392  You can check the validity of the returned value using QVariant::type(). */
0393 KDB_EXPORT QVariant loadPropertyValueFromDom(const QDomNode& node, bool *ok);
0394 
0395 /*! Convenience version of loadPropertyValueFromDom(). @return int value. */
0396 KDB_EXPORT int loadIntPropertyValueFromDom(const QDomNode& node, bool* ok);
0397 
0398 /*! Convenience version of loadPropertyValueFromDom(). @return QString value. */
0399 KDB_EXPORT QString loadStringPropertyValueFromDom(const QDomNode& node, bool* ok);
0400 
0401 /*! Saves integer element for value @a value to @a doc document within parent element
0402  @a parentEl. The value will be enclosed in "number" element and "elementName" element.
0403  Example: saveNumberElementToDom(doc, parentEl, "height", 15) will create
0404  @code
0405   <height><number>15</number></height>
0406  @endcode
0407  @return the reference to element created with tag elementName. */
0408 KDB_EXPORT QDomElement saveNumberElementToDom(QDomDocument *doc, QDomElement *parentEl,
0409         const QString& elementName, int value);
0410 
0411 /*! Saves boolean element for value @a value to @a doc document within parent element
0412  @a parentEl. Like saveNumberElementToDom() but creates "bool" tags. True/false values will be
0413  saved as "true"/"false" strings.
0414  @return the reference to element created with tag elementName. */
0415 KDB_EXPORT QDomElement saveBooleanElementToDom(QDomDocument *doc, QDomElement *parentEl,
0416         const QString& elementName, bool value);
0417 
0418 //! @return equivalent of empty (default) value that can be set for a database field of type @a type
0419 /*! In particular returns:
0420  - empty string for text types,
0421  - 0 for integer and floating-point types,
0422  - false for boolean types,
0423  - a null byte array for BLOB type,
0424  - current date, time, date+time is returned (measured at client side) for date, time and
0425    date/time types respectively,
0426  - a null QVariant for unsupported values such as KDbField::InvalidType. */
0427 KDB_EXPORT QVariant emptyValueForFieldType(KDbField::Type type);
0428 
0429 //! @return a value that can be set for a database field of type @a type having "notEmpty" property set.
0430 /*! It works in a similar way as @ref QVariant KDb::emptyValueForFieldType(KDbField::Type type)
0431  with the following differences:
0432  - " " string (a single space) is returned for Text and LongText types
0433  - a byte array with saved "filenew" PNG image (icon) for BLOB type
0434  Returns null QVariant for unsupported values like KDbField::InvalidType. */
0435 KDB_EXPORT QVariant notEmptyValueForFieldType(KDbField::Type type);
0436 
0437 /*! @return true if the @a word is an reserved KDbSQL keyword
0438  See src/generated/sqlkeywords.cpp in the KDb source code.
0439  @todo add function returning list of keywords. */
0440 KDB_EXPORT bool isKDbSqlKeyword(const QByteArray& word);
0441 
0442 //! @return @a string string with applied KDbSQL identifier escaping
0443 /*! This escaping can be used for field, table, database names, etc.
0444     Use it for user-visible backend-independent statements.
0445     @see KDb::escapeIdentifierAndAddQuotes() */
0446 KDB_EXPORT QString escapeIdentifier(const QString& string);
0447 
0448 //! @overload QString escapeIdentifier(const QString&)
0449 KDB_EXPORT QByteArray escapeIdentifier(const QByteArray& string);
0450 
0451 //! @return @a string string with applied KDbSQL identifier escaping and enclosed in " quotes
0452 /*! This escaping can be used for field, table, database names, etc.
0453     Use it for user-visible backend-independent statements.
0454     @see KDb::escapeIdentifier */
0455 KDB_EXPORT QString escapeIdentifierAndAddQuotes(const QString& string);
0456 
0457 //! @overload QString escapeIdentifierAndAddQuotes(const QString&)
0458 KDB_EXPORT QByteArray escapeIdentifierAndAddQuotes(const QByteArray& string);
0459 
0460 /*! @return escaped string @a string w using KDbSQL dialect,
0461             i.e. doubles single quotes ("'") and inserts the string into single quotes.
0462     Quotes "'" are prepended and appended.
0463     Also escapes \\n, \\r, \\t, \\\\, \\0.
0464     Use it for user-visible backend-independent statements. */
0465 KDB_EXPORT QString escapeString(const QString& string);
0466 
0467 /**
0468  * @brief Returns escaped string @a string
0469  *
0470  * If @a drv driver is present, it is used to perform escaping, otherwise escapeString() is used
0471  * so the KDbSQL dialect-escaping is performed.
0472  */
0473 KDB_EXPORT KDbEscapedString escapeString(KDbDriver *drv, const QString& string);
0474 
0475 /**
0476  * @brief Returns escaped string @a string
0477  *
0478  * If @a conn is present, its driver is used to perform escaping, otherwise escapeString() is used
0479  * so the KDbSQL dialect-escaping is performed.
0480  */
0481 KDB_EXPORT KDbEscapedString escapeString(KDbConnection *conn, const QString& string);
0482 #endif
0483 
0484 void KDbTest::testUnescapeString_data()
0485 {
0486     QTest::addColumn<QString>("sequence");
0487     QTest::addColumn<QString>("result");
0488     QTest::addColumn<char>("quote"); // can be ' or ", if 0 then both variants are checked
0489     QTest::addColumn<int>("errorPosition");
0490     QTest::addColumn<int>("errorPositionWhenAppended");
0491 
0492     // quote-independent cases, success
0493 #define T2(tag, sequence, result, quote) QTest::newRow(tag) << QString::fromUtf8(sequence) \
0494             << QString::fromUtf8(result) << quote << -1 << -1
0495 #define T(tag, sequence, result) T2(tag, sequence, result, '\0')
0496     QTest::newRow("null") << QString() << QString() << '\0' << -1 << -1;
0497     QTest::newRow("\\0") << QString("\\0") << QString(QLatin1Char('\0')) << '\0' << -1 << -1;
0498     const char *s = " String without escaping %_? 𝌆 ©";
0499     T("without escaping", s, s);
0500     T("empty", "", "");
0501     T("\\'", "\\'", "'");
0502     T("\\\"", "\\\"", "\"");
0503     T("\\\\", "\\\\", "\\");
0504     T("\\b", "\\b", "\b");
0505     T("\\f", "\\f", "\f");
0506     T("\\n", "\\n", "\n");
0507     T("\\r", "\\r", "\r");
0508     T("\\t", "\\t", "\t");
0509     T("\\v", "\\v", "\v");
0510     T("_\\_", "_\\_", "__");
0511     T("?\\?", "?\\?", "??");
0512     T("%\\%", "%\\%", "%%");
0513     T("ignored \\ in \\a", "\\a", "a");
0514     T("ignored \\ in \\♥", "\\♥ ", "♥ ");
0515     T("ignored \\ in 𝌆\\\\\\a", "𝌆\\\\\\a", "𝌆\\a");
0516     T("unfinished \\", "\\", "");
0517     T("unfinished \\ 2", "one two\\", "one two");
0518     T("\\xA9", "\\xA9", "©");
0519     T("\\xa9\\xa9", "\\xa9\\xa9", "©©");
0520     QTest::newRow("\\x00") << QString("\\x00") << QString(QLatin1Char('\0')) << '\0' << -1 << -1;
0521     QTest::newRow("\\u0000") << QString("\\u0000") << QString(QChar(static_cast<unsigned short>(0)))
0522                              << '\0' << -1 << -1;
0523     T("\\u2665", "\\u2665", "♥");
0524 #ifndef _MSC_VER // does not work with MSVC: "warning C4566: character represented
0525                  // by universal-character-name cannot be represented in the current code page"
0526     T("\\xff", "\\xff", "\u00ff");
0527     T("\\uffff", "\\uffff", "\uffff");
0528 #endif
0529     QTest::newRow("\\u{0}") << QString("\\u{0}") << QString(QLatin1Char('\0')) << '\0' << -1 << -1;
0530     QTest::newRow("\\u{0000000000}") << QString("\\u{0000000000}")
0531                                      << QString(QLatin1Char('\0')) << '\0' << -1 << -1;
0532     T("\\u{A9}", "\\u{A9}", "©");
0533     T("\\u{a9}", "\\u{a9}", "©");
0534     T("\\u{0a9}", "\\u{0a9}", "©");
0535     T("\\u{00a9}", "\\u{00a9}", "©");
0536     T("\\u{2665}", "\\u{2665}", "♥");
0537     T("\\u{02665}", "\\u{02665}", "♥");
0538     QTest::newRow("\\u{1D306}") << QString("\\u{1D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1;
0539     QTest::newRow("\\u{1d306}") << QString("\\u{1d306}") << QString(QChar(0x1d306)) << '\0' << -1 << -1;
0540     QTest::newRow("\\u{01D306}") << QString("\\u{01D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1;
0541     QTest::newRow("\\u{01d306}") << QString("\\u{01d306}") << QString(QChar(0x1d306)) << '\0' << -1 << -1;
0542     QTest::newRow("\\u{00001D306}") << QString("\\u{00001D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1;
0543     QTest::newRow("\\u{10FFFF}") << QString("\\u{10FFFF}") << QString(QChar(0x10FFFF)) << '\0' << -1 << -1;
0544 
0545     // quote-dependent cases, success
0546     T2("2x ' for ' quote", "''", "'", '\'');
0547     T2("4x ' for ' quote", "''''", "''", '\'');
0548     T2("2x \" for ' quote", "\"\"", "\"\"", '\'');
0549     T2("3x \" for ' quote", "\"\"\"", "\"\"\"", '\'');
0550     T2("2x ' for \" quote", "''", "''", '"');
0551     T2("3x ' for \" quote", "'''", "'''", '"');
0552     T2("2x \" for \" quote", "\"\"", "\"", '"');
0553     T2("4x \" for \" quote", "\"\"\"\"", "\"\"", '"');
0554 #undef T
0555 #undef T2
0556     // failures
0557     QTest::newRow("invalid quote") << QString::fromUtf8("abc") << QString() << 'x' << 0 << 0;
0558 #define T(tag, sequence, quote, errorPosition, errorPositionWhenAppended) \
0559         QTest::newRow(tag) << QString::fromUtf8(sequence) << QString() << quote \
0560                            << errorPosition << errorPositionWhenAppended
0561     T("missing ' quote", "'", '\'', 0, 0);
0562     T("missing \" quote", "\"", '"', 0, 0);
0563     T("invalid \\x", "\\x", '\0', 1, 2);
0564     T("invalid \\xQ", "\\xQ", '\0', 2, 2);
0565     T("invalid \\xQt", "\\xQt", '\0', 2, 2);
0566     T("invalid \\xAQ", "\\xAQ", '\0', 3, 3);
0567     T("invalid \\u", "\\u", '\0', 1, 2);
0568     T("invalid \\ua", "\\ua", '\0', 2, 3);
0569     T("invalid \\u40", "\\u40", '\0', 3, 4);
0570     T("invalid \\u405", "\\u405", '\0', 4, 5);
0571     T("invalid \\uQ", "\\uQ", '\0', 2, 2);
0572     T("invalid \\uQt", "\\uQt", '\0', 2, 2);
0573     T("invalid \\uQt5", "\\uQt5", '\0', 2, 2);
0574     T("invalid \\uQt57", "\\uQt57", '\0', 2, 2);
0575     T("invalid \\uaQ", "\\uaQ", '\0', 3, 3);
0576     T("invalid \\uabQ", "\\uabQ", '\0', 4, 4);
0577     T("invalid \\uabcQ", "\\uabcQ", '\0', 5, 5);
0578     T("invalid \\u{", "\\u{", '\0', 2, 3);
0579     T("invalid \\u{26", "\\u{26", '\0', 4, 5);
0580     T("invalid \\u{266", "\\u{266", '\0', 5, 6);
0581     T("invalid \\u{2665", "\\u{2665", '\0', 6, 7);
0582     T("invalid \\u{2665a", "\\u{2665a", '\0', 7, 8);
0583     T("invalid \\u{}", "\\u{}", '\0', 3, 3);
0584     T("invalid \\u{Q}", "\\u{Q}", '\0', 3, 3);
0585     T("invalid \\u{Qt}", "\\u{Qt}", '\0', 3, 3);
0586     T("invalid \\u{Qt5}", "\\u{Qt5}", '\0', 3, 3);
0587     T("invalid \\u{Qt57}", "\\u{Qt57}", '\0', 3, 3);
0588     T("invalid \\u{Qt57", "\\u{Qt57", '\0', 3, 3);
0589     T("invalid \\u{aQ}", "\\u{aQ}", '\0', 4, 4);
0590     T("invalid \\u{abQ}", "\\u{abQ}", '\0', 5, 5);
0591     T("invalid \\u{abcQ}", "\\u{abcQ}", '\0', 6, 6);
0592     T("invalid \\u{abcdQ}", "\\u{abcdQ}", '\0', 7, 7);
0593     T("invalid \\u{abcdQ}", "\\u{abcdQ}", '\0', 7, 7);
0594     T("invalid \\u{abcdfQ}", "\\u{abcdfQ}", '\0', 8, 8);
0595     T("invalid too large \\u{110000}", "\\u{110000}", '\0', 8, 8);
0596     T("invalid too large \\u{1100000}", "\\u{1100000}", '\0', 8, 8);
0597     T("invalid too large \\u{00110000}", "\\u{00110000}", '\0', 10, 10);
0598 }
0599 
0600 void KDbTest::testUnescapeStringHelper(const QString &sequenceString, const QString &resultString_,
0601                                        char quote, int errorPosition, int offset)
0602 {
0603     int actualErrorPosition = -2;
0604     QString resultString(resultString_);
0605     if (errorPosition >= 0) {
0606         errorPosition += offset;
0607         resultString.clear();
0608     }
0609     //qDebug() << KDb::unescapeString("\\0bar", '\'', &errorPosition);
0610 
0611 #define COMPARE(x, y) \
0612     if (x != y) { \
0613         qDebug() << "sequenceString:" << sequenceString << "resultString:" << resultString; \
0614     } \
0615     QCOMPARE(x, y)
0616 
0617     if (quote == 0) { // both cases
0618         COMPARE(KDb::unescapeString(sequenceString, '\'', &actualErrorPosition), resultString);
0619         COMPARE(actualErrorPosition, errorPosition);
0620         COMPARE(KDb::unescapeString(sequenceString, '\'', nullptr), resultString);
0621 
0622         COMPARE(KDb::unescapeString(sequenceString, '"', &actualErrorPosition), resultString);
0623         COMPARE(actualErrorPosition, errorPosition);
0624         COMPARE(KDb::unescapeString(sequenceString, '"', nullptr), resultString);
0625     } else {
0626         if (quote != '\'' && quote != '"') {
0627             resultString.clear();
0628             errorPosition = 0;
0629         }
0630         COMPARE(KDb::unescapeString(sequenceString, quote, &actualErrorPosition), resultString);
0631         COMPARE(actualErrorPosition, errorPosition);
0632         COMPARE(KDb::unescapeString(sequenceString, quote, nullptr), resultString);
0633     }
0634 #undef CHECK_POS
0635 }
0636 
0637 void KDbTest::testUnescapeString()
0638 {
0639     QFETCH(QString, sequence);
0640     QFETCH(QString, result);
0641     QFETCH(char, quote);
0642     QFETCH(int, errorPosition);
0643     QFETCH(int, errorPositionWhenAppended);
0644     testUnescapeStringHelper(sequence, result, quote, errorPosition, 0);
0645     testUnescapeStringHelper("foo" + sequence, "foo" + result, quote, errorPosition, 3);
0646     testUnescapeStringHelper(sequence + " bar", result + " bar", quote, errorPositionWhenAppended,
0647                              0);
0648     testUnescapeStringHelper("foo" + sequence + " bar", "foo" + result + " bar",
0649                              quote, errorPositionWhenAppended, 3);
0650 }
0651 
0652 void KDbTest::testEscapeBLOB_data()
0653 {
0654     QTest::addColumn<QByteArray>("blob");
0655     QTest::addColumn<QString>("escapedX");
0656     QTest::addColumn<QString>("escaped0x");
0657     QTest::addColumn<QString>("escapedHex");
0658     QTest::addColumn<QString>("escapedOctal");
0659     QTest::addColumn<QString>("escapedBytea");
0660 
0661     QTest::newRow("") << QByteArray()
0662         << QString("X''") << QString() << QString("") << QString("''") << QString("E'\\\\x'::bytea");
0663     QTest::newRow("0,1,k") << QByteArray("\0\1k", 3)
0664         << QString("X'00016B'") << QString("0x00016B") << QString("00016B") << QString("'\\\\000\\\\001k'") << QString("E'\\\\x00016B'::bytea");
0665     QTest::newRow("ABC\\\\0") << QByteArray("ABC\0", 4)
0666         << QString("X'41424300'") << QString("0x41424300") << QString("41424300") << QString("'ABC\\\\000'") << QString("E'\\\\x41424300'::bytea");
0667     QTest::newRow("'") << QByteArray("'")
0668         << QString("X'27'") << QString("0x27") << QString("27") << QString("'\\\\047'") << QString("E'\\\\x27'::bytea");
0669     QTest::newRow("\\") << QByteArray("\\")
0670         << QString("X'5C'") << QString("0x5C") << QString("5C") << QString("'\\\\134'") << QString("E'\\\\x5C'::bytea");
0671 }
0672 
0673 void KDbTest::testEscapeBLOB()
0674 {
0675     QFETCH(QByteArray, blob);
0676     QFETCH(QString, escapedX);
0677     QFETCH(QString, escaped0x);
0678     QFETCH(QString, escapedHex);
0679     QFETCH(QString, escapedOctal);
0680     QFETCH(QString, escapedBytea);
0681 
0682     QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::XHex), escapedX);
0683     QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::ZeroXHex), escaped0x);
0684     QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::Hex), escapedHex);
0685     QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::Octal), escapedOctal);
0686     QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::ByteaHex), escapedBytea);
0687 }
0688 
0689 void KDbTest::testPgsqlByteaToByteArray()
0690 {
0691     QCOMPARE(KDb::pgsqlByteaToByteArray(nullptr, 0), QByteArray());
0692     QCOMPARE(KDb::pgsqlByteaToByteArray("", 0), QByteArray());
0693     QCOMPARE(KDb::pgsqlByteaToByteArray(" ", 0), QByteArray());
0694     QCOMPARE(KDb::pgsqlByteaToByteArray("\\101"), QByteArray("A"));
0695     QCOMPARE(KDb::pgsqlByteaToByteArray("\\101", 4), QByteArray("A"));
0696     QCOMPARE(KDb::pgsqlByteaToByteArray("\\101B", 4), QByteArray("A")); // cut-off at #4
0697     QCOMPARE(KDb::pgsqlByteaToByteArray("\\'\\\\\\'"), QByteArray("\'\\\'"));
0698     QCOMPARE(KDb::pgsqlByteaToByteArray("\\\\a\\377bc\\'d\"\n"), QByteArray("\\a\377bc\'d\"\n"));
0699 }
0700 
0701 void KDbTest::testXHexToByteArray_data()
0702 {
0703     QTest::addColumn<QByteArray>("data");
0704     QTest::addColumn<int>("length"); // -2 means "compute length", other values: pass it as is
0705     QTest::addColumn<bool>("ok");
0706     QTest::addColumn<QByteArray>("result");
0707 
0708     QTest::newRow("") << QByteArray() << 0 << false << QByteArray();
0709     QTest::newRow("bad prefix") << QByteArray("bad") << -2 << false << QByteArray();
0710     QTest::newRow("X") << QByteArray("X") << -2 << false << QByteArray();
0711     QTest::newRow("X'") << QByteArray("X'") << -2 << false << QByteArray();
0712     QTest::newRow("X''") << QByteArray("X''") << -2 << true << QByteArray();
0713     QTest::newRow("X'1") << QByteArray("X'1") << -2 << false << QByteArray();
0714     QTest::newRow("X'1' cut") << QByteArray("X'1'") << 3 << false << QByteArray();
0715     QTest::newRow("X'1'") << QByteArray("X'1'") << -2 << true << QByteArray("\1");
0716     QTest::newRow("X'0'") << QByteArray("X'0'") << -2 << true << QByteArray("\0", 1);
0717     QTest::newRow("X'000'") << QByteArray("X'000'") << -2 << true << QByteArray("\0\0", 2);
0718     QTest::newRow("X'01'") << QByteArray("X'01'") << -2 << true << QByteArray("\1");
0719     QTest::newRow("X'FeAb2C'") << QByteArray("X'FeAb2C'") << -2 << true << QByteArray("\376\253\54");
0720 }
0721 
0722 void KDbTest::testXHexToByteArray()
0723 {
0724     QFETCH(QByteArray, data);
0725     QFETCH(int, length);
0726     QFETCH(bool, ok);
0727     QFETCH(QByteArray, result);
0728 
0729     bool actualOk;
0730     QCOMPARE(KDb::xHexToByteArray(data.constData(), length == -1 ? data.length() : length, &actualOk), result);
0731     QCOMPARE(actualOk, ok);
0732     QCOMPARE(KDb::xHexToByteArray(data.constData(), length, nullptr), result);
0733 }
0734 
0735 void KDbTest::testZeroXHexToByteArray_data()
0736 {
0737     QTest::addColumn<QByteArray>("data");
0738     QTest::addColumn<int>("length"); // -2 means "compute length", other values: pass it as is
0739     QTest::addColumn<bool>("ok");
0740     QTest::addColumn<QByteArray>("result");
0741 
0742     QTest::newRow("") << QByteArray() << 0 << false << QByteArray();
0743     QTest::newRow("0") << QByteArray("0") << -2 << false << QByteArray();
0744     QTest::newRow("0x") << QByteArray("0x") << -2 << false << QByteArray();
0745     QTest::newRow("0X22") << QByteArray("0X22") << -2 << false << QByteArray();
0746     QTest::newRow("bad prefix") << QByteArray("bad") << -2 << false << QByteArray();
0747     QTest::newRow("0x0") << QByteArray("0x0") << -2 << true << QByteArray("\0", 1);
0748     QTest::newRow("0x0 cut") << QByteArray("0x0") << 2 << false << QByteArray();
0749     QTest::newRow("0X0") << QByteArray("0X0") << -2 << false << QByteArray();
0750     QTest::newRow("0x0123") << QByteArray("0x0123") << -2 << true << QByteArray("\1\43");
0751     QTest::newRow("0x0123 cut") << QByteArray("0x0123") << 4 << true << QByteArray("\1");
0752     QTest::newRow("0x00000'") << QByteArray("0x00000") << -2 << true << QByteArray("\0\0\0", 3);
0753     QTest::newRow("0xFeAb2C") << QByteArray("0xFeAb2C") << -2 << true << QByteArray("\376\253\54");
0754 }
0755 
0756 void KDbTest::testZeroXHexToByteArray()
0757 {
0758     QFETCH(QByteArray, data);
0759     QFETCH(int, length);
0760     QFETCH(bool, ok);
0761     QFETCH(QByteArray, result);
0762 
0763     bool actualOk;
0764     QCOMPARE(KDb::zeroXHexToByteArray(data.constData(), length == -1 ? data.length() : length, &actualOk), result);
0765     QCOMPARE(actualOk, ok);
0766     QCOMPARE(KDb::zeroXHexToByteArray(data.constData(), length, nullptr), result);
0767 }
0768 
0769 //! @todo add tests
0770 #if 0
0771 /*! @return int list converted from string list.
0772    If @a ok is not 0, *ok is set to result of the conversion. */
0773 KDB_EXPORT QList<int> stringListToIntList(const QStringList &list, bool *ok);
0774 
0775 /*! @return string converted from list @a list.
0776    Separators are ',' characters, "," and "\\" are escaped.
0777     @see KDb::deserializeList() */
0778 KDB_EXPORT QString serializeList(const QStringList &list);
0779 
0780 /*! @return string list converted from @a data which was built using serializeList().
0781    Separators are ',' characters, escaping is assumed as "\\,". */
0782 KDB_EXPORT QStringList deserializeList(const QString &data);
0783 
0784 /*! @return int list converted from @a data which was built using serializeList().
0785    Separators are ',' characters, escaping is assumed as "\\,".
0786    If @a ok is not 0, *ok is set to result of the conversion.
0787    @see KDb::stringListToIntList() */
0788 KDB_EXPORT QList<int> deserializeIntList(const QString &data, bool *ok);
0789 
0790 /*! @return string value serialized from a variant value @a v.
0791  This functions works like QVariant::toString() except the case when @a v is of type:
0792  - QByteArray - in this case KDb::escapeBLOB(v.toByteArray(), KDb::BLOBEscapeHex) is used.
0793  - QStringList - in this case KDb::serializeList(v.toStringList()) is used.
0794 
0795  This function is needed for handling values of random type, for example "defaultValue"
0796  property of table fields can contain value of any type.
0797  Note: the returned string is an unescaped string. */
0798 KDB_EXPORT QString variantToString(const QVariant& v);
0799 
0800 /*! @return variant value of type @a type for a string @a s that was previously serialized using
0801  @ref variantToString( const QVariant& v ) function.
0802  @a ok is set to the result of the operation. With exception for types mentioned in documentation
0803  of variantToString(), QVariant::convert() is used for conversion. */
0804 KDB_EXPORT QVariant stringToVariant(const QString& s, QVariant::Type type, bool* ok);
0805 
0806 /*! @return true if setting default value for @a field field is allowed. Fields with unique
0807  (and thus primary key) flags set do not accept  default values. */
0808 KDB_EXPORT bool isDefaultValueAllowed(const KDbField &field);
0809 
0810 //! Provides limits for values of type @a type
0811 /*! The result is put into integers pointed by @a minValue and @a maxValue.
0812  The limits are machine-independent,. what is useful for format and protocol compatibility.
0813  Supported types are Byte, ShortInteger, Integer and BigInteger.
0814  The value of @a signedness controls the values; they can be limited to unsigned or not.
0815  Results for BigInteger or non-integer types are the same as for Integer due to limitation
0816  of int type. Signed integers are assumed. @a minValue and @a maxValue must not be 0. */
0817 KDB_EXPORT void getLimitsForFieldType(KDbField::Type type, qlonglong *minValue, qlonglong *maxValue,
0818                                       KDb::Signedness signedness = KDb::Signed);
0819 
0820 /*! @return type that's maximum of two integer types @a t1 and @a t2, e.g. Integer for (Byte, Integer).
0821  If one of the types is not of the integer group, KDbField::InvalidType is returned.
0822  Returned type may not fit to the result of evaluated expression that involves the arguments.
0823  For example, 100 is within Byte type, maximumForIntegerFieldTypes(Byte, Byte) is Byte but result
0824  of 100 * 100 exceeds the range of Byte. */
0825 KDB_EXPORT KDbField::Type maximumForIntegerFieldTypes(KDbField::Type t1, KDbField::Type t2);
0826 #endif
0827 
0828 void KDbTest::testCstringToVariant_data()
0829 {
0830     QTest::addColumn<QString>("data"); // QString() -> 0, QString("") -> empty string ""
0831     QTest::addColumn<KDbField::Type>("type");
0832     QTest::addColumn<int>("length");
0833     QTest::addColumn<QVariant>("variant");
0834     QTest::addColumn<KDb::Signedness>("signedness");
0835     QTest::addColumn<bool>("okResult");
0836 
0837     int c = 0;
0838     ++c;
0839     QTest::newRow("invalid1") << QString() << KDbField::InvalidType << -1 << QVariant() << KDb::Signed << false;
0840     QTest::newRow("invalid2") << "" << KDbField::InvalidType << -1 << QVariant() << KDb::Signed << false;
0841     QTest::newRow("invalid3") << "abc" << KDbField::InvalidType << 3 << QVariant() << KDb::Signed << false;
0842     ++c;
0843     QTest::newRow("byte1") << "0" << KDbField::Byte << 1 << QVariant(0) << KDb::Signed << true;
0844     QTest::newRow("ubyte1") << "0" << KDbField::Byte << 1 << QVariant(0) << KDb::Unsigned << true;
0845     QTest::newRow("byte2") << "42" << KDbField::Byte << -1 << QVariant(42) << KDb::Signed << true;
0846     QTest::newRow("ubyte2") << "42" << KDbField::Byte << -1 << QVariant(42) << KDb::Unsigned << true;
0847     QTest::newRow("byte3") << "129" << KDbField::Byte << -1 << QVariant() << KDb::Signed << false;
0848     QTest::newRow("ubyte3") << "129" << KDbField::Byte << -1 << QVariant(129) << KDb::Unsigned << true;
0849     QTest::newRow("byte4") << "-128" << KDbField::Byte << -1 << QVariant(-128) << KDb::Signed << true;
0850     QTest::newRow("ubyte4") << "-128" << KDbField::Byte << -1 << QVariant() << KDb::Unsigned << false;
0851     ++c;
0852     QTest::newRow("short1") << "-123" << KDbField::ShortInteger << -1 << QVariant(-123) << KDb::Signed << true;
0853     QTest::newRow("short2") << "942" << KDbField::ShortInteger << -1 << QVariant(942) << KDb::Signed << true;
0854     QTest::newRow("short3") << "32767" << KDbField::ShortInteger << -1 << QVariant(32767) << KDb::Signed << true;
0855     QTest::newRow("short4") << "32768" << KDbField::ShortInteger << -1 << QVariant() << KDb::Signed << false;
0856     QTest::newRow("ushort4") << "32768" << KDbField::ShortInteger << -1 << QVariant(32768) << KDb::Unsigned << true;
0857     QTest::newRow("short5") << "-32768" << KDbField::ShortInteger << -1 << QVariant(-32768) << KDb::Signed << true;
0858     QTest::newRow("ushort5") << "-32768" << KDbField::ShortInteger << -1 << QVariant() << KDb::Unsigned << false;
0859     ++c;
0860     QTest::newRow("int1") << QString::number(0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(0x07FFFFFFF) << KDb::Signed << true;
0861     QTest::newRow("uint1") << QString::number(0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(0x07FFFFFFF) << KDb::Unsigned << true;
0862     QTest::newRow("int2") << QString::number(-0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(-0x07FFFFFFF) << KDb::Signed << true;
0863     QTest::newRow("uint2") << QString::number(-0x07FFFFFFF) << KDbField::Integer << -1 << QVariant() << KDb::Unsigned << false;
0864     QTest::newRow("int3") << QString::number(std::numeric_limits<qlonglong>::min()) << KDbField::Integer << -1 << QVariant() << KDb::Signed << false;
0865     QTest::newRow("uint4") << "-1" << KDbField::Integer << -1 << QVariant() << KDb::Unsigned << false;
0866     QTest::newRow("int4") << "-1" << KDbField::Integer << -1 << QVariant(-1) << KDb::Signed << true;
0867     //!< @todo cannot be larger?
0868     ++c;
0869     QTest::newRow("bigint1") << QString::number(0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(0x07FFFFFFF) << KDb::Signed << true;
0870     QTest::newRow("ubigint1") << QString::number(0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(0x07FFFFFFF) << KDb::Unsigned << true;
0871     QTest::newRow("bigint2") << QString::number(-0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(-0x07FFFFFFF) << KDb::Signed << true;
0872     QTest::newRow("ubigint2") << QString::number(-0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant() << KDb::Unsigned << false;
0873     QTest::newRow("bigint3") << QString::number(std::numeric_limits<qlonglong>::min()) << KDbField::BigInteger << -1 << QVariant() << KDb::Signed << false;
0874     QTest::newRow("ubigint4") << "-1" << KDbField::BigInteger << -1 << QVariant() << KDb::Unsigned << false;
0875     QTest::newRow("bigint4") << "-1" << KDbField::BigInteger << -1 << QVariant(-1) << KDb::Signed << true;
0876     ++c;
0877     QTest::newRow("bool0") << "0" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true;
0878     QTest::newRow("bool1") << "1" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true;
0879     QTest::newRow("bool-") << "-" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true;
0880     QTest::newRow("bool5") << "5" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true;
0881     QTest::newRow("bool false") << "false" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true;
0882     QTest::newRow("bool False") << "False" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true;
0883     QTest::newRow("bool TRUE") << "TRUE" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true;
0884     QTest::newRow("bool true") << "true" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true;
0885     QTest::newRow("bool no") << "no" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; // surprised? See docs for QVariant::toBool().
0886     ++c;
0887     //! @todo support Date
0888     ++c;
0889     //! @todo support DateTime
0890     ++c;
0891     //! @todo support Time
0892     ++c;
0893     //! @todo support Float
0894     ++c;
0895     //! @todo support Double
0896     ++c;
0897     //! @todo support Text
0898     ++c;
0899     //! @todo support LongText
0900     ++c;
0901     //! @todo support BLOB
0902 
0903     ++c; QTest::newRow("Null") << " " << KDbField::Null << -1 << QVariant() << KDb::Signed << false;
0904     ++c; QTest::newRow("Asterisk") << " " << KDbField::Asterisk << -1 << QVariant() << KDb::Signed << false;
0905     ++c; QTest::newRow("Enum") << " " << KDbField::Enum << -1 << QVariant() << KDb::Signed << false;
0906     ++c; QTest::newRow("Map") << " " << KDbField::Map << -1 << QVariant() << KDb::Signed << false;
0907     ++c; QTest::newRow("Tuple") << " " << KDbField::Tuple << -1 << QVariant() << KDb::Signed << false;
0908     QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount());
0909 }
0910 
0911 void KDbTest::testCstringToVariant()
0912 {
0913     QFETCH(QString, data);
0914     QFETCH(KDbField::Type, type);
0915     QFETCH(int, length);
0916     QFETCH(QVariant, variant);
0917     QFETCH(KDb::Signedness, signedness);
0918     QFETCH(bool, okResult);
0919     bool ok;
0920     const QByteArray ba(data.toUtf8()); // to avoid pointer to temp.
0921     const char *realData = ba.isNull() ? nullptr : ba.constData();
0922     QCOMPARE(KDb::cstringToVariant(realData, type, &ok, length, signedness), variant);
0923     QCOMPARE(ok, okResult);
0924     QCOMPARE(KDb::cstringToVariant(realData, type, nullptr, length, signedness), variant); // a case where ok == 0
0925     if (realData) {
0926         QCOMPARE(KDb::cstringToVariant(realData, type, &ok, data.length(), signedness), variant); // a case where length is set
0927         QCOMPARE(ok, okResult);
0928     }
0929     QCOMPARE(KDb::cstringToVariant(nullptr, type, &ok, length, signedness), QVariant()); // a case where data == 0 (NULL)
0930     QVERIFY(ok || type < KDbField::Byte || type > KDbField::LastType); // fails for NULL if this type isn't allowed
0931     if (type != KDbField::Boolean) {
0932         QCOMPARE(KDb::cstringToVariant(realData, type, &ok, 0, signedness), QVariant()); // a case where length == 0
0933         QVERIFY(!ok);
0934     }
0935     if (KDbField::isTextType(type)) { // a case where data == ""
0936         QCOMPARE(KDb::cstringToVariant("", type, &ok, length, signedness), QVariant(""));
0937         QVERIFY(ok);
0938     }
0939     else if (type != KDbField::Boolean) {
0940         QCOMPARE(KDb::cstringToVariant("", type, &ok, length, signedness), QVariant());
0941         QVERIFY(!ok);
0942     }
0943 }
0944 
0945 //! @todo add tests
0946 #if 0
0947 
0948 /*! @return default file-based driver MIME type
0949  (typically something like "application/x-kexiproject-sqlite") */
0950 KDB_EXPORT QString defaultFileBasedDriverMimeType();
0951 
0952 /*! @return default file-based driver ID (currently, "org.kde.kdb.sqlite"). */
0953 KDB_EXPORT QString defaultFileBasedDriverId();
0954 
0955 /*! Escapes and converts value @a v (for type @a ftype)
0956     to string representation required by KDbSQL commands.
0957     For Date/Time type KDb::dateTimeToSql() is used.
0958     For BLOB type KDb::escapeBlob() with BLOBEscapingType::ZeroXHex conversion type is used. */
0959 KDB_EXPORT KDbEscapedString valueToSql(KDbField::Type ftype, const QVariant& v);
0960 
0961 /*! Converts value @a v to string representation required by KDbSQL commands:
0962     ISO 8601 DateTime format - with "T" delimiter/
0963     For specification see https://www.w3.org/TR/NOTE-datetime.
0964     Example: "1994-11-05T13:15:30" not "1994-11-05 13:15:30".
0965     @todo Add support for time zones */
0966 KDB_EXPORT KDbEscapedString dateTimeToSql(const QDateTime& v);
0967 
0968 #ifdef KDB_DEBUG_GUI
0969 //! A prototype of handler for GUI debugger
0970 typedef void(*DebugGUIHandler)(const QString&);
0971 
0972 //! Sets handler for GUI debugger
0973 KDB_EXPORT void setDebugGUIHandler(DebugGUIHandler handler);
0974 
0975 //! Outputs string @a text to the GUI debugger
0976 KDB_EXPORT void debugGUI(const QString& text);
0977 
0978 //! A prototype of handler for GUI debugger (specialized for the Alter Table feature)
0979 typedef void(*AlterTableActionDebugGUIHandler)(const QString&, int);
0980 
0981 //! Sets handler for GUI debugger (specialized for the Alter Table feature)
0982 KDB_EXPORT void setAlterTableActionDebugHandler(AlterTableActionDebugGUIHandler handler);
0983 
0984 //! Outputs string @a text to the GUI debugger (specialized for the Alter Table feature);
0985 //! @a nestingLevel can be provided for nested outputs.
0986 KDB_EXPORT void alterTableActionDebugGUI(const QString& text, int nestingLevel = 0);
0987 #endif
0988 
0989 //! @return @a string if it is not empty, else returns @a stringIfEmpty.
0990 /*! This function is an optimization in cases when @a string is a result of expensive
0991  functioncall because any evaluation will be performed once, not twice. Another advantage
0992  is simpified code through the functional approach.
0993  The function expects bool isEmpty() method to be present in type T, so T can typically
0994  be QString or QByteArray. */
0995 template<typename T>
0996 T iifNotEmpty(const T &string, const T &stringIfEmpty)
0997 {
0998     return string.isEmpty() ? stringIfEmpty : string;
0999 }
1000 
1001 //! @overload iifNotEmpty(const T &string, const T &stringIfEmpty)
1002 template<typename T>
1003 T iifNotEmpty(const QByteArray &string, const T &stringIfEmpty)
1004 {
1005     return iifNotEmpty(QLatin1String(string), stringIfEmpty);
1006 }
1007 
1008 //! @overload iifNotEmpty(const T &string, const T &stringIfEmpty)
1009 template<typename T>
1010 T iifNotEmpty(const T &string, const QByteArray &stringIfEmpty)
1011 {
1012     return iifNotEmpty(string, QLatin1String(stringIfEmpty));
1013 }
1014 
1015 //! @return @a value if @a ok is true, else returns default value T().
1016 template<typename T>
1017 T iif(bool ok, const T &value)
1018 {
1019     if (ok) {
1020         return value;
1021     }
1022     return T();
1023 }
1024 
1025 /*! @return a list of paths that KDb will search when dynamically loading libraries (plugins)
1026  This is basicaly list of directories returned QCoreApplication::libraryPaths() that have readable
1027  subdirectory "kdb".
1028  @see QCoreApplication::libraryPaths() */
1029 KDB_EXPORT QStringList libraryPaths();
1030 #endif
1031 
1032 void KDbTest::testTemporaryTableName()
1033 {
1034     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1035 
1036     QString baseName = QLatin1String("foobar");
1037     QString tempName1 = KDb::temporaryTableName(utils.connection(), baseName);
1038     QVERIFY(!tempName1.isEmpty());
1039     QVERIFY(tempName1.contains(baseName));
1040     QString tempName2 = KDb::temporaryTableName(utils.connection(), baseName);
1041     QVERIFY(!tempName2.isEmpty());
1042     QVERIFY(tempName2.contains(baseName));
1043     QVERIFY(tempName1 != tempName2);
1044 
1045     utils.connection()->closeDatabase();
1046     QTest::ignoreMessage(QtWarningMsg, "Missing database handle");
1047     QTest::ignoreMessage(QtWarningMsg, QRegularExpression("!executeQuery().*"));
1048     QString tempName = KDb::temporaryTableName(utils.connection(), baseName);
1049     QVERIFY2(tempName.isEmpty(), "Temporary name should not be created when database is closed ");
1050 
1051     utils.connection()->disconnect();
1052     QTest::ignoreMessage(QtWarningMsg, "Missing database handle");
1053     QTest::ignoreMessage(QtWarningMsg, QRegularExpression("!executeQuery().*"));
1054     tempName = KDb::temporaryTableName(utils.connection(), baseName);
1055     QVERIFY2(tempName.isEmpty(), "Temporary name should not be created connection is missing");
1056 
1057     utils.connection()->dropDatabase(utils.connection()->data().databaseName());
1058 }
1059 
1060 //! @todo add tests
1061 #if 0
1062 /*! @return absolute path to "sqlite3" program.
1063  Empty string is returned if the program was not found. */
1064 KDB_EXPORT QString sqlite3ProgramPath();
1065 
1066 /*! Imports file in SQL format from @a inputFileName into @a outputFileName.
1067  Works for any SQLite 3 dump file. Requires access to executing the "sqlite3" command.
1068  File named @a outputFileName will be silently overwritten with a new SQLite 3 database file.
1069  @return true on success. */
1070 KDB_EXPORT bool importSqliteFile(const QString &inputFileName, const QString &outputFileName);
1071 
1072 /*! @return @c true if @a s is a valid identifier, i.e. starts with a letter or '_' character
1073  and contains only letters, numbers and '_' character. */
1074 KDB_EXPORT bool isIdentifier(const QString& s);
1075 
1076 /*! @return valid identifier based on @a s.
1077  Non-alphanumeric characters (or spaces) are replaced with '_'.
1078  If a number is at the beginning, '_' is added at start.
1079  Empty strings are not changed. Case remains unchanged. */
1080 KDB_EXPORT QString stringToIdentifier(const QString &s);
1081 
1082 /*! @return useful message "Value of "valueName" column must be an identifier.
1083   "v" is not a valid identifier.". It is also used by KDbIdentifierValidator.  */
1084 KDB_EXPORT QString identifierExpectedMessage(const QString &valueName,
1085         const QVariant& v);
1086 #endif
1087 
1088 void KDbTest::deleteRecordWithOneConstraintsTest()
1089 {
1090     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1091     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "id", 2));
1092     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "id", "3"),
1093             "Passing a valid Integer in String Format");
1094     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "id", "Foo"));
1095     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "name", "Jaroslaw"));
1096     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "surname", "FooBar"));
1097     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", 45));
1098     // and empty data.
1099     KDbTableSchema *kdb_t = utils.connection()->tableSchema("persons");
1100     QVERIFY(kdb_t);
1101     QVERIFY2(utils.connection()->insertRecord(kdb_t, 10, 20, QVariant(), "Bar"),
1102              "Inserting NULL data");
1103     QVERIFY2(utils.connection()->insertRecord(kdb_t,15, 20, "", "Bar"),
1104              "Inserting empty data");
1105     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "name", QString()),
1106             "Passing a null value instead of string");
1107     //
1108     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "name", ""),
1109              "Passing an empty string");
1110     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", "Nitish"));
1111     QVERIFY(utils.testDisconnectAndDropDb());
1112 }
1113 
1114 static QRegularExpression resultRegExp(const QString &code, const QString &message,
1115                                        const QString &sql, const QString &serverErrorCode,
1116                                        const QString &serverMessage)
1117 {
1118     return QRegularExpression(
1119         QString::fromLatin1("KDbResult: CODE=%1 MESSAGE=\\\"%2\\\" ERR_SQL=KDbEscapedString:"
1120                             "\\\"%3\\\"  SERVER_ERROR_CODE=%4 SERVER_MESSAGE=\\\"%5")
1121                 .arg(code.isEmpty() ? "[0-9]*" : code, message, sql,
1122                      serverErrorCode.isEmpty() ? "[0-9]*" : serverErrorCode, serverMessage));
1123 }
1124 
1125 void KDbTest::deleteNonExistingRecordTest()
1126 {
1127     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1128     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "id", 400));
1129     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "name", "FooBar"));
1130     QTest::ignoreMessage(QtWarningMsg, resultRegExp("260", "Error while executing SQL statement.",
1131         "DELETE FROM \\[persons\\] WHERE \\[Foo\\]='FooBar'", "0", "no such column: Foo"));
1132     QVERIFY2(!KDb::deleteRecords(utils.connection(), "persons", "Foo", "FooBar"),
1133              "Passing a NonExisting Column - should fail because 'Foo' column does not exist, "
1134              "See also https://bugs.kde.org/376052");
1135     QVERIFY(utils.testDisconnectAndDropDb());
1136 }
1137 
1138 void KDbTest::deleteRecordWithTwoConstraintsTest()
1139 {
1140     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1141     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "id", KDbField::Integer,
1142                                 2, "age", KDbField::Integer, 60),
1143              "Both fields are INTEGER");
1144     KDbTableSchema *kdb_t = utils.connection()->tableSchema("persons");
1145     QVERIFY(kdb_t);
1146     utils.connection()->insertRecord(kdb_t, 10, QVariant(), "Foo", "Bar") ;
1147     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "id", KDbField::Integer,
1148                                 10, "age", KDbField::Integer, QVariant()),
1149              "Passing NULL value for integer field");
1150     QVERIFY(utils.connection()->insertRecord(kdb_t, 20, QVariant(), QVariant(), "Bar"));
1151     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer,
1152                                 QVariant(), "name", KDbField::Text, QVariant()),
1153              "Passing 2 NULL values");
1154     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer,
1155                                 20, "name", KDbField::Text, "Jaroslaw"),
1156              "One argument is Integer and another is Text");
1157     QVERIFY2(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer,
1158                                 20, "name", KDbField::Text, 56),
1159              "Two arguments, passing second integer instead of text but it is converted to text");
1160     QTest::ignoreMessage(QtWarningMsg, resultRegExp("260", "Error while executing SQL statement.",
1161         "DELETE FROM \\[persons\\] WHERE \\[age\\]=TRAP AND \\[name\\]='56'", "0", "no such column: TRAP"));
1162     QVERIFY2(!KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer,
1163                                  "TRAP", "name", KDbField::Text, 56),
1164              "Passing text instead of integer, conversion error expected");
1165     QVERIFY(utils.testDisconnectAndDropDb());
1166 }
1167 
1168 void  KDbTest::deleteRecordWithThreeConstraintsTest()
1169 {
1170     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1171     KDbTableSchema *kdb_t = utils.connection()->tableSchema("persons");
1172     QVERIFY(kdb_t);
1173     //One null value.
1174     QVERIFY(utils.connection()->insertRecord(kdb_t, 10, QVariant(), "Foo", "Bar"));
1175     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer, QVariant(),
1176                                 "name", KDbField::Text, "Foo", "surname", KDbField::Text, "Bar"));
1177     //Mix of null and empty values
1178     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer, QVariant(),
1179                                 "name", KDbField::Text, "", "surname", KDbField::Text, ""));
1180     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer,27,
1181                                 "name", KDbField::Text, "Jaraslaw", "id", KDbField::Integer, 1));
1182     QVERIFY(KDb::deleteRecords(utils.connection(), "persons", "age", KDbField::Integer, 60,
1183                                 "name", KDbField::Text, "Lech", "id", KDbField::Integer, 2));
1184     QVERIFY(utils.testDisconnectAndDropDb());
1185 }
1186 
1187 void KDbTest::deleteAllRecordsTest()
1188 {
1189     QVERIFY(utils.testCreateDbWithTables("KDbTest"));
1190     QVERIFY(KDb::deleteAllRecords(utils.connection(), "persons"));
1191 
1192     QRegularExpression deleteAllErrorRegExp = resultRegExp(
1193         "", "Error while executing SQL statement.", "DELETE FROM \\[.*\\]", "0", "no such table: .*");
1194     QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp);
1195     QVERIFY2(!KDb::deleteAllRecords(utils.connection(), QString()),
1196              "Passing a null table name");
1197     QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp);
1198     QVERIFY2(!KDb::deleteAllRecords(utils.connection(), ""),
1199              "Passing an empty table name");
1200     QVERIFY(KDb::deleteAllRecords(utils.connection(), "cars"));
1201     QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp);
1202     QVERIFY2(!KDb::deleteAllRecords(utils.connection(), "NonExistingTable"),
1203              "Passing a nonexisting table name");
1204     QVERIFY(utils.testDisconnectAndDropDb());
1205 }
1206 
1207 void KDbTest::cleanupTestCase()
1208 {
1209 }