diff --git a/autotests/KDbTest.cpp b/autotests/KDbTest.cpp index 57c5b6b5..d2b3d7a1 100644 --- a/autotests/KDbTest.cpp +++ b/autotests/KDbTest.cpp @@ -1,1209 +1,1209 @@ /* This file is part of the KDE project Copyright (C) 2015-2016 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbTest.h" #include #include #include #include #include QTEST_GUILESS_MAIN(KDbTest) void KDbTest::initTestCase() { } void KDbTest::testVersionInfo() { KDbVersionInfo info = KDb::version(); KDbVersionInfo info2(KDb::version()); QCOMPARE(info, info2); KDbVersionInfo info3(info.major(), info.minor(), info.release()); QCOMPARE(info, info3); QVERIFY(KDbVersionInfo(0, 0, 0).isNull()); QVERIFY(!info.isNull()); QVERIFY(!info2.isNull()); QVERIFY(!info3.isNull()); } //! @todo add tests requiring connection #if 0 //! @overload bool deleteRecord(KDbConnection*, const KDbTableSchema&, const QString &, KDbField::Type, const QVariant &) KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName, const QString &keyname, KDbField::Type keytype, const QVariant &keyval); //! Deletes records using one generic criteria. inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table, const QString &keyname, KDbField::Type keytype, const QVariant &keyval) //! @overload bool deleteRecords(KDbConnection*, const QString&, const QString&, KDbField::Type, const QVariant&); inline bool deleteRecords(KDbConnection* conn, const QString &tableName, const QString &keyname, const QString &keyval) //! @overload bool deleteRecords(KDbConnection*, const QString&, const QString&, const QString&); inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table, const QString &keyname, const QString &keyval) //! @overload bool deleteRecords(KDbConnection*, const KDbTableSchema&, const QString&, const QString&); inline bool deleteRecords(KDbConnection* conn, const KDbTableSchema &table, const QString& keyname, int keyval) //! @overload bool deleteRecords(KDbConnection*, const KDbTableSchema&, const QString&, int); inline bool deleteRecords(KDbConnection* conn, const QString &tableName, const QString& keyname, int keyval) //! Deletes records with two generic criterias. KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName, const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1, const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2); //! Deletes records with three generic criterias. KDB_EXPORT bool deleteRecords(KDbConnection* conn, const QString &tableName, const QString &keyname1, KDbField::Type keytype1, const QVariant& keyval1, const QString &keyname2, KDbField::Type keytype2, const QVariant& keyval2, const QString &keyname3, KDbField::Type keytype3, const QVariant& keyval3); //! Deletes all records from table @a tableName. KDB_EXPORT bool deleteAllRecords(KDbConnection* conn, const QString &tableName); //! @overload bool deleteAllRecords(KDbConnection*, const QString&); inline bool deleteAllRecords(KDbConnection* conn, const KDbTableSchema &table) #endif void KDbTest::testFieldTypes() { QCOMPARE(KDbField::FirstType, KDbField::Byte); QCOMPARE(KDbField::LastType, KDbField::BLOB); QVERIFY(KDbField::FirstType < KDbField::LastType); } void KDbTest::testFieldTypesForGroup_data() { QTest::addColumn("typeGroup"); QTest::addColumn>("types"); int c = 0; ++c; QTest::newRow("invalid") << KDbField::InvalidGroup << (QList() << KDbField::InvalidType); ++c; QTest::newRow("text") << KDbField::TextGroup << (QList() << KDbField::Text << KDbField::LongText); ++c; QTest::newRow("integer") << KDbField::IntegerGroup << (QList() << KDbField::Byte << KDbField::ShortInteger << KDbField::Integer << KDbField::BigInteger); ++c; QTest::newRow("float") << KDbField::FloatGroup << (QList() << KDbField::Float << KDbField::Double); ++c; QTest::newRow("boolean") << KDbField::BooleanGroup << (QList() << KDbField::Boolean); ++c; QTest::newRow("datetime") << KDbField::DateTimeGroup << (QList() << KDbField::Date << KDbField::DateTime << KDbField::Time); ++c; QTest::newRow("blob") << KDbField::BLOBGroup << (QList() << KDbField::BLOB); QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything } void KDbTest::testFieldTypesForGroup() { QFETCH(KDbField::TypeGroup, typeGroup); QFETCH(QList, types); QCOMPARE(KDb::fieldTypesForGroup(typeGroup), types); } void KDbTest::testFieldTypeNamesAndStringsForGroup_data() { QTest::addColumn("typeGroup"); QTest::addColumn>("typeNames"); QTest::addColumn("typeStrings"); int c = 0; ++c; QTest::newRow("invalid") << KDbField::InvalidGroup << (QList() << "Invalid Type") << (QStringList() << "InvalidType"); ++c; QTest::newRow("text") << KDbField::TextGroup << (QList() << "Text" << "Long Text") << (QStringList() << "Text" << "LongText"); ++c; QTest::newRow("integer") << KDbField::IntegerGroup << (QList() << "Byte" << "Short Integer Number" << "Integer Number" << "Big Integer Number") << (QStringList() << "Byte" << "ShortInteger" << "Integer" << "BigInteger"); ++c; QTest::newRow("float") << KDbField::FloatGroup << (QList() << "Single Precision Number" << "Double Precision Number") << (QStringList() << "Float" << "Double"); ++c; QTest::newRow("boolean") << KDbField::BooleanGroup << (QList() << "Yes/No Value") << (QStringList() << "Boolean"); ++c; QTest::newRow("datetime") << KDbField::DateTimeGroup << (QList() << "Date" << "Date and Time" << "Time") << (QStringList() << "Date" << "DateTime" << "Time"); ++c; QTest::newRow("blob") << KDbField::BLOBGroup << (QList() << "Object") << (QStringList() << "BLOB"); QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything } void KDbTest::testFieldTypeNamesAndStringsForGroup() { QFETCH(KDbField::TypeGroup, typeGroup); QFETCH(QList, typeNames); QFETCH(QStringList, typeStrings); QStringList translatedNames; foreach(const QByteArray &name, typeNames) { translatedNames.append(KDbField::tr(name.constData())); } QCOMPARE(KDb::fieldTypeNamesForGroup(typeGroup), translatedNames); QCOMPARE(KDb::fieldTypeStringsForGroup(typeGroup), typeStrings); } void KDbTest::testDefaultFieldTypeForGroup() { int c = 0; ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::InvalidGroup), KDbField::InvalidType); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::TextGroup), KDbField::Text); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::IntegerGroup), KDbField::Integer); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::FloatGroup), KDbField::Double); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::BooleanGroup), KDbField::Boolean); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::DateTimeGroup), KDbField::Date); ++c; QCOMPARE(KDb::defaultFieldTypeForGroup(KDbField::BLOBGroup), KDbField::BLOB); QCOMPARE(c, KDbField::typeGroupsCount()); // make sure we're checking everything } void KDbTest::testSimplifiedFieldTypeName() { int c = 0; ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::InvalidType), KDbField::tr("Invalid Group")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Byte), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::ShortInteger), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Integer), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::BigInteger), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Boolean), KDbField::tr("Yes/No")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Date), KDbField::tr("Date/Time")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::DateTime), KDbField::tr("Date/Time")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Time), KDbField::tr("Date/Time")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Float), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Double), KDbField::tr("Number")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Text), KDbField::tr("Text")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::LongText), KDbField::tr("Text")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::BLOB), KDbField::tr("Image")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Null), KDbField::tr("Invalid Group")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Asterisk), KDbField::tr("Invalid Group")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Enum), KDbField::tr("Invalid Group")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Map), KDbField::tr("Invalid Group")); ++c; QCOMPARE(KDb::simplifiedFieldTypeName(KDbField::Tuple), KDbField::tr("Invalid Group")); QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount()); // make sure we're checking everything } void KDbTest::testIsEmptyValue_data() { QTest::addColumn("type"); QTest::addColumn("value"); QTest::addColumn("result"); QTest::addColumn("resultForNullValue"); QTest::addColumn("resultForEmptyString"); int c = 0; ++c; QTest::newRow("Invalid") << KDbField::InvalidType << QVariant("abc") << false << true << false; ++c; QTest::newRow("Byte") << KDbField::Byte << QVariant(17) << false << true << false; ++c; QTest::newRow("ShortInteger") << KDbField::ShortInteger << QVariant(1733) << false << true << false; ++c; QTest::newRow("Integer") << KDbField::Integer << QVariant(11733) << false << true << false; ++c; QTest::newRow("BigInteger") << KDbField::BigInteger << QVariant(0xffffff12) << false << true << false; ++c; QTest::newRow("Boolean") << KDbField::Boolean << QVariant(false) << false << true << false; ++c; QTest::newRow("Date") << KDbField::Date << QVariant(QDate(2015, 11, 07)) << false << true << false; ++c; QTest::newRow("DateTime") << KDbField::DateTime << QVariant(QDateTime(QDate(2015, 11, 07), QTime(12, 58, 17))) << false << true << false; ++c; QTest::newRow("Time") << KDbField::Time << QVariant(QTime(12, 58, 17)) << false << true << false; ++c; QTest::newRow("Float") << KDbField::Float << QVariant(3.14) << false << true << false; ++c; QTest::newRow("Double") << KDbField::Double << QVariant(3.1415) << false << true << false; ++c; QTest::newRow("Text") << KDbField::Text << QVariant(QLatin1String("abc")) << false << false << true; ++c; QTest::newRow("LongText") << KDbField::LongText << QVariant(QLatin1String("abc")) << false << false << true; ++c; QTest::newRow("BLOB") << KDbField::LongText << QVariant(QByteArray(5, 'X')) << false << false << true; ++c; QTest::newRow("Null") << KDbField::Null << QVariant(123) << false << true << false; ++c; QTest::newRow("Asterisk") << KDbField::Asterisk << QVariant(123) << false << true << false; ++c; QTest::newRow("Enum") << KDbField::Enum << QVariant(123) << false << true << false; ++c; QTest::newRow("Map") << KDbField::Map << QVariant(123) << false << true << false; ++c; QTest::newRow("Tuple") << KDbField::Tuple << QVariant(123) << false << true << false; QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount()); } void KDbTest::testIsEmptyValue() { QFETCH(KDbField::Type, type); QFETCH(QVariant, value); QFETCH(bool, result); QFETCH(bool, resultForNullValue); QFETCH(bool, resultForEmptyString); QCOMPARE(KDb::isEmptyValue(type, QVariant()), resultForNullValue); QCOMPARE(KDb::isEmptyValue(type, QVariant(QString(""))), resultForEmptyString); QCOMPARE(KDb::isEmptyValue(type, value), result); } //! @todo add tests #if 0 /*! Sets string pointed by @a msg to an error message retrieved from @a resultable, and string pointed by @a details to details of this error (server message and result number). Does nothing if @a result is empty. In this case @a msg and @a details strings are not overwritten. If the string pointed by @a msg is not empty, @a result message is appended to the string pointed by @a details. */ KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, QString *msg, QString *details); /*! This methods works like above, but appends both a message and a description to string pointed by @a msg. */ KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, QString *msg); /*! This methods works like above, but works on @a result's members instead. */ KDB_EXPORT void getHTMLErrorMesage(const KDbResultable& resultable, KDbResultInfo *info); /*! Function useful for building WHERE parts of SQL statements. Constructs an SQL string like "fielname = value" for specific @a drv driver, field type @a t, @a fieldName and @a value. If @a value is null, "fieldname is NULL" string is returned. */ KDB_EXPORT KDbEscapedString sqlWhere(KDbDriver *drv, KDbField::Type t, const QString& fieldName, const QVariant& value); /*! Find an identifier for object @a objName of type @a objType. On success true is returned and *id is set to the value of the identifier. On failure false is returned. If there is no such object, @c cancelled value is returned. */ KDB_EXPORT tristate idForObjectName(KDbConnection* conn, int *id, const QString& objName, int objType); /*! @return a number of columns that can be retrieved from table or query schema. In case of query, expanded fields are counted. Can return -1 if @a tableOrQuery has neither table or query assigned. */ KDB_EXPORT int fieldCount(KDbTableOrQuerySchema* tableOrQuery); /*! shows connection test dialog with a progress bar indicating connection testing (within a second thread). @a data is used to perform a (temporary) test connection. @a msgHandler is used to display errors. On successful connecting, a message is displayed. After testing, temporary connection is closed. */ KDB_EXPORT void connectionTestDialog(QWidget* parent, const KDbConnectionData& data, KDbMessageHandler* msgHandler); //! Used in splitToTableAndFieldParts(). enum SplitToTableAndFieldPartsOptions { FailIfNoTableOrFieldName = 0, //!< default value for splitToTableAndFieldParts() SetFieldNameIfNoTableName = 1 //!< see splitToTableAndFieldParts() }; /*! Splits @a string like "table.field" into "table" and "field" parts. On success, a table name is passed to @a tableName and a field name is passed to @a fieldName. The function fails if either: - @a string is empty, or - @a string does not contain '.' character and @a option is FailIfNoTableOrFieldName (the default), or - '.' character is the first of last character of @a string (in this case table name or field name could become empty what is not allowed). If @a option is SetFieldNameIfNoTableName and @a string does not contain '.', @a string is passed to @a fieldName and @a tableName is set to QString() without failure. If function fails, @a tableName and @a fieldName remain unchanged. @return true on success. */ KDB_EXPORT bool splitToTableAndFieldParts(const QString& string, QString *tableName, QString *fieldName, SplitToTableAndFieldPartsOptions option = FailIfNoTableOrFieldName); /*! @return true if @a type supports "visibleDecimalPlaces" property. */ KDB_EXPORT bool supportsVisibleDecimalPlacesProperty(KDbField::Type type); //*! @return string constructed by converting @a value. * If @a decimalPlaces is < 0, all meaningful fractional digits are returned (up to 10). * If @a automatically is 0, just integer part is returned. * If @a automatically is > 0, fractional part should take exactly N digits: if the fractional part is shorter than N, additional zeros are appended. Examples: * numberToString(12.345, 6) == "12.345000" * numberToString(12.345, 0) == "12" * numberToString(12.345, -1) == "12.345" * numberToString(12.0, -1) == "12" * numberToString(0.0, -1) == "0" @note No rounding is performed @note No thousands group separator is used. Decimal symbol is '.'. @see KDb::numberToLocaleString() KDbField::visibleDecimalPlaces() */ KDB_EXPORT QString numberToString(double value, int decimalPlaces); /*! Like KDb::numberToString() but formats the string using locale.toString(). If @a locale if @c nullptr, desault QLocale is used. @see KDb::numberToString() KDbField::visibleDecimalPlaces() */ KDB_EXPORT QString numberToLocaleString(double value, int decimalPlaces, const QLocale *locale = nullptr); //! @return true if @a propertyName is a builtin field property. KDB_EXPORT bool isBuiltinTableFieldProperty(const QByteArray& propertyName); //! @return true if @a propertyName is an extended field property. KDB_EXPORT bool isExtendedTableFieldProperty(const QByteArray& propertyName); //! @return true if @a propertyName is belongs to lookup field's schema. KDB_EXPORT bool isLookupFieldSchemaProperty(const QByteArray& propertyName); /*! @return type of field for integer value @a type. If @a type cannot be casted to KDbField::Type, KDbField::InvalidType is returned. This can be used when type information is deserialized from a string or QVariant. */ KDB_EXPORT KDbField::Type intToFieldType(int type); /*! @return type group of field for integer value @a typeGroup. If @a typeGroup cannot be casted to KDbField::TypeGroup, KDbField::InvalidGroup is returned. This can be used when type information is deserialized from a string or QVariant. */ KDB_EXPORT KDbField::TypeGroup intToFieldTypeGroup(int typeGroup); /*! Gets property values for the lookup schema @a lookup. @a values is cleared before filling. This function is used e.g. for altering table design. */ KDB_EXPORT void getProperties(const KDbLookupFieldSchema *lookup, QMap *values); /*! Gets property values for @a field. Properties from extended schema are included. @a values is cleared before filling. The same number of properties in the same order is returned. This function is used e.g. for altering table design. */ KDB_EXPORT void getFieldProperties(const KDbField &field, QMap *values); /*! Sets property values for @a field. @return true if all the values are valid and allowed. On failure contents of @a field is undefined. Properties from extended schema are also supported. This function is used e.g. by KDbAlterTableHandler when property information comes in form of text. */ KDB_EXPORT bool setFieldProperties(KDbField *field, const QMap& values); /*! Sets property value for @a field. @return true if the property has been found and the value is valid for this property. On failure contents of @a field is undefined. Properties from extended schema are also supported as well as QVariant customProperty(const QString& propertyName) const; This function is used e.g. by KDbAlterTableHandler when property information comes in form of text. */ KDB_EXPORT bool setFieldProperty(KDbField *field, const QByteArray& propertyName, const QVariant& value); /*! @return property value loaded from a DOM @a node, written in a QtDesigner-like notation: <number>int</number> or <bool>bool</bool>, etc. Supported types are "string", "cstring", "bool", "number". For invalid values null QVariant is returned. You can check the validity of the returned value using QVariant::type(). */ KDB_EXPORT QVariant loadPropertyValueFromDom(const QDomNode& node, bool *ok); /*! Convenience version of loadPropertyValueFromDom(). @return int value. */ KDB_EXPORT int loadIntPropertyValueFromDom(const QDomNode& node, bool* ok); /*! Convenience version of loadPropertyValueFromDom(). @return QString value. */ KDB_EXPORT QString loadStringPropertyValueFromDom(const QDomNode& node, bool* ok); /*! Saves integer element for value @a value to @a doc document within parent element @a parentEl. The value will be enclosed in "number" element and "elementName" element. Example: saveNumberElementToDom(doc, parentEl, "height", 15) will create @code 15 @endcode @return the reference to element created with tag elementName. */ KDB_EXPORT QDomElement saveNumberElementToDom(QDomDocument *doc, QDomElement *parentEl, const QString& elementName, int value); /*! Saves boolean element for value @a value to @a doc document within parent element @a parentEl. Like saveNumberElementToDom() but creates "bool" tags. True/false values will be saved as "true"/"false" strings. @return the reference to element created with tag elementName. */ KDB_EXPORT QDomElement saveBooleanElementToDom(QDomDocument *doc, QDomElement *parentEl, const QString& elementName, bool value); //! @return equivalent of empty (default) value that can be set for a database field of type @a type /*! In particular returns: - empty string for text types, - 0 for integer and floating-point types, - false for boolean types, - a null byte array for BLOB type, - current date, time, date+time is returned (measured at client side) for date, time and date/time types respectively, - a null QVariant for unsupported values such as KDbField::InvalidType. */ KDB_EXPORT QVariant emptyValueForFieldType(KDbField::Type type); //! @return a value that can be set for a database field of type @a type having "notEmpty" property set. /*! It works in a similar way as @ref QVariant KDb::emptyValueForFieldType(KDbField::Type type) with the following differences: - " " string (a single space) is returned for Text and LongText types - a byte array with saved "filenew" PNG image (icon) for BLOB type Returns null QVariant for unsupported values like KDbField::InvalidType. */ KDB_EXPORT QVariant notEmptyValueForFieldType(KDbField::Type type); /*! @return true if the @a word is an reserved KDbSQL keyword See src/generated/sqlkeywords.cpp in the KDb source code. @todo add function returning list of keywords. */ KDB_EXPORT bool isKDbSqlKeyword(const QByteArray& word); //! @return @a string string with applied KDbSQL identifier escaping /*! This escaping can be used for field, table, database names, etc. Use it for user-visible backend-independent statements. @see KDb::escapeIdentifierAndAddQuotes() */ KDB_EXPORT QString escapeIdentifier(const QString& string); //! @overload QString escapeIdentifier(const QString&) KDB_EXPORT QByteArray escapeIdentifier(const QByteArray& string); //! @return @a string string with applied KDbSQL identifier escaping and enclosed in " quotes /*! This escaping can be used for field, table, database names, etc. Use it for user-visible backend-independent statements. @see KDb::escapeIdentifier */ KDB_EXPORT QString escapeIdentifierAndAddQuotes(const QString& string); //! @overload QString escapeIdentifierAndAddQuotes(const QString&) KDB_EXPORT QByteArray escapeIdentifierAndAddQuotes(const QByteArray& string); /*! @return escaped string @a string w using KDbSQL dialect, i.e. doubles single quotes ("'") and inserts the string into single quotes. Quotes "'" are prepended and appended. Also escapes \\n, \\r, \\t, \\\\, \\0. Use it for user-visible backend-independent statements. */ KDB_EXPORT QString escapeString(const QString& string); /** * @brief Returns escaped string @a string * * If @a drv driver is present, it is used to perform escaping, otherwise escapeString() is used * so the KDbSQL dialect-escaping is performed. */ KDB_EXPORT KDbEscapedString escapeString(KDbDriver *drv, const QString& string); /** * @brief Returns escaped string @a string * * If @a conn is present, its driver is used to perform escaping, otherwise escapeString() is used * so the KDbSQL dialect-escaping is performed. */ KDB_EXPORT KDbEscapedString escapeString(KDbConnection *conn, const QString& string); #endif void KDbTest::testUnescapeString_data() { QTest::addColumn("sequence"); QTest::addColumn("result"); QTest::addColumn("quote"); // can be ' or ", if 0 then both variants are checked QTest::addColumn("errorPosition"); QTest::addColumn("errorPositionWhenAppended"); // quote-independent cases, success #define T2(tag, sequence, result, quote) QTest::newRow(tag) << QString::fromUtf8(sequence) \ << QString::fromUtf8(result) << quote << -1 << -1 #define T(tag, sequence, result) T2(tag, sequence, result, '\0') QTest::newRow("null") << QString() << QString() << '\0' << -1 << -1; QTest::newRow("\\0") << QString("\\0") << QString(QLatin1Char('\0')) << '\0' << -1 << -1; const char *s = " String without escaping %_? 𝌆 ©"; T("without escaping", s, s); T("empty", "", ""); T("\\'", "\\'", "'"); T("\\\"", "\\\"", "\""); T("\\\\", "\\\\", "\\"); T("\\b", "\\b", "\b"); T("\\f", "\\f", "\f"); T("\\n", "\\n", "\n"); T("\\r", "\\r", "\r"); T("\\t", "\\t", "\t"); T("\\v", "\\v", "\v"); T("_\\_", "_\\_", "__"); T("?\\?", "?\\?", "??"); T("%\\%", "%\\%", "%%"); T("ignored \\ in \\a", "\\a", "a"); T("ignored \\ in \\♥", "\\♥ ", "♥ "); T("ignored \\ in 𝌆\\\\\\a", "𝌆\\\\\\a", "𝌆\\a"); T("unfinished \\", "\\", ""); T("unfinished \\ 2", "one two\\", "one two"); T("\\xA9", "\\xA9", "©"); T("\\xa9\\xa9", "\\xa9\\xa9", "©©"); QTest::newRow("\\x00") << QString("\\x00") << QString(QLatin1Char('\0')) << '\0' << -1 << -1; QTest::newRow("\\u0000") << QString("\\u0000") << QString(QChar(static_cast(0))) << '\0' << -1 << -1; T("\\u2665", "\\u2665", "♥"); #ifndef _MSC_VER // does not work with MSVC: "warning C4566: character represented // by universal-character-name cannot be represented in the current code page" T("\\xff", "\\xff", "\u00ff"); T("\\uffff", "\\uffff", "\uffff"); #endif QTest::newRow("\\u{0}") << QString("\\u{0}") << QString(QLatin1Char('\0')) << '\0' << -1 << -1; QTest::newRow("\\u{0000000000}") << QString("\\u{0000000000}") << QString(QLatin1Char('\0')) << '\0' << -1 << -1; T("\\u{A9}", "\\u{A9}", "©"); T("\\u{a9}", "\\u{a9}", "©"); T("\\u{0a9}", "\\u{0a9}", "©"); T("\\u{00a9}", "\\u{00a9}", "©"); T("\\u{2665}", "\\u{2665}", "♥"); T("\\u{02665}", "\\u{02665}", "♥"); QTest::newRow("\\u{1D306}") << QString("\\u{1D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1; QTest::newRow("\\u{1d306}") << QString("\\u{1d306}") << QString(QChar(0x1d306)) << '\0' << -1 << -1; QTest::newRow("\\u{01D306}") << QString("\\u{01D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1; QTest::newRow("\\u{01d306}") << QString("\\u{01d306}") << QString(QChar(0x1d306)) << '\0' << -1 << -1; QTest::newRow("\\u{00001D306}") << QString("\\u{00001D306}") << QString(QChar(0x1D306)) << '\0' << -1 << -1; QTest::newRow("\\u{10FFFF}") << QString("\\u{10FFFF}") << QString(QChar(0x10FFFF)) << '\0' << -1 << -1; // quote-dependent cases, success T2("2x ' for ' quote", "''", "'", '\''); T2("4x ' for ' quote", "''''", "''", '\''); T2("2x \" for ' quote", "\"\"", "\"\"", '\''); T2("3x \" for ' quote", "\"\"\"", "\"\"\"", '\''); T2("2x ' for \" quote", "''", "''", '"'); T2("3x ' for \" quote", "'''", "'''", '"'); T2("2x \" for \" quote", "\"\"", "\"", '"'); T2("4x \" for \" quote", "\"\"\"\"", "\"\"", '"'); #undef T #undef T2 // failures QTest::newRow("invalid quote") << QString::fromUtf8("abc") << QString() << 'x' << 0 << 0; #define T(tag, sequence, quote, errorPosition, errorPositionWhenAppended) \ QTest::newRow(tag) << QString::fromUtf8(sequence) << QString() << quote \ << errorPosition << errorPositionWhenAppended T("missing ' quote", "'", '\'', 0, 0); T("missing \" quote", "\"", '"', 0, 0); T("invalid \\x", "\\x", '\0', 1, 2); T("invalid \\xQ", "\\xQ", '\0', 2, 2); T("invalid \\xQt", "\\xQt", '\0', 2, 2); T("invalid \\xAQ", "\\xAQ", '\0', 3, 3); T("invalid \\u", "\\u", '\0', 1, 2); T("invalid \\ua", "\\ua", '\0', 2, 3); T("invalid \\u40", "\\u40", '\0', 3, 4); T("invalid \\u405", "\\u405", '\0', 4, 5); T("invalid \\uQ", "\\uQ", '\0', 2, 2); T("invalid \\uQt", "\\uQt", '\0', 2, 2); T("invalid \\uQt5", "\\uQt5", '\0', 2, 2); T("invalid \\uQt57", "\\uQt57", '\0', 2, 2); T("invalid \\uaQ", "\\uaQ", '\0', 3, 3); T("invalid \\uabQ", "\\uabQ", '\0', 4, 4); T("invalid \\uabcQ", "\\uabcQ", '\0', 5, 5); T("invalid \\u{", "\\u{", '\0', 2, 3); T("invalid \\u{26", "\\u{26", '\0', 4, 5); T("invalid \\u{266", "\\u{266", '\0', 5, 6); T("invalid \\u{2665", "\\u{2665", '\0', 6, 7); T("invalid \\u{2665a", "\\u{2665a", '\0', 7, 8); T("invalid \\u{}", "\\u{}", '\0', 3, 3); T("invalid \\u{Q}", "\\u{Q}", '\0', 3, 3); T("invalid \\u{Qt}", "\\u{Qt}", '\0', 3, 3); T("invalid \\u{Qt5}", "\\u{Qt5}", '\0', 3, 3); T("invalid \\u{Qt57}", "\\u{Qt57}", '\0', 3, 3); T("invalid \\u{Qt57", "\\u{Qt57", '\0', 3, 3); T("invalid \\u{aQ}", "\\u{aQ}", '\0', 4, 4); T("invalid \\u{abQ}", "\\u{abQ}", '\0', 5, 5); T("invalid \\u{abcQ}", "\\u{abcQ}", '\0', 6, 6); T("invalid \\u{abcdQ}", "\\u{abcdQ}", '\0', 7, 7); T("invalid \\u{abcdQ}", "\\u{abcdQ}", '\0', 7, 7); T("invalid \\u{abcdfQ}", "\\u{abcdfQ}", '\0', 8, 8); T("invalid too large \\u{110000}", "\\u{110000}", '\0', 8, 8); T("invalid too large \\u{1100000}", "\\u{1100000}", '\0', 8, 8); T("invalid too large \\u{00110000}", "\\u{00110000}", '\0', 10, 10); } void KDbTest::testUnescapeStringHelper(const QString &sequenceString, const QString &resultString_, char quote, int errorPosition, int offset) { int actualErrorPosition = -2; QString resultString(resultString_); if (errorPosition >= 0) { errorPosition += offset; resultString.clear(); } //qDebug() << KDb::unescapeString("\\0bar", '\'', &errorPosition); #define COMPARE(x, y) \ if (x != y) { \ qDebug() << "sequenceString:" << sequenceString << "resultString:" << resultString; \ } \ QCOMPARE(x, y) if (quote == 0) { // both cases COMPARE(KDb::unescapeString(sequenceString, '\'', &actualErrorPosition), resultString); COMPARE(actualErrorPosition, errorPosition); COMPARE(KDb::unescapeString(sequenceString, '\'', nullptr), resultString); COMPARE(KDb::unescapeString(sequenceString, '"', &actualErrorPosition), resultString); COMPARE(actualErrorPosition, errorPosition); COMPARE(KDb::unescapeString(sequenceString, '"', nullptr), resultString); } else { if (quote != '\'' && quote != '"') { resultString.clear(); errorPosition = 0; } COMPARE(KDb::unescapeString(sequenceString, quote, &actualErrorPosition), resultString); COMPARE(actualErrorPosition, errorPosition); COMPARE(KDb::unescapeString(sequenceString, quote, nullptr), resultString); } #undef CHECK_POS } void KDbTest::testUnescapeString() { QFETCH(QString, sequence); QFETCH(QString, result); QFETCH(char, quote); QFETCH(int, errorPosition); QFETCH(int, errorPositionWhenAppended); testUnescapeStringHelper(sequence, result, quote, errorPosition, 0); testUnescapeStringHelper("foo" + sequence, "foo" + result, quote, errorPosition, 3); testUnescapeStringHelper(sequence + " bar", result + " bar", quote, errorPositionWhenAppended, 0); testUnescapeStringHelper("foo" + sequence + " bar", "foo" + result + " bar", quote, errorPositionWhenAppended, 3); } void KDbTest::testEscapeBLOB_data() { QTest::addColumn("blob"); QTest::addColumn("escapedX"); QTest::addColumn("escaped0x"); QTest::addColumn("escapedHex"); QTest::addColumn("escapedOctal"); QTest::addColumn("escapedBytea"); QTest::newRow("") << QByteArray() << QString("X''") << QString() << QString("") << QString("''") << QString("E'\\\\x'::bytea"); QTest::newRow("0,1,k") << QByteArray("\0\1k", 3) << QString("X'00016B'") << QString("0x00016B") << QString("00016B") << QString("'\\\\000\\\\001k'") << QString("E'\\\\x00016B'::bytea"); QTest::newRow("ABC\\\\0") << QByteArray("ABC\0", 4) << QString("X'41424300'") << QString("0x41424300") << QString("41424300") << QString("'ABC\\\\000'") << QString("E'\\\\x41424300'::bytea"); QTest::newRow("'") << QByteArray("'") << QString("X'27'") << QString("0x27") << QString("27") << QString("'\\\\047'") << QString("E'\\\\x27'::bytea"); QTest::newRow("\\") << QByteArray("\\") << QString("X'5C'") << QString("0x5C") << QString("5C") << QString("'\\\\134'") << QString("E'\\\\x5C'::bytea"); } void KDbTest::testEscapeBLOB() { QFETCH(QByteArray, blob); QFETCH(QString, escapedX); QFETCH(QString, escaped0x); QFETCH(QString, escapedHex); QFETCH(QString, escapedOctal); QFETCH(QString, escapedBytea); QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::XHex), escapedX); QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::ZeroXHex), escaped0x); QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::Hex), escapedHex); QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::Octal), escapedOctal); QCOMPARE(KDb::escapeBLOB(blob, KDb::BLOBEscapingType::ByteaHex), escapedBytea); } void KDbTest::testPgsqlByteaToByteArray() { QCOMPARE(KDb::pgsqlByteaToByteArray(nullptr, 0), QByteArray()); QCOMPARE(KDb::pgsqlByteaToByteArray("", 0), QByteArray()); QCOMPARE(KDb::pgsqlByteaToByteArray(" ", 0), QByteArray()); QCOMPARE(KDb::pgsqlByteaToByteArray("\\101"), QByteArray("A")); QCOMPARE(KDb::pgsqlByteaToByteArray("\\101", 4), QByteArray("A")); QCOMPARE(KDb::pgsqlByteaToByteArray("\\101B", 4), QByteArray("A")); // cut-off at #4 QCOMPARE(KDb::pgsqlByteaToByteArray("\\'\\\\\\'"), QByteArray("\'\\\'")); QCOMPARE(KDb::pgsqlByteaToByteArray("\\\\a\\377bc\\'d\"\n"), QByteArray("\\a\377bc\'d\"\n")); } void KDbTest::testXHexToByteArray_data() { QTest::addColumn("data"); QTest::addColumn("length"); // -2 means "compute length", other values: pass it as is QTest::addColumn("ok"); QTest::addColumn("result"); QTest::newRow("") << QByteArray() << 0 << false << QByteArray(); QTest::newRow("bad prefix") << QByteArray("bad") << -2 << false << QByteArray(); QTest::newRow("X") << QByteArray("X") << -2 << false << QByteArray(); QTest::newRow("X'") << QByteArray("X'") << -2 << false << QByteArray(); QTest::newRow("X''") << QByteArray("X''") << -2 << true << QByteArray(); QTest::newRow("X'1") << QByteArray("X'1") << -2 << false << QByteArray(); QTest::newRow("X'1' cut") << QByteArray("X'1'") << 3 << false << QByteArray(); QTest::newRow("X'1'") << QByteArray("X'1'") << -2 << true << QByteArray("\1"); QTest::newRow("X'0'") << QByteArray("X'0'") << -2 << true << QByteArray("\0", 1); QTest::newRow("X'000'") << QByteArray("X'000'") << -2 << true << QByteArray("\0\0", 2); QTest::newRow("X'01'") << QByteArray("X'01'") << -2 << true << QByteArray("\1"); QTest::newRow("X'FeAb2C'") << QByteArray("X'FeAb2C'") << -2 << true << QByteArray("\376\253\54"); } void KDbTest::testXHexToByteArray() { QFETCH(QByteArray, data); QFETCH(int, length); QFETCH(bool, ok); QFETCH(QByteArray, result); bool actualOk; QCOMPARE(KDb::xHexToByteArray(data.constData(), length == -1 ? data.length() : length, &actualOk), result); QCOMPARE(actualOk, ok); QCOMPARE(KDb::xHexToByteArray(data.constData(), length, nullptr), result); } void KDbTest::testZeroXHexToByteArray_data() { QTest::addColumn("data"); QTest::addColumn("length"); // -2 means "compute length", other values: pass it as is QTest::addColumn("ok"); QTest::addColumn("result"); QTest::newRow("") << QByteArray() << 0 << false << QByteArray(); QTest::newRow("0") << QByteArray("0") << -2 << false << QByteArray(); QTest::newRow("0x") << QByteArray("0x") << -2 << false << QByteArray(); QTest::newRow("0X22") << QByteArray("0X22") << -2 << false << QByteArray(); QTest::newRow("bad prefix") << QByteArray("bad") << -2 << false << QByteArray(); QTest::newRow("0x0") << QByteArray("0x0") << -2 << true << QByteArray("\0", 1); QTest::newRow("0x0 cut") << QByteArray("0x0") << 2 << false << QByteArray(); QTest::newRow("0X0") << QByteArray("0X0") << -2 << false << QByteArray(); QTest::newRow("0x0123") << QByteArray("0x0123") << -2 << true << QByteArray("\1\43"); QTest::newRow("0x0123 cut") << QByteArray("0x0123") << 4 << true << QByteArray("\1"); QTest::newRow("0x00000'") << QByteArray("0x00000") << -2 << true << QByteArray("\0\0\0", 3); QTest::newRow("0xFeAb2C") << QByteArray("0xFeAb2C") << -2 << true << QByteArray("\376\253\54"); } void KDbTest::testZeroXHexToByteArray() { QFETCH(QByteArray, data); QFETCH(int, length); QFETCH(bool, ok); QFETCH(QByteArray, result); bool actualOk; QCOMPARE(KDb::zeroXHexToByteArray(data.constData(), length == -1 ? data.length() : length, &actualOk), result); QCOMPARE(actualOk, ok); QCOMPARE(KDb::zeroXHexToByteArray(data.constData(), length, nullptr), result); } //! @todo add tests #if 0 /*! @return int list converted from string list. If @a ok is not 0, *ok is set to result of the conversion. */ KDB_EXPORT QList stringListToIntList(const QStringList &list, bool *ok); /*! @return string converted from list @a list. Separators are ',' characters, "," and "\\" are escaped. @see KDb::deserializeList() */ KDB_EXPORT QString serializeList(const QStringList &list); /*! @return string list converted from @a data which was built using serializeList(). Separators are ',' characters, escaping is assumed as "\\,". */ KDB_EXPORT QStringList deserializeList(const QString &data); /*! @return int list converted from @a data which was built using serializeList(). Separators are ',' characters, escaping is assumed as "\\,". If @a ok is not 0, *ok is set to result of the conversion. @see KDb::stringListToIntList() */ KDB_EXPORT QList deserializeIntList(const QString &data, bool *ok); /*! @return string value serialized from a variant value @a v. This functions works like QVariant::toString() except the case when @a v is of type: - QByteArray - in this case KDb::escapeBLOB(v.toByteArray(), KDb::BLOBEscapeHex) is used. - QStringList - in this case KDb::serializeList(v.toStringList()) is used. This function is needed for handling values of random type, for example "defaultValue" property of table fields can contain value of any type. Note: the returned string is an unescaped string. */ KDB_EXPORT QString variantToString(const QVariant& v); /*! @return variant value of type @a type for a string @a s that was previously serialized using @ref variantToString( const QVariant& v ) function. @a ok is set to the result of the operation. With exception for types mentioned in documentation of variantToString(), QVariant::convert() is used for conversion. */ KDB_EXPORT QVariant stringToVariant(const QString& s, QVariant::Type type, bool* ok); /*! @return true if setting default value for @a field field is allowed. Fields with unique (and thus primary key) flags set do not accept default values. */ KDB_EXPORT bool isDefaultValueAllowed(const KDbField &field); //! Provides limits for values of type @a type /*! The result is put into integers pointed by @a minValue and @a maxValue. The limits are machine-independent,. what is useful for format and protocol compatibility. Supported types are Byte, ShortInteger, Integer and BigInteger. The value of @a signedness controls the values; they can be limited to unsigned or not. Results for BigInteger or non-integer types are the same as for Integer due to limitation of int type. Signed integers are assumed. @a minValue and @a maxValue must not be 0. */ KDB_EXPORT void getLimitsForFieldType(KDbField::Type type, qlonglong *minValue, qlonglong *maxValue, KDb::Signedness signedness = KDb::Signed); /*! @return type that's maximum of two integer types @a t1 and @a t2, e.g. Integer for (Byte, Integer). If one of the types is not of the integer group, KDbField::InvalidType is returned. Returned type may not fit to the result of evaluated expression that involves the arguments. For example, 100 is within Byte type, maximumForIntegerFieldTypes(Byte, Byte) is Byte but result of 100 * 100 exceeds the range of Byte. */ KDB_EXPORT KDbField::Type maximumForIntegerFieldTypes(KDbField::Type t1, KDbField::Type t2); #endif void KDbTest::testCstringToVariant_data() { QTest::addColumn("data"); // QString() -> 0, QString("") -> empty string "" QTest::addColumn("type"); QTest::addColumn("length"); QTest::addColumn("variant"); QTest::addColumn("signedness"); QTest::addColumn("okResult"); int c = 0; ++c; QTest::newRow("invalid1") << QString() << KDbField::InvalidType << -1 << QVariant() << KDb::Signed << false; QTest::newRow("invalid2") << "" << KDbField::InvalidType << -1 << QVariant() << KDb::Signed << false; QTest::newRow("invalid3") << "abc" << KDbField::InvalidType << 3 << QVariant() << KDb::Signed << false; ++c; QTest::newRow("byte1") << "0" << KDbField::Byte << 1 << QVariant(0) << KDb::Signed << true; QTest::newRow("ubyte1") << "0" << KDbField::Byte << 1 << QVariant(0) << KDb::Unsigned << true; QTest::newRow("byte2") << "42" << KDbField::Byte << -1 << QVariant(42) << KDb::Signed << true; QTest::newRow("ubyte2") << "42" << KDbField::Byte << -1 << QVariant(42) << KDb::Unsigned << true; QTest::newRow("byte3") << "129" << KDbField::Byte << -1 << QVariant() << KDb::Signed << false; QTest::newRow("ubyte3") << "129" << KDbField::Byte << -1 << QVariant(129) << KDb::Unsigned << true; QTest::newRow("byte4") << "-128" << KDbField::Byte << -1 << QVariant(-128) << KDb::Signed << true; QTest::newRow("ubyte4") << "-128" << KDbField::Byte << -1 << QVariant() << KDb::Unsigned << false; ++c; QTest::newRow("short1") << "-123" << KDbField::ShortInteger << -1 << QVariant(-123) << KDb::Signed << true; QTest::newRow("short2") << "942" << KDbField::ShortInteger << -1 << QVariant(942) << KDb::Signed << true; QTest::newRow("short3") << "32767" << KDbField::ShortInteger << -1 << QVariant(32767) << KDb::Signed << true; QTest::newRow("short4") << "32768" << KDbField::ShortInteger << -1 << QVariant() << KDb::Signed << false; QTest::newRow("ushort4") << "32768" << KDbField::ShortInteger << -1 << QVariant(32768) << KDb::Unsigned << true; QTest::newRow("short5") << "-32768" << KDbField::ShortInteger << -1 << QVariant(-32768) << KDb::Signed << true; QTest::newRow("ushort5") << "-32768" << KDbField::ShortInteger << -1 << QVariant() << KDb::Unsigned << false; ++c; QTest::newRow("int1") << QString::number(0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(0x07FFFFFFF) << KDb::Signed << true; QTest::newRow("uint1") << QString::number(0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(0x07FFFFFFF) << KDb::Unsigned << true; QTest::newRow("int2") << QString::number(-0x07FFFFFFF) << KDbField::Integer << -1 << QVariant(-0x07FFFFFFF) << KDb::Signed << true; QTest::newRow("uint2") << QString::number(-0x07FFFFFFF) << KDbField::Integer << -1 << QVariant() << KDb::Unsigned << false; QTest::newRow("int3") << QString::number(std::numeric_limits::min()) << KDbField::Integer << -1 << QVariant() << KDb::Signed << false; QTest::newRow("uint4") << "-1" << KDbField::Integer << -1 << QVariant() << KDb::Unsigned << false; QTest::newRow("int4") << "-1" << KDbField::Integer << -1 << QVariant(-1) << KDb::Signed << true; //!< @todo cannot be larger? ++c; QTest::newRow("bigint1") << QString::number(0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(0x07FFFFFFF) << KDb::Signed << true; QTest::newRow("ubigint1") << QString::number(0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(0x07FFFFFFF) << KDb::Unsigned << true; QTest::newRow("bigint2") << QString::number(-0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant(-0x07FFFFFFF) << KDb::Signed << true; QTest::newRow("ubigint2") << QString::number(-0x07FFFFFFF) << KDbField::BigInteger << -1 << QVariant() << KDb::Unsigned << false; QTest::newRow("bigint3") << QString::number(std::numeric_limits::min()) << KDbField::BigInteger << -1 << QVariant() << KDb::Signed << false; QTest::newRow("ubigint4") << "-1" << KDbField::BigInteger << -1 << QVariant() << KDb::Unsigned << false; QTest::newRow("bigint4") << "-1" << KDbField::BigInteger << -1 << QVariant(-1) << KDb::Signed << true; ++c; QTest::newRow("bool0") << "0" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true; QTest::newRow("bool1") << "1" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; QTest::newRow("bool-") << "-" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; QTest::newRow("bool5") << "5" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; QTest::newRow("bool false") << "false" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true; QTest::newRow("bool False") << "False" << KDbField::Boolean << -1 << QVariant(false) << KDb::Signed << true; QTest::newRow("bool TRUE") << "TRUE" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; QTest::newRow("bool true") << "true" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; QTest::newRow("bool no") << "no" << KDbField::Boolean << -1 << QVariant(true) << KDb::Signed << true; // surprised? See docs for QVariant::toBool(). ++c; //! @todo support Date ++c; //! @todo support DateTime ++c; //! @todo support Time ++c; //! @todo support Float ++c; //! @todo support Double ++c; //! @todo support Text ++c; //! @todo support LongText ++c; //! @todo support BLOB ++c; QTest::newRow("Null") << " " << KDbField::Null << -1 << QVariant() << KDb::Signed << false; ++c; QTest::newRow("Asterisk") << " " << KDbField::Asterisk << -1 << QVariant() << KDb::Signed << false; ++c; QTest::newRow("Enum") << " " << KDbField::Enum << -1 << QVariant() << KDb::Signed << false; ++c; QTest::newRow("Map") << " " << KDbField::Map << -1 << QVariant() << KDb::Signed << false; ++c; QTest::newRow("Tuple") << " " << KDbField::Tuple << -1 << QVariant() << KDb::Signed << false; QCOMPARE(c, KDbField::typesCount() + KDbField::specialTypesCount()); } void KDbTest::testCstringToVariant() { QFETCH(QString, data); QFETCH(KDbField::Type, type); QFETCH(int, length); QFETCH(QVariant, variant); QFETCH(KDb::Signedness, signedness); QFETCH(bool, okResult); bool ok; const QByteArray ba(data.toUtf8()); // to avoid pointer to temp. const char *realData = ba.isNull() ? nullptr : ba.constData(); QCOMPARE(KDb::cstringToVariant(realData, type, &ok, length, signedness), variant); QCOMPARE(ok, okResult); QCOMPARE(KDb::cstringToVariant(realData, type, nullptr, length, signedness), variant); // a case where ok == 0 if (realData) { QCOMPARE(KDb::cstringToVariant(realData, type, &ok, data.length(), signedness), variant); // a case where length is set QCOMPARE(ok, okResult); } QCOMPARE(KDb::cstringToVariant(nullptr, type, &ok, length, signedness), QVariant()); // a case where data == 0 (NULL) QVERIFY(ok || type < KDbField::Byte || type > KDbField::LastType); // fails for NULL if this type isn't allowed if (type != KDbField::Boolean) { QCOMPARE(KDb::cstringToVariant(realData, type, &ok, 0, signedness), QVariant()); // a case where length == 0 QVERIFY(!ok); } if (KDbField::isTextType(type)) { // a case where data == "" QCOMPARE(KDb::cstringToVariant("", type, &ok, length, signedness), QVariant("")); QVERIFY(ok); } else if (type != KDbField::Boolean) { QCOMPARE(KDb::cstringToVariant("", type, &ok, length, signedness), QVariant()); QVERIFY(!ok); } } //! @todo add tests #if 0 /*! @return default file-based driver MIME type (typically something like "application/x-kexiproject-sqlite") */ KDB_EXPORT QString defaultFileBasedDriverMimeType(); /*! @return default file-based driver ID (currently, "org.kde.kdb.sqlite"). */ KDB_EXPORT QString defaultFileBasedDriverId(); /*! Escapes and converts value @a v (for type @a ftype) to string representation required by KDbSQL commands. For Date/Time type KDb::dateTimeToSql() is used. For BLOB type KDb::escapeBlob() with BLOBEscapingType::ZeroXHex conversion type is used. */ KDB_EXPORT KDbEscapedString valueToSql(KDbField::Type ftype, const QVariant& v); /*! Converts value @a v to string representation required by KDbSQL commands: ISO 8601 DateTime format - with "T" delimiter/ For specification see https://www.w3.org/TR/NOTE-datetime. Example: "1994-11-05T13:15:30" not "1994-11-05 13:15:30". @todo Add support for time zones */ KDB_EXPORT KDbEscapedString dateTimeToSql(const QDateTime& v); #ifdef KDB_DEBUG_GUI //! A prototype of handler for GUI debugger typedef void(*DebugGUIHandler)(const QString&); //! Sets handler for GUI debugger KDB_EXPORT void setDebugGUIHandler(DebugGUIHandler handler); //! Outputs string @a text to the GUI debugger KDB_EXPORT void debugGUI(const QString& text); //! A prototype of handler for GUI debugger (specialized for the Alter Table feature) typedef void(*AlterTableActionDebugGUIHandler)(const QString&, int); //! Sets handler for GUI debugger (specialized for the Alter Table feature) KDB_EXPORT void setAlterTableActionDebugHandler(AlterTableActionDebugGUIHandler handler); //! Outputs string @a text to the GUI debugger (specialized for the Alter Table feature); //! @a nestingLevel can be provided for nested outputs. KDB_EXPORT void alterTableActionDebugGUI(const QString& text, int nestingLevel = 0); #endif //! @return @a string if it is not empty, else returns @a stringIfEmpty. /*! This function is an optimization in cases when @a string is a result of expensive functioncall because any evaluation will be performed once, not twice. Another advantage is simpified code through the functional approach. The function expects bool isEmpty() method to be present in type T, so T can typically be QString or QByteArray. */ template T iifNotEmpty(const T &string, const T &stringIfEmpty) { return string.isEmpty() ? stringIfEmpty : string; } //! @overload iifNotEmpty(const T &string, const T &stringIfEmpty) template T iifNotEmpty(const QByteArray &string, const T &stringIfEmpty) { return iifNotEmpty(QLatin1String(string), stringIfEmpty); } //! @overload iifNotEmpty(const T &string, const T &stringIfEmpty) template T iifNotEmpty(const T &string, const QByteArray &stringIfEmpty) { return iifNotEmpty(string, QLatin1String(stringIfEmpty)); } //! @return @a value if @a ok is true, else returns default value T(). template T iif(bool ok, const T &value) { if (ok) { return value; } return T(); } /*! @return a list of paths that KDb will search when dynamically loading libraries (plugins) This is basicaly list of directories returned QCoreApplication::libraryPaths() that have readable subdirectory "kdb". @see QCoreApplication::libraryPaths() */ KDB_EXPORT QStringList libraryPaths(); #endif void KDbTest::testTemporaryTableName() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); QString baseName = QLatin1String("foobar"); QString tempName1 = KDb::temporaryTableName(utils.connection.data(), baseName); QVERIFY(!tempName1.isEmpty()); QVERIFY(tempName1.contains(baseName)); QString tempName2 = KDb::temporaryTableName(utils.connection.data(), baseName); QVERIFY(!tempName2.isEmpty()); QVERIFY(tempName2.contains(baseName)); QVERIFY(tempName1 != tempName2); utils.connection->closeDatabase(); QTest::ignoreMessage(QtWarningMsg, "Missing database handle"); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("!executeQuery().*")); QString tempName = KDb::temporaryTableName(utils.connection.data(), baseName); QVERIFY2(tempName.isEmpty(), "Temporary name should not be created when database is closed "); utils.connection->disconnect(); QTest::ignoreMessage(QtWarningMsg, "Missing database handle"); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("!executeQuery().*")); tempName = KDb::temporaryTableName(utils.connection.data(), baseName); QVERIFY2(tempName.isEmpty(), "Temporary name should not be created connection is missing"); utils.connection->dropDatabase(utils.connection->data().databaseName()); } //! @todo add tests #if 0 /*! @return absolute path to "sqlite3" program. Empty string is returned if the program was not found. */ KDB_EXPORT QString sqlite3ProgramPath(); /*! Imports file in SQL format from @a inputFileName into @a outputFileName. Works for any SQLite 3 dump file. Requires access to executing the "sqlite3" command. File named @a outputFileName will be silently overwritten with a new SQLite 3 database file. @return true on success. */ KDB_EXPORT bool importSqliteFile(const QString &inputFileName, const QString &outputFileName); /*! @return @c true if @a s is a valid identifier, i.e. starts with a letter or '_' character and contains only letters, numbers and '_' character. */ KDB_EXPORT bool isIdentifier(const QString& s); /*! @return valid identifier based on @a s. Non-alphanumeric characters (or spaces) are replaced with '_'. If a number is at the beginning, '_' is added at start. Empty strings are not changed. Case remains unchanged. */ KDB_EXPORT QString stringToIdentifier(const QString &s); /*! @return useful message "Value of "valueName" column must be an identifier. "v" is not a valid identifier.". It is also used by KDbIdentifierValidator. */ KDB_EXPORT QString identifierExpectedMessage(const QString &valueName, const QVariant& v); #endif void KDbTest::deleteRecordWithOneConstraintsTest() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "id", 2)); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "id", "3"), "Passing a valid Integer in String Format"); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "id", "Foo")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "name", "Jaroslaw")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "surname", "FooBar")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", 45)); // and empty data. KDbTableSchema *kdb_t = utils.connection.data()->tableSchema("persons"); QVERIFY(kdb_t); QVERIFY2(utils.connection.data()->insertRecord(kdb_t, 10, 20, QVariant(), "Bar"), "Inserting NULL data"); QVERIFY2(utils.connection.data()->insertRecord(kdb_t,15, 20, "", "Bar"), "Inserting empty data"); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "name", QString()), "Passing a null value instead of string"); // QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "name", ""), "Passing an empty string"); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", "Nitish")); QVERIFY(utils.testDisconnectAndDropDb()); } static QRegularExpression resultRegExp(const QString &code, const QString &message, const QString &sql, const QString &serverErrorCode, const QString &serverMessage) { return QRegularExpression( QString::fromLatin1("KDbResult: CODE=%1 MESSAGE=\\\"%2\\\" ERR_SQL=KDbEscapedString:" "\\\"%3\\\" SERVER_ERROR_CODE=%4 SERVER_MESSAGE=\\\"%5") .arg(code.isEmpty() ? "[0-9]*" : code, message, sql, serverErrorCode.isEmpty() ? "[0-9]*" : serverErrorCode, serverMessage)); } void KDbTest::deleteNonExistingRecordTest() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "id", 400)); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "name", "FooBar")); QTest::ignoreMessage(QtWarningMsg, resultRegExp("260", "Error while executing SQL statement.", "DELETE FROM \\[persons\\] WHERE \\[Foo\\]='FooBar'", "0", "no such column: Foo")); QVERIFY2(!KDb::deleteRecords(utils.connection.data(), "persons", "Foo", "FooBar"), "Passing a NonExisting Column - should fail because 'Foo' column does not exist, " "See also https://bugs.kde.org/376052"); QVERIFY(utils.testDisconnectAndDropDb()); } void KDbTest::deleteRecordWithTwoConstraintsTest() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "id", KDbField::Integer, 2, "age", KDbField::Integer, 60), "Both fields are INTEGER"); KDbTableSchema *kdb_t = utils.connection.data()->tableSchema("persons"); QVERIFY(kdb_t); utils.connection.data()->insertRecord(kdb_t, 10, QVariant(), "Foo", "Bar") ; QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "id", KDbField::Integer, 10, "age", KDbField::Integer, QVariant()), "Passing NULL value for integer field"); QVERIFY(utils.connection.data()->insertRecord(kdb_t, 20, QVariant(), QVariant(), "Bar")); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, QVariant(), "name", KDbField::Text, QVariant()), "Passing 2 NULL values"); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, 20, "name", KDbField::Text, "Jaroslaw"), "One argument is Integer and another is Text"); QVERIFY2(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, 20, "name", KDbField::Text, 56), "Two arguments, passing second integer instead of text but it is converted to text"); QTest::ignoreMessage(QtWarningMsg, resultRegExp("260", "Error while executing SQL statement.", "DELETE FROM \\[persons\\] WHERE \\[age\\]=TRAP AND \\[name\\]='56'", "0", "no such column: TRAP")); QVERIFY2(!KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, "TRAP", "name", KDbField::Text, 56), "Passing text instead of integer, conversion error expected"); QVERIFY(utils.testDisconnectAndDropDb()); } void KDbTest::deleteRecordWithThreeConstraintsTest() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); KDbTableSchema *kdb_t = utils.connection.data()->tableSchema("persons"); QVERIFY(kdb_t); //One null value. QVERIFY(utils.connection.data()->insertRecord(kdb_t, 10, QVariant(), "Foo", "Bar")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, QVariant(), "name", KDbField::Text, "Foo", "surname", KDbField::Text, "Bar")); //Mix of null and empty values QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, QVariant(), "name", KDbField::Text, "", "surname", KDbField::Text, "")); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer,27, "name", KDbField::Text, "Jaraslaw", "id", KDbField::Integer, 1)); QVERIFY(KDb::deleteRecords(utils.connection.data(), "persons", "age", KDbField::Integer, 60, "name", KDbField::Text, "Lech", "id", KDbField::Integer, 2)); QVERIFY(utils.testDisconnectAndDropDb()); } void KDbTest::deleteAllRecordsTest() { QVERIFY(utils.testCreateDbWithTables("KDbTest")); QVERIFY(KDb::deleteAllRecords(utils.connection.data(), "persons")); QRegularExpression deleteAllErrorRegExp = resultRegExp( - "", "Error while executing SQL statement.", "DELETE FROM \\[.*\\]", 0, "no such table: .*"); + "", "Error while executing SQL statement.", "DELETE FROM \\[.*\\]", "0", "no such table: .*"); QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp); QVERIFY2(!KDb::deleteAllRecords(utils.connection.data(), QString()), "Passing a null table name"); QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp); QVERIFY2(!KDb::deleteAllRecords(utils.connection.data(), ""), "Passing an empty table name"); QVERIFY(KDb::deleteAllRecords(utils.connection.data(), "cars")); QTest::ignoreMessage(QtWarningMsg, deleteAllErrorRegExp); QVERIFY2(!KDb::deleteAllRecords(utils.connection.data(), "NonExistingTable"), "Passing a nonexisting table name"); QVERIFY(utils.testDisconnectAndDropDb()); } void KDbTest::cleanupTestCase() { } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdc0e3f7..d9ef0526 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,467 +1,467 @@ # Private options (visible only within KDb) simple_option(KDB_EXPRESSION_DEBUG "Debugging of Expression classes" OFF) simple_option(KDB_DRIVERMANAGER_DEBUG "Debugging of the Driver Manager class" OFF) simple_option(KDB_TRANSACTIONS_DEBUG "Debugging of the Transaction class" OFF) simple_option(KDB_TABLESCHEMACHANGELISTENER_DEBUG "Debugging of the KDbTableSchemaChangeListener class" OFF) # Public options (affecting public behavior or contents of KDb) simple_option(KDB_DEBUG_GUI "GUI for debugging" OFF) # NOTE: always add public options to KDbConfig.cmake.in as well include(CheckIncludeFile) check_include_file(unistd.h HAVE_UNISTD_H) #add_definitions( # TODO -DKDE_DEFAULT_DEBUG_AREA=44000 #) ########### generate parser/lexer files ############### # as described at https://public.kitware.com/pipermail/cmake/2002-September/003028.html # Create target for the parser add_custom_target(parser echo "Creating parser/lexer files") set(PARSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/parser) # Create custom command for flex/lex (note the outputs) # TODO(GEN) uncomment GENERATED if we ever use this patch: https://phabricator.kde.org/D357 "No more generated parser/scanner files in the source dir" add_custom_command( TARGET parser COMMAND ${PARSER_SOURCE_DIR}/generate_parser_code.sh DEPENDS ${PARSER_SOURCE_DIR}/KDbSqlParser.y ${PARSER_SOURCE_DIR}/KDbSqlScanner.l ${PARSER_SOURCE_DIR}/generate_parser_code.sh OUTPUT #TODO(GEN) ${PARSER_SOURCE_DIR}/generated/sqlparser.h #TODO(GEN) ${PARSER_SOURCE_DIR}/generated/sqlparser.cpp #TODO(GEN) ${PARSER_SOURCE_DIR}/generated/sqlscanner.cpp #TODO(GEN) ${PARSER_SOURCE_DIR}/generated/KDbToken.h #TODO(GEN) ${PARSER_SOURCE_DIR}/generated/KDbToken.cpp ) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if("${CMAKE_BUILD_TYPE_LOWER}" MATCHES "debug") add_definitions(-DYYDEBUG=1) # needed where sqlparser.h is used endif() if(NOT HAVE_UNISTD_H) set(EXTRA_SCANNER_COMPILE_FLAGS "-DYY_NO_UNISTD_H=1") endif() if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) - set(EXTRA_SCANNER_COMPILE_FLAGS "${EXTRA_SCANNER_COMPILE_FLAGS} -Wno-sign-compare -Wno-unused-function -Wno-deprecated-register") + set(EXTRA_SCANNER_COMPILE_FLAGS "${EXTRA_SCANNER_COMPILE_FLAGS} -Wno-sign-compare -Wno-unused-function -Wno-deprecated-register -Wno-zero-as-null-pointer-constant") elseif(MSVC) set(EXTRA_SCANNER_COMPILE_FLAGS "${EXTRA_SCANNER_COMPILE_FLAGS} /wd4018") # disable warning C4018: '<' : signed/unsigned mismatch endif() # Mark files as generated, set compile flags set_source_files_properties(${PARSER_SOURCE_DIR}/generated/sqlparser.cpp PROPERTIES #TODO(GEN) GENERATED TRUE SKIP_AUTOMOC ON # YYERROR_VERBOSE=1 needed to get a token table for tokenName() even for release builds COMPILE_FLAGS "-DYYERROR_VERBOSE=1 ${EXTRA_PARSER_COMPILE_FLAGS} " ) # TODO(GEN) set_source_files_properties(${PARSER_SOURCE_DIR}/generated/sqlparser.h PROPERTIES GENERATED TRUE) # TODO(GEN) set_source_files_properties(${PARSER_SOURCE_DIR}/generated/KDbToken.h PROPERTIES GENERATED TRUE) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/KDbConnectionData_sdc.cpp PROPERTIES GENERATED TRUE SKIP_AUTOMOC ON ) set_source_files_properties( ${PARSER_SOURCE_DIR}/generated/sqlscanner.cpp PROPERTIES #TODO(GEN) GENERATED TRUE SKIP_AUTOMOC ON COMPILE_FLAGS "${EXTRA_SCANNER_COMPILE_FLAGS} " ) set(kdb_LIB_SRCS parser/generated/sqlscanner.cpp parser/generated/sqlparser.cpp parser/generated/KDbToken.cpp parser/KDbParser.cpp parser/KDbParser_p.cpp parser/KDbSqlParser.y parser/KDbSqlScanner.l parser/generate_parser_code.sh parser/extract_tokens.sh parser/TODO tools/KDbJsonTrader_p.cpp # mostly copied from KReport's KReportJsonTrader_p.cpp tools/KDbValidator.cpp tools/KDbFieldValidator.cpp tools/KDbLongLongValidator.cpp tools/KDbObjectNameValidator.cpp tools/KDbIdentifierValidator.cpp tools/KDbUtils.cpp #TODO tools/debuggui.cpp #TODO tools/KDbSimpleCommandLineApp.cpp tools/transliteration/transliteration_table.cpp tools/transliteration/generate_transliteration_table.sh tools/transliteration/transliteration_table.readme KDbEscapedString.cpp KDbResult.cpp KDbQueryAsterisk.cpp KDbConnectionData.cpp KDbVersionInfo.cpp ${CMAKE_CURRENT_BINARY_DIR}/KDbConnectionData_sdc.cpp KDbField.cpp KDbQuerySchemaParameter.cpp expression/KDbExpression.cpp expression/KDbNArgExpression.cpp expression/KDbUnaryExpression.cpp expression/KDbBinaryExpression.cpp expression/KDbConstExpression.cpp expression/KDbQueryParameterExpression.cpp expression/KDbVariableExpression.cpp expression/KDbFunctionExpression.cpp KDbFieldList.cpp KDbTableSchema.cpp KDbTableSchemaChangeListener.cpp KDbIndexSchema.cpp KDbOrderByColumn.cpp KDbQuerySchema.cpp KDbQuerySchema_p.cpp KDbQueryColumnInfo.cpp KDbTableOrQuerySchema.cpp KDbDriverManager.cpp KDbDriver.cpp KDbDriver_p.cpp KDbDriverMetaData.cpp KDbConnection.cpp KDbConnectionProxy.cpp generated/sqlkeywords.cpp KDbObject.cpp KDb.cpp KDbRecordData.cpp KDbCursor.cpp KDbTransaction.cpp KDbGlobal.cpp KDbRelationship.cpp KDbRecordEditBuffer.cpp KDbMessageHandler.cpp KDbPreparedStatement.cpp KDbProperties.cpp KDbAdmin.cpp KDbLookupFieldSchema.cpp KDbAlter.cpp KDbNativeStatementBuilder.cpp kdb_debug.cpp views/KDbTableViewData.cpp views/KDbTableViewColumn.cpp views/chartable.txt sql/KDbSqlField.cpp sql/KDbSqlRecord.cpp sql/KDbSqlResult.cpp # private: tools/KDbUtils_p.h # non-source: Mainpage.dox Messages.sh ) ecm_create_qm_loader(kdb_LIB_SRCS kdb_qt) add_library(KDb SHARED ${kdb_LIB_SRCS}) set_coinstallable_lib_version(KDb) kdb_create_shared_data_classes( kdb_GENERATED_SHARED_DATA_CLASS_HEADERS # output variable with list of headers NO_PREFIX # subdirectory in which the headers should be generated KDbConnectionData.shared.h KDbObject.shared.h KDbQuerySchemaParameter.shared.h KDbResult.shared.h KDbSelectStatementOptions.shared.h KDbVersionInfo.shared.h ) kdb_remove_extensions( kdb_GENERATED_SHARED_DATA_CLASS_BASENAMES ${kdb_GENERATED_SHARED_DATA_CLASS_HEADERS} ) #message(STATUS "kdb_GENERATED_SHARED_DATA_CLASS_HEADERS: ${kdb_GENERATED_SHARED_DATA_CLASS_HEADERS}") #add_dependencies(KDb _shared_classes) # generate shared classes before they can be used in KDb generate_export_header(KDb) set(kdb_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/expression ${CMAKE_CURRENT_SOURCE_DIR}/interfaces ${CMAKE_CURRENT_SOURCE_DIR}/parser ${CMAKE_CURRENT_SOURCE_DIR}/parser/generated ${CMAKE_CURRENT_SOURCE_DIR}/sql ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/views ) target_include_directories(KDb PUBLIC "$" INTERFACE "$" PRIVATE ${ICU_INCLUDE_DIRS} ) target_link_libraries(KDb PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets KF5::CoreAddons PRIVATE Qt5::Xml ${ICU_I18N_LIBRARY} ) if(BUILD_TEST_COVERAGE) target_link_libraries(KDb PRIVATE gcov ) endif() # Create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/${KDB_BASE_NAME}") ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX KDB SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdb_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDbConfigVersion.cmake" ) install(TARGETS KDb EXPORT KDbTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KDbConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KDbConfig.cmake" INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_DIR}" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDbConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KDbConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel) install(EXPORT KDbTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KDbTargets.cmake) ecm_generate_pri_file( BASE_NAME ${KDB_BASE_NAME} LIB_NAME ${KDB_BASE_NAME} DEPS "widgets xml" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDB_INCLUDE_INSTALL_DIR} ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kdb_version.h" DESTINATION "${KDB_INCLUDE_INSTALL_DIR}" COMPONENT Devel) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE HEADER_NAMES KDb KDbAdmin KDbAlter KDbQueryAsterisk KDbConnection KDbConnectionOptions KDbConnectionProxy KDbCursor KDbDriver KDbDriverBehavior KDbDriverManager KDbDriverMetaData KDbError KDbEscapedString KDbField KDbFieldList KDbGlobal KDbIndexSchema KDbLookupFieldSchema KDbMessageHandler KDbNativeStatementBuilder KDbPreparedStatement KDbProperties KDbQueryColumnInfo KDbOrderByColumn KDbQuerySchema KDbRecordData KDbRecordEditBuffer KDbRelationship KDbTableOrQuerySchema KDbTableSchema KDbTableSchemaChangeListener KDbTransaction KDbTransactionData KDbTransactionGuard ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE expression HEADER_NAMES KDbExpression KDbExpressionData ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE interfaces HEADER_NAMES KDbPreparedStatementInterface ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE parser HEADER_NAMES KDbParser ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE parser/generated HEADER_NAMES KDbToken ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE sql HEADER_NAMES KDbSqlField KDbSqlRecord KDbSqlResult KDbSqlString ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE views HEADER_NAMES KDbTableViewData KDbTableViewColumn ) ecm_generate_headers(kdb_FORWARDING_HEADERS REQUIRED_HEADERS kdb_HEADERS ORIGINAL CAMELCASE RELATIVE tools HEADER_NAMES KDbValidator KDbUtils KDbTristate #todo KDbSimpleCommandLineApp KDbLongLongValidator KDbIdentifierValidator KDbFieldValidator KDbObjectNameValidator ) #message(STATUS "%% ${kdb_GENERATED_SHARED_DATA_CLASS_BASENAMES}") ecm_generate_headers(kdb_FORWARDING_HEADERS_FROM_BUILDDIR REQUIRED_HEADERS kdb_HEADERS_FROM_BUILDDIR ORIGINAL CAMELCASE SOURCE_DIR ${PROJECT_BINARY_DIR}/src HEADER_NAMES ${kdb_GENERATED_SHARED_DATA_CLASS_BASENAMES} ) #message(STATUS "%%kdb_HEADERS_FROM_BUILDDIR ${kdb_HEADERS_FROM_BUILDDIR}") install( FILES ${kdb_HEADERS} ${kdb_HEADERS_FROM_BUILDDIR} DESTINATION ${KDB_INCLUDE_INSTALL_DIR} COMPONENT Devel ) install( FILES ${kdb_FORWARDING_HEADERS} ${kdb_FORWARDING_HEADERS_FROM_BUILDDIR} ${PROJECT_BINARY_DIR}/src/kdb_export.h ${PROJECT_BINARY_DIR}/src/config-kdb.h DESTINATION ${KDB_INCLUDE_INSTALL_DIR} COMPONENT Devel ) # KDb/Private includes # install( FILES # Connection_p.h # Driver_p.h # DESTINATION ${KDB_INCLUDE_INSTALL_DIR}/Private COMPONENT Devel # ) # KDb/Interfaces includes # install( FILES # Interfaces/KDbPreparedStatementInterface.h includes/KDb/Interfaces/KDbPreparedStatementInterface # DESTINATION ${KDB_INCLUDE_INSTALL_DIR}/Interfaces COMPONENT Devel # ) if(BUILD_QCH) kdb_add_qch( KDb_QCH NAME KDb BASE_NAME ${KDB_BASE_NAME} VERSION ${PROJECT_VERSION} NAMESPACE org.kde.${KDB_BASE_NAME} SOURCES Mainpage.dox ${kdb_HEADERS} ${kdb_HEADERS_FROM_BUILDDIR} LINK_QCHS Qt5Core_QCH Qt5Gui_QCH Qt5Widgets_QCH KF5CoreAddons_QCH BLANK_MACROS KDB_EXPORT KDB_DEPRECATED TAGFILE_INSTALL_DESTINATION ${KDB_QTQCH_FULL_INSTALL_DIR} QCH_INSTALL_DESTINATION ${KDB_QTQCH_FULL_INSTALL_DIR} ) set(kdb_qch_targets KDb_QCH) endif() kdb_install_qch_export( TARGETS ${kdb_qch_targets} FILE KDbQCHTargets.cmake ​ DESTINATION "${CMAKECONFIG_INSTALL_DIR}" ​ COMPONENT Devel ​) add_subdirectory(drivers) enable_testing() configure_file(config-kdb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kdb.h) diff --git a/src/KDbFieldList.cpp b/src/KDbFieldList.cpp index 111d76f5..5864f25e 100644 --- a/src/KDbFieldList.cpp +++ b/src/KDbFieldList.cpp @@ -1,408 +1,408 @@ /* This file is part of the KDE project Copyright (C) 2003-2012 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbFieldList.h" #include "KDbConnection.h" #include "kdb_debug.h" class Q_DECL_HIDDEN KDbFieldList::Private { public: Private() { } ~Private() { delete autoincFields; } //! Clear cached autoinc fields list void clearAutoincFields() { delete autoincFields; autoincFields = nullptr; } bool renameFieldInternal(KDbField *field, const QString& newNameLower) { if (fieldsByName.value(newNameLower)) { kdbWarning() << "Field" << newNameLower << "already exists"; return false; } fieldsByName.remove(field->name().toLower()); field->setName(newNameLower); fieldsByName.insert(newNameLower, field); return true; } KDbField::List fields; //!< Fields collected by name. Not used by KDbQuerySchema. QHash fieldsByName; KDbField::List *autoincFields = nullptr; //! cached KDbEscapedString sqlFields; }; //------------------------------------------------------- KDbFieldList::KDbFieldList(bool owner) : d(new Private) { d->fields.setAutoDelete(owner); } //! @todo IMPORTANT: (API) improve deepCopyFields KDbFieldList::KDbFieldList(const KDbFieldList& fl, bool deepCopyFields) : KDbFieldList(fl.d->fields.autoDelete()) { if (deepCopyFields) { //deep copy for the fields for (KDbField *origField : *fl.fields()) { KDbField *f = origField->copy(); if (origField->parent() == &fl) { f->setParent(this); } const bool addFieldOk = addField(f); Q_ASSERT(addFieldOk); } } } KDbFieldList::~KDbFieldList() { delete d; } KDbField::List *KDbFieldList::fields() { return &d->fields; } const KDbField::List* KDbFieldList::fields() const { return &d->fields; } int KDbFieldList::fieldCount() const { return d->fields.count(); } bool KDbFieldList::isEmpty() const { return d->fields.isEmpty(); } void KDbFieldList::clear() { d->fieldsByName.clear(); d->clearAutoincFields(); d->fields.clear(); d->sqlFields.clear(); } bool KDbFieldList::insertField(int index, KDbField *field) { if (!field) { return false; } if (index > d->fields.count()) { kdbWarning() << "index (" << index << ") out of range"; return false; } d->fields.insert(index, field); if (!field->name().isEmpty()) { d->fieldsByName.insert(field->name().toLower(), field); } d->sqlFields.clear(); d->clearAutoincFields(); return true; } bool KDbFieldList::renameField(const QString& oldName, const QString& newName) { KDbField *field = d->fieldsByName.value(oldName.toLower()); if (!field) { kdbWarning() << "Fiels" << oldName << "not found"; return false; } return d->renameFieldInternal(field, newName.toLower()); } bool KDbFieldList::renameField(KDbField *field, const QString& newName) { if (!field || field != d->fieldsByName.value(field->name().toLower())) { kdbWarning() << "No field found" << QString::fromLatin1("\"%1\"").arg(field ? field->name() : QString()); return false; } return d->renameFieldInternal(field, newName.toLower()); } bool KDbFieldList::addField(KDbField *field) { return insertField(d->fields.count(), field); } bool KDbFieldList::removeField(KDbField *field) { if (!field) { return false; } if (d->fieldsByName.remove(field->name().toLower()) < 1) { return false; } d->fields.removeAt(d->fields.indexOf(field)); d->sqlFields.clear(); d->clearAutoincFields(); return true; } bool KDbFieldList::moveField(KDbField *field, int newIndex) { if (!field || !d->fields.removeOne(field)) { return false; } if (newIndex > d->fields.count()) { newIndex = d->fields.count(); } d->fields.insert(newIndex, field); d->sqlFields.clear(); d->clearAutoincFields(); return true; } KDbField* KDbFieldList::field(int id) { return d->fields.value(id); } const KDbField* KDbFieldList::field(int id) const { return d->fields.value(id); } KDbField* KDbFieldList::field(const QString& name) { return d->fieldsByName.value(name.toLower()); } const KDbField* KDbFieldList::field(const QString& name) const { return d->fieldsByName.value(name.toLower()); } bool KDbFieldList::hasField(const KDbField& field) const { return d->fields.contains(const_cast(&field)); } int KDbFieldList::indexOf(const KDbField& field) const { return d->fields.indexOf(const_cast(&field)); } KDB_EXPORT QDebug operator<<(QDebug dbg, const KDbFieldList& list) { if (list.fields()->isEmpty()) dbg.nospace() << ""; bool start = true; foreach(const KDbField *field, *list.fields()) { if (!start) dbg.nospace() << '\n'; else start = false; dbg.nospace() << " - " << *field; } return dbg.space(); } #define _ADD_FIELD(fname) \ { \ if (fname.isEmpty()) return fl; \ KDbField *f = d->fieldsByName.value(fname.toLower()); \ - if (!f || !fl->addField(f)) { kdbWarning() << subListWarning1(fname); delete fl; return 0; } \ + if (!f || !fl->addField(f)) { kdbWarning() << subListWarning1(fname); delete fl; return nullptr; } \ } static QString subListWarning1(const QString& fname) { return QString::fromLatin1("could not find field \"%1\"").arg(fname); } KDbFieldList* KDbFieldList::subList(const QString& n1, const QString& n2, const QString& n3, const QString& n4, const QString& n5, const QString& n6, const QString& n7, const QString& n8, const QString& n9, const QString& n10, const QString& n11, const QString& n12, const QString& n13, const QString& n14, const QString& n15, const QString& n16, const QString& n17, const QString& n18) { if (n1.isEmpty()) return nullptr; KDbFieldList *fl = new KDbFieldList(false); _ADD_FIELD(n1); _ADD_FIELD(n2); _ADD_FIELD(n3); _ADD_FIELD(n4); _ADD_FIELD(n5); _ADD_FIELD(n6); _ADD_FIELD(n7); _ADD_FIELD(n8); _ADD_FIELD(n9); _ADD_FIELD(n10); _ADD_FIELD(n11); _ADD_FIELD(n12); _ADD_FIELD(n13); _ADD_FIELD(n14); _ADD_FIELD(n15); _ADD_FIELD(n16); _ADD_FIELD(n17); _ADD_FIELD(n18); return fl; } KDbFieldList* KDbFieldList::subList(const QStringList& list) { KDbFieldList *fl = new KDbFieldList(false); for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { _ADD_FIELD((*it)); } return fl; } #undef _ADD_FIELD #define _ADD_FIELD(fname) \ { \ if (fname.isEmpty()) return fl; \ KDbField *f = d->fieldsByName.value(QLatin1String(fname.toLower())); \ - if (!f || !fl->addField(f)) { kdbWarning() << subListWarning1(QLatin1String(fname)); delete fl; return 0; } \ + if (!f || !fl->addField(f)) { kdbWarning() << subListWarning1(QLatin1String(fname)); delete fl; return nullptr; } \ } KDbFieldList* KDbFieldList::subList(const QList& list) { KDbFieldList *fl = new KDbFieldList(false); for (QList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { _ADD_FIELD((*it)); } return fl; } #undef _ADD_FIELD KDbFieldList* KDbFieldList::subList(const QList& list) { QScopedPointer fl(new KDbFieldList(false)); foreach(int index, list) { KDbField *f = field(index); if (!f) { kdbWarning() << QString::fromLatin1("could not find field at position %1").arg(index); return nullptr; } if (!fl->addField(f)) { kdbWarning() << QString::fromLatin1("could not add field at position %1").arg(index); return nullptr; } } return fl.take(); } QStringList KDbFieldList::names() const { QStringList r; for (KDbField *f : d->fields) { r += f->name().toLower(); } return r; } KDbField::ListIterator KDbFieldList::fieldsIterator() const { return d->fields.constBegin(); } KDbField::ListIterator KDbFieldList::fieldsIteratorConstEnd() const { return d->fields.constEnd(); } bool KDbFieldList::isOwner() const { return d->fields.autoDelete(); } //static KDbEscapedString KDbFieldList::sqlFieldsList(const KDbField::List& list, KDbConnection *conn, const QString& separator, const QString& tableOrAlias, KDb::IdentifierEscapingType escapingType) { KDbEscapedString result; result.reserve(256); bool start = true; QString tableOrAliasAndDot; if (!tableOrAlias.isEmpty()) { tableOrAliasAndDot = ((conn && escapingType == KDb::DriverEscaping) ? conn->escapeIdentifier(tableOrAlias) : KDb::escapeIdentifier(tableOrAlias)) + QLatin1Char('.'); } foreach(KDbField *f, list) { if (!start) result.append(separator); else start = false; result = (result + tableOrAliasAndDot + ((conn && escapingType == KDb::DriverEscaping) ? conn->escapeIdentifier(f->name()) : KDb::escapeIdentifier(f->name())) ); } return result; } KDbEscapedString KDbFieldList::sqlFieldsList(KDbConnection *conn, const QString& separator, const QString& tableOrAlias, KDb::IdentifierEscapingType escapingType) const { if (!d->sqlFields.isEmpty()) return d->sqlFields; d->sqlFields = KDbFieldList::sqlFieldsList(d->fields, conn, separator, tableOrAlias, escapingType); return d->sqlFields; } KDbField::List* KDbFieldList::autoIncrementFields() const { if (d->autoincFields) return d->autoincFields; d->autoincFields = new KDbField::List(false); for (KDbField *f : d->fields) { if (f->isAutoIncrement()) { d->autoincFields->append(f); } } return d->autoincFields; } diff --git a/src/expression/KDbFunctionExpression.cpp b/src/expression/KDbFunctionExpression.cpp index b3390519..40a5ae2c 100644 --- a/src/expression/KDbFunctionExpression.cpp +++ b/src/expression/KDbFunctionExpression.cpp @@ -1,1414 +1,1414 @@ /* This file is part of the KDE project Copyright (C) 2003-2016 Jarosław Staniek Based on nexp.cpp : Parser module of Python-like language (C) 2001 Jarosław Staniek, MIMUW (www.mimuw.edu.pl) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbExpression.h" #include "KDb.h" #include "KDbQuerySchema.h" #include "KDbDriver.h" #include "KDbParser.h" #include "KDbParser_p.h" #include "kdb_debug.h" #include #include #include // Enable to add SQLite-specific functions //#define KDB_ENABLE_SQLITE_SPECIFIC_FUNCTIONS //! A set of names of aggregation SQL functions class BuiltInAggregates { public: BuiltInAggregates() : data({ QStringLiteral("SUM"), QStringLiteral("MIN"), QStringLiteral("MAX"), QStringLiteral("AVG"), QStringLiteral("COUNT"), QStringLiteral("STD"), QStringLiteral("STDDEV"), QStringLiteral("VARIANCE") }) { } const QSet data; }; Q_GLOBAL_STATIC(BuiltInAggregates, _builtInAggregates) //! Type of a single function argument, used with KDbField::Type values. //! Used to indicate that multiple types are allowed. enum BuiltInFunctionArgumentType { AnyText = KDbField::LastType + 1, AnyInt, AnyFloat, AnyNumber, Any }; //! @return any concrete type matching rule @a argType static KDbField::Type anyMatchingType(int argType) { if (argType == AnyText || argType == Any) { return KDbField::Text; } else if (argType == AnyInt || argType == AnyNumber) { return KDbField::Integer; } else if (argType == AnyFloat) { return KDbField::Double; } return KDbField::InvalidType; } //! Declaration of a single built-in function. It can offer multiple signatures. class BuiltInFunctionDeclaration { public: inline BuiltInFunctionDeclaration() : defaultReturnType(KDbField::InvalidType), copyReturnTypeFromArg(-1) { } virtual ~BuiltInFunctionDeclaration() {} virtual KDbField::Type returnType(const KDbFunctionExpressionData* f, KDbParseInfo* parseInfo) const { Q_UNUSED(parseInfo); const KDbNArgExpressionData *argsData = f->args.constData()->convertConst(); if (argsData->containsNullArgument()) { return KDbField::Null; } if (copyReturnTypeFromArg >= 0 && copyReturnTypeFromArg < argsData->children.count()) { KDbQueryParameterExpressionData *queryParameterExpressionData = argsData->children.at(copyReturnTypeFromArg) ->convert(); if (queryParameterExpressionData) { // Set query parameter type (if there are any) to deduced result type //! @todo Most likely but can be also other type for (size_t i = 0; i < signatures.size(); ++i) { int** signature = signatures[i]; const KDbField::Type t = anyMatchingType(signature[copyReturnTypeFromArg][0]); if (t != KDbField::InvalidType) { queryParameterExpressionData->m_type = t; return t; } } } return argsData->children.at(copyReturnTypeFromArg)->type(); } return defaultReturnType; } std::vector signatures; protected: KDbField::Type defaultReturnType; int copyReturnTypeFromArg; friend class BuiltInFunctions; private: Q_DISABLE_COPY(BuiltInFunctionDeclaration) }; //! Declaration of a single built-in function COALESCE() and similar ones. class CoalesceFunctionDeclaration : public BuiltInFunctionDeclaration { public: CoalesceFunctionDeclaration() {} KDbField::Type returnType(const KDbFunctionExpressionData* f, KDbParseInfo* parseInfo) const override { Q_UNUSED(parseInfo); // Find type //! @todo Most likely but can be also other type KDbField::Type t = KDbField::Integer; const KDbNArgExpressionData *argsData = f->args.constData()->convertConst(); foreach(const ExplicitlySharedExpressionDataPointer &expr, argsData->children) { KDbQueryParameterExpressionData *queryParameterExpressionData = expr->convert(); const KDbField::Type currentType = expr->type(); if (!queryParameterExpressionData && currentType != KDbField::Null) { t = currentType; break; } } foreach(const ExplicitlySharedExpressionDataPointer &expr, argsData->children) { KDbQueryParameterExpressionData *queryParameterExpressionData = expr->convert(); if (queryParameterExpressionData) { // Set query parameter type (if there are any) to deduced result type queryParameterExpressionData->m_type = t; } } return t; } private: Q_DISABLE_COPY(CoalesceFunctionDeclaration) }; //! Declaration of a single built-in function MIN(), MAX() and similar ones. //! Its return type is: //! - NULL if any argument is NULL //! - valid type if types of all arguments are compatible (e.g. text, numeric, date...) //! - InvalidType if types of any two are incompatible class MinMaxFunctionDeclaration : public BuiltInFunctionDeclaration { Q_DECLARE_TR_FUNCTIONS(MinMaxFunctionDeclaration) public: MinMaxFunctionDeclaration() {} KDbField::Type returnType(const KDbFunctionExpressionData* f, KDbParseInfo* parseInfo) const override { const KDbNArgExpressionData *argsData = f->args.constData()->convertConst(); if (argsData->children.isEmpty()) { return KDbField::Null; } const KDbField::Type type0 = argsData->children.at(0)->type(); // cache: evaluating type of expressions can be expensive if (nullOrInvalid(type0)) { return type0; } KDbField::TypeGroup prevTg = KDbField::typeGroup(type0); // use typegroup for simplicity bool prevTgIsAny = argsData->children.at(0)->convertConst(); for(int i = 1; i < argsData->children.count(); ++i) { const ExplicitlySharedExpressionDataPointer expr = argsData->children.at(i); const KDbField::Type t = expr->type(); if (nullOrInvalid(t)) { return t; } const KDbField::TypeGroup tg = KDbField::typeGroup(t); const bool tgIsAny = argsData->children.at(i)->convertConst(); if (prevTgIsAny) { if (!tgIsAny) { // no longer "Any" (query parameter) prevTgIsAny = false; prevTg = tg; } continue; } else if (tgIsAny) { continue; // use previously found concrete type } if ((prevTg == KDbField::IntegerGroup || prevTg == KDbField::FloatGroup) && (tg == KDbField::IntegerGroup || tg == KDbField::FloatGroup)) { if (prevTg == KDbField::IntegerGroup && tg == KDbField::FloatGroup) { prevTg = KDbField::FloatGroup; // int -> float } continue; } if (prevTg == tg) { continue; } if (parseInfo) { parseInfo->setErrorMessage( tr("Incompatible types in %1() function").arg(f->name)); parseInfo->setErrorDescription( tr("Argument #%1 of type \"%2\" in function %3() is not " "compatible with previous arguments of type \"%4\".") .arg(i+1) .arg(KDbField::typeName(simpleTypeForGroup(tg)), f->name, KDbField::typeName(simpleTypeForGroup(prevTg)))); } return KDbField::InvalidType; } if (prevTgIsAny) { //! @todo Most likely Integer but can be also Float/Double/Text/Date... return KDbField::Integer; } const KDbField::Type resultType = safeTypeForGroup(prevTg); // Set query parameter types (if there are any) to deduced result type for(ExplicitlySharedExpressionDataPointer expr : argsData->children) { KDbQueryParameterExpressionData *queryParameterExpressionData = expr->convert(); if (queryParameterExpressionData) { queryParameterExpressionData->m_type = resultType; } } return resultType; } private: static bool nullOrInvalid(KDbField::Type type) { return type == KDbField::Null || type == KDbField::InvalidType; } //! @return safe default type for type group @a tg (too big sizes better than too small) static KDbField::Type safeTypeForGroup(KDbField::TypeGroup tg) { switch (tg) { case KDbField::TextGroup: return KDbField::LongText; case KDbField::IntegerGroup: return KDbField::BigInteger; case KDbField::FloatGroup: return KDbField::Double; case KDbField::BooleanGroup: return KDbField::Boolean; case KDbField::DateTimeGroup: return KDbField::DateTime; case KDbField::BLOBGroup: return KDbField::BLOB; default: break; } return KDbField::InvalidType; } //! @return resonable default type for type group @a tg (used for displaying in error message) static KDbField::Type simpleTypeForGroup(KDbField::TypeGroup tg) { switch (tg) { case KDbField::TextGroup: return KDbField::Text; case KDbField::IntegerGroup: return KDbField::Integer; case KDbField::FloatGroup: return KDbField::Double; case KDbField::BooleanGroup: return KDbField::Boolean; case KDbField::DateTimeGroup: return KDbField::DateTime; case KDbField::BLOBGroup: return KDbField::BLOB; default: break; } return KDbField::InvalidType; } Q_DISABLE_COPY(MinMaxFunctionDeclaration) }; //! Declaration of a single built-in function RANDOM() and RANDOM(X,Y). //! Its return type is: //! - Double when number of arguments is zero //! - integer if there are two integer arguments (see KDb::maximumForIntegerFieldTypes()) //! - InvalidType for other number of arguments class RandomFunctionDeclaration : public BuiltInFunctionDeclaration { Q_DECLARE_TR_FUNCTIONS(RandomFunctionDeclaration) public: RandomFunctionDeclaration() {} KDbField::Type returnType(const KDbFunctionExpressionData* f, KDbParseInfo* parseInfo) const override { const KDbNArgExpressionData *argsData = f->args.constData()->convertConst(); if (argsData->children.isEmpty()) { return KDbField::Double; } if (argsData->children.count() == 2) { const KDbConstExpressionData *const0 = argsData->children.at(0)->convertConst(); const KDbConstExpressionData *const1 = argsData->children.at(1)->convertConst(); if (const0 && const1) { bool ok0; const qlonglong val0 = const0->value.toLongLong(&ok0); bool ok1; const qlonglong val1 = const1->value.toLongLong(&ok1); if (ok0 && ok1) { if (val0 >= val1) { if (parseInfo) { parseInfo->setErrorMessage( tr("Invalid arguments of %1() function").arg(f->name)); parseInfo->setErrorDescription( tr("Value of the first argument should be less than " "value of the second argument.")); } return KDbField::InvalidType; } } } KDbField::Type t0; KDbField::Type t1; // deduce query parameter types KDbQueryParameterExpressionData *queryParameterExpressionData0 = argsData->children.at(0)->convert(); KDbQueryParameterExpressionData *queryParameterExpressionData1 = argsData->children.at(1)->convert(); if (queryParameterExpressionData0 && queryParameterExpressionData1) { queryParameterExpressionData0->m_type = KDbField::Integer; queryParameterExpressionData1->m_type = KDbField::Integer; t0 = KDbField::Integer; t1 = KDbField::Integer; } else if (queryParameterExpressionData0 && !queryParameterExpressionData1) { queryParameterExpressionData0->m_type = KDbField::Integer; t0 = queryParameterExpressionData0->m_type; t1 = argsData->children.at(1)->type(); } else if (!queryParameterExpressionData0 && queryParameterExpressionData1) { queryParameterExpressionData1->m_type = KDbField::Integer; t0 = argsData->children.at(0)->type(); t1 = queryParameterExpressionData1->m_type; } else { t0 = argsData->children.at(0)->type(); t1 = argsData->children.at(1)->type(); } return KDb::maximumForIntegerFieldTypes(t0, t1); } return KDbField::InvalidType; } private: Q_DISABLE_COPY(RandomFunctionDeclaration) }; //! Declaration of a single built-in function CEILING(X) and FLOOR(X). //! Its return type is: //! - integer if there are two integer arguments (see KDb::maximumForIntegerFieldTypes()) //! - InvalidType for other number of arguments class CeilingFloorFunctionDeclaration : public BuiltInFunctionDeclaration { public: CeilingFloorFunctionDeclaration() {} KDbField::Type returnType(const KDbFunctionExpressionData* f, KDbParseInfo* parseInfo) const override { Q_UNUSED(parseInfo); const KDbNArgExpressionData *argsData = f->args.constData()->convertConst(); if (argsData->children.count() == 1) { KDbQueryParameterExpressionData *queryParameterExpressionData = argsData->children.at(0)->convert(); if (queryParameterExpressionData) { // Set query parameter type (if there are any) to deduced result type //! @todo Most likely but can be also other type queryParameterExpressionData->m_type = KDbField::Double; return KDbField::BigInteger; } const KDbField::Type type = argsData->children.at(0)->type(); // cache: evaluating type of expressions can be expensive if (KDbField::isFPNumericType(type)) { return KDbField::BigInteger; } switch (type) { case KDbField::Byte: return KDbField::ShortInteger; case KDbField::ShortInteger: return KDbField::Integer; case KDbField::Integer: return KDbField::BigInteger; case KDbField::Null: return KDbField::Null; case KDbField::InvalidType: return KDbField::InvalidType; default:; } } return KDbField::InvalidType; } private: Q_DISABLE_COPY(CeilingFloorFunctionDeclaration) }; //! A map of built-in SQL functions //! See https://community.kde.org/Kexi/Plugins/Queries/SQL_Functions for the status. class BuiltInFunctions { public: BuiltInFunctions(); ~BuiltInFunctions() { qDeleteAll(m_functions); } //! @return function declaration's structure for name @a name //! If @a name is alias of the function, e.g. "MIN" for "LEAST", the original //! function's declaration is returned. BuiltInFunctionDeclaration* value(const QString &name) const; //! @return a list of function aliases. QStringList aliases() const; static int multipleArgs[]; private: QHash m_functions; QHash m_aliases; Q_DISABLE_COPY(BuiltInFunctions) }; int BuiltInFunctions::multipleArgs[] = { 0 }; BuiltInFunctions::BuiltInFunctions() { BuiltInFunctionDeclaration *decl; #define _TYPES(name, ...) static int name[] = { __VA_ARGS__, KDbField::InvalidType } _TYPES(argAnyTextOrNull, AnyText, KDbField::Null); _TYPES(argAnyIntOrNull, AnyInt, KDbField::Null); _TYPES(argAnyNumberOrNull, AnyNumber, KDbField::Null); _TYPES(argAnyFloatOrNull, AnyFloat, KDbField::Null); Q_UNUSED(argAnyFloatOrNull); _TYPES(argAnyOrNull, Any, KDbField::Null); _TYPES(argBLOBOrNull, KDbField::BLOB, KDbField::Null); Q_UNUSED(argBLOBOrNull); _TYPES(argAnyTextBLOBOrNull, AnyText, KDbField::BLOB, KDbField::Null); #undef _TYPES //! Adds a signature named @a name with specified arguments to declaration decl #define _SIG(name, ...) \ - static int* name[] = { __VA_ARGS__, 0 }; \ + static int* name[] = { __VA_ARGS__, nullptr }; \ decl->signatures.push_back(name) //! Adds a signature with no arguments to declaration decl #define _SIG0 \ decl->signatures.push_back(sig0) static int* sig0[] = { nullptr }; m_functions.insert(QLatin1String("ABS"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The abs(X) function returns the absolute value of the numeric argument X. Abs(X) returns NULL if X is NULL. Abs(X) returns 0.0 if X is a string or blob that cannot be converted to a numeric value. If X is the integer -9223372036854775808 then abs(X) throws an integer overflow error since there is no equivalent positive 64-bit two complement value. */ // example: SELECT ABS(-27), ABS(-3.1415), ABS(NULL + 1) // result: 27, 3.1415, NULL decl->copyReturnTypeFromArg = 0; _SIG(abs_1, argAnyNumberOrNull); m_functions.insert(QLatin1String("CEILING"), decl = new CeilingFloorFunctionDeclaration); /* ceiling(X) returns the largest integer value not less than X. */ // See also https://dev.mysql.com/doc/refman/5.1/en/mathematical-functions.html#function_ceiling // See also https://www.postgresql.org/docs/9.5/static/functions-math.html#FUNCTIONS-MATH-FUNC-TABLE // SQLite has no equivalent of ceiling() so this is used: // (CASE WHEN X = CAST(X AS INT) THEN CAST(X AS INT) WHEN X >= 0 THEN CAST(X AS INT) + 1 ELSE CAST(X AS INT) END) //! @todo add a custom function to SQLite to optimize/simplify things // example: SELECT CEILING(3.14), CEILING(-99.001) // result: 4, -99 _SIG(ceiling, argAnyNumberOrNull); m_functions.insert(QLatin1String("CHAR"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The char(X1,X2,...,XN) function returns a string composed of characters having the unicode code point values of integers X1 through XN, respectively. */ // example: SELECT CHAR(75,69,88,73), CHAR() // result: "KEXI" "" decl->defaultReturnType = KDbField::LongText; static int char_min_args[] = { 0 }; _SIG(char_N, argAnyIntOrNull, multipleArgs, char_min_args); m_functions.insert(QLatin1String("COALESCE"), decl = new CoalesceFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The coalesce() function returns a copy of its first non-NULL argument, or NULL if all arguments are NULL. Coalesce() must have at least 2 arguments. */ // example: SELECT COALESCE(NULL, 17, NULL, "A") // result: 17 static int coalesce_min_args[] = { 2 }; _SIG(coalesce_N, argAnyOrNull, multipleArgs, coalesce_min_args); m_functions.insert(QLatin1String("FLOOR"), decl = new CeilingFloorFunctionDeclaration); /* floor(X) returns the largest integer value not greater than X. */ // See also https://dev.mysql.com/doc/refman/5.1/en/mathematical-functions.html#function_floor // See also https://www.postgresql.org/docs/9.5/static/functions-math.html#FUNCTIONS-MATH-FUNC-TABLE // SQLite has no equivalent of floor() so this is used: // (CASE WHEN X >= 0 OR X = CAST(X AS INT) THEN CAST(X AS INT) ELSE CAST(X AS INT) - 1 END) //! @todo add a custom function to SQLite to optimize/simplify things // example: SELECT FLOOR(3.14), FLOOR(-99.001) // result: 3, -100 _SIG(floor, argAnyNumberOrNull); m_functions.insert(QLatin1String("GREATEST"), decl = new MinMaxFunctionDeclaration); m_aliases.insert(QLatin1String("MAX"), decl); // From https://www.sqlite.org/lang_corefunc.html // For SQLite MAX() is used. // If arguments are of text type, to each argument default (unicode) collation // is assigned that is configured for SQLite by KDb. // Example: SELECT MAX('ą' COLLATE '', 'z' COLLATE ''). // Example: SELECT MAX('ą' COLLATE '', 'z' COLLATE ''). /* The multi-argument max() function returns the argument with the maximum value, or return NULL if any argument is NULL. The multi-argument max() function searches its arguments from left to right for an argument that defines a collating function and uses that collating function for all string comparisons. If none of the arguments to max() define a collating function, then the BINARY collating function is used. Note that max() is a simple function when it has 2 or more arguments but operates as an aggregate function if given only a single argument. */ // For pgsql GREATEST() function ignores NULL values, it only returns NULL // if all the expressions evaluate to NULL. So this is used for MAX(v0,..,vN): // (CASE WHEN (v0) IS NULL OR .. OR (vN) IS NULL THEN NULL ELSE GREATEST(v0,..,vN) END) // See also https://www.postgresql.org/docs/9.5/static/functions-conditional.html#FUNCTIONS-GREATEST-LEAST //! @todo for pgsql CREATE FUNCTION can be used to speed up and simplify things // For mysql GREATEST() is used. // See https://dev.mysql.com/doc/refman/5.1/en/comparison-operators.html#function_greatest // Note: Before MySQL 5.0.13, GREATEST() returns NULL only if all arguments are NULL // (like pgsql). As of 5.0.13, it returns NULL if any argument is NULL (like sqlite's MAX()). // See also https://bugs.mysql.com/bug.php?id=15610 //! @todo MySQL: check for server version and don't use the pgsql's approach for ver >= 5.0.13 //! We cannot do that now because we only have access to driver, not the connection. // example: SELECT GREATEST("Z", "ą", "AA"), MAX(0.1, 7.1, 7), GREATEST(9, NULL, -1) // result: "Z", 7.1, NULL static int greatest_min_args[] = { 2 }; _SIG(greatest_N, argAnyOrNull, multipleArgs, greatest_min_args); m_functions.insert(QLatin1String("HEX"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_hex /* The hex() function interprets its argument as a BLOB and returns a string which is the upper-case hexadecimal rendering of the content of that blob. */ /* For pgsql UPPER(ENCODE(val, 'hex')) is used, See https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-OTHER */ // example: SELECT HEX(X'BEEF'), HEX('DEAD') // result: "BEEF", "44454144" //! @todo HEX(int) for SQLite is not the same as HEX(int) for MySQL so we disable it //! -- maybe can be wrapped? decl->defaultReturnType = KDbField::LongText; _SIG(hex_1, argAnyTextBLOBOrNull); m_functions.insert(QLatin1String("IFNULL"), decl = new CoalesceFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The ifnull() function returns a copy of its first non-NULL argument, or NULL if both arguments are NULL. Ifnull() must have exactly 2 arguments. The ifnull() function is equivalent to coalesce() with two arguments. */ // For postgresql coalesce() is used. // example: SELECT IFNULL(NULL, 17), IFNULL(NULL, NULL) // result: 17, NULL _SIG(ifnull_2, argAnyOrNull, argAnyOrNull); m_functions.insert(QLatin1String("INSTR"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The instr(X,Y) function finds the first occurrence of string Y within string X and returns the number of prior characters plus 1, or 0 if Y is nowhere found within X. If both arguments X and Y to instr(X,Y) are non-NULL and are not BLOBs then both are interpreted as strings. If either X or Y are NULL in instr(X,Y) then the result is NULL. */ //! @todo PostgreSQL does not have instr() but CREATE FUNCTION can be used, //! see https://www.postgresql.org/docs/9.5/static/plpgsql-porting.html //! @todo support (BLOB, BLOB)? /* From the same docs: Or, if X and Y are both BLOBs, then instr(X,Y) returns one more than the number bytes prior to the first occurrence of Y, or 0 if Y does not occur anywhere within X. */ // example: SELECT INSTR("KEXI", "X"), INSTR("KEXI", "ZZ") // result: 3, 0 decl->defaultReturnType = KDbField::Integer; _SIG(instr_2, argAnyTextOrNull, argAnyTextOrNull); m_functions.insert(QLatin1String("LEAST"), decl = new MinMaxFunctionDeclaration); m_aliases.insert(QLatin1String("MIN"), decl); // From https://www.sqlite.org/lang_corefunc.html // For SQLite uses MIN(). /* The multi-argument min() function returns the argument with the minimum value, or return NULL if any argument is NULL. The multi-argument min() function searches its arguments from left to right for an argument that defines a collating function and uses that collating function for all string comparisons. If none of the arguments to max() define a collating function, then the BINARY collating function is used. Note that max() is a simple function when it has 2 or more arguments but operates as an aggregate function if given only a single argument. */ // For pgsql LEAST() function ignores NULL values, it only returns NULL // if all the expressions evaluate to NULL. So this is used for MAX(v0,..,vN): // (CASE WHEN (v0) IS NULL OR .. OR (vN) IS NULL THEN NULL ELSE LEAST(v0,..,vN) END) // See also https://www.postgresql.org/docs/9.5/static/functions-conditional.html#FUNCTIONS-GREATEST-LEAST //! @todo for pgsql CREATE FUNCTION can be used to speed up and simplify things // For mysql LEAST() is used. // See https://dev.mysql.com/doc/refman/5.1/en/comparison-operators.html#function_least // Note: Before MySQL 5.0.13, LEAST() returns NULL only if all arguments are NULL // (like pgsql). As of 5.0.13, it returns NULL if any argument is NULL (like sqlite's MIN()). //! @todo MySQL: check for server version and don't use the pgsql's approach for ver >= 5.0.13 //! We cannot do that now because we only have access to driver, not the connection. // See also https://bugs.mysql.com/bug.php?id=15610 // example: SELECT LEAST("Z", "ą", "AA"), MIN(0.1, 7.1, 7), LEAST(9, NULL, -1) // result: "ą", 0.1, NULL static int least_min_args[] = { 2 }; _SIG(least_N, argAnyOrNull, multipleArgs, least_min_args); m_functions.insert(QLatin1String("LENGTH"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_length /* For a string value X, the length(X) function returns the number of characters (not bytes) in X prior to the first NUL character. Since SQLite strings do not normally contain NUL characters, the length(X) function will usually return the total number of characters in the string X. For a blob value X, length(X) returns the number of bytes in the blob. If X is NULL then length(X) is NULL. If X is numeric then length(X) returns the length of a string representation of X. */ /* For postgres octet_length(val) is used if val is a of BLOB type. length(val) for BLOB cannot be used because it returns number of bits. */ /* For mysql char_length(val) is used. This is because length(val) in mysql returns number of bytes, what is not right for multibyte (unicode) encodings. */ // example: SELECT LENGTH('Straße'), LENGTH(X'12FE') // result: 6, 2 decl->defaultReturnType = KDbField::Integer; _SIG(length_1, argAnyTextBLOBOrNull); m_functions.insert(QLatin1String("LOWER"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The lower(X) function returns a copy of string X with all characters converted to lower case. */ // Note: SQLite such as 3.8 without ICU extension does not convert non-latin1 characters // too well; Kexi uses ICU extension by default so the results are very good. // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_lower // See also https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-SQL // example: SELECT LOWER("MEGSZENTSÉGTELENÍTHETETLENSÉGESKEDÉSEITEKÉRT") // result: "megszentségteleníthetetlenségeskedéseitekért" decl->defaultReturnType = KDbField::LongText; _SIG(lower_1, argAnyTextOrNull); m_functions.insert(QLatin1String("LTRIM"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The ltrim(X,Y) function returns a string formed by removing any and all characters that appear in Y from the left side of X. If the Y argument is omitted, ltrim(X) removes spaces from the left side of X.*/ // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_ltrim //! @todo MySQL's LTRIM only supports one arg. TRIM() does not work too //! https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_trim // See also https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-SQL // example: SELECT LTRIM(" John Smith") // result: "John Smith" // example: SELECT LTRIM("a b or c", "ab ") // result: "or c" decl->defaultReturnType = KDbField::LongText; _SIG(ltrim_1, argAnyTextOrNull); _SIG(ltrim_2, argAnyTextOrNull, argAnyTextOrNull); m_functions.insert(QLatin1String("NULLIF"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The nullif(X,Y) function returns its first argument if the arguments are different and NULL if the arguments are the same. The nullif(X,Y) function searches its arguments from left to right for an argument that defines a collating function and uses that collating function for all string comparisons. If neither argument to nullif() defines a collating function then the BINARY is used. */ // See also https://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_nullif // See also https://www.postgresql.org/docs/9.5/static/functions-conditional.html#FUNCTIONS-NULLIF // example: SELECT NULLIF("John", "Smith"), NULLIF(177, 177) // result: "John", NULL decl->copyReturnTypeFromArg = 0; _SIG(nullif_2, argAnyOrNull, argAnyOrNull); m_functions.insert(QLatin1String("RANDOM"), decl = new RandomFunctionDeclaration); /* RANDOM() returns a random floating-point value v in the range 0 <= v < 1.0. RANDOM(X,Y) - returns returns a random integer that is equal or greater than X and less than Y. */ // For MySQL RANDOM() is equal to RAND(). // For MySQL RANDOM(X,Y) is equal to (X + FLOOR(RAND() * (Y - X)) // For PostreSQL RANDOM() is equal to RANDOM(). // For PostreSQL RANDOM(X,Y) is equal to (X + FLOOR(RANDOM() * (Y - X)) // Because SQLite returns integer between -9223372036854775808 and +9223372036854775807, // so RANDOM() for SQLite is equal to (RANDOM()+9223372036854775807)/18446744073709551615. // Similarly, RANDOM(X,Y) for SQLite is equal // to (X + CAST((Y - X) * (RANDOM()+9223372036854775807)/18446744073709551615 AS INT)). // See also https://dev.mysql.com/doc/refman/5.1/en/mathematical-functions.html#function_rand // See also https://www.postgresql.org/docs/9.5/static/functions-math.html#FUNCTIONS-MATH-RANDOM-TABLE //! @note rand(X) (where X is a seed value to set) isn't portable between MySQL and PostgreSQL, //! and does not exist in SQLite, so we don't support it. // example: SELECT RANDOM(), RANDOM(2, 5) // result: (some random floating-point value v where 0 <= v < 1.0) // example: SELECT RANDOM(2, 5) // result: (some random integer value v where 2 <= v < 5) decl->defaultReturnType = KDbField::Double; _SIG0; _SIG(random_2, argAnyIntOrNull, argAnyIntOrNull); m_functions.insert(QLatin1String("ROUND"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The round(X,Y) function returns a floating-point value X rounded to Y digits to the right of the decimal point. If the Y argument is omitted, it is assumed to be 0. */ // See also https://dev.mysql.com/doc/refman/5.1/en/mathematical-functions.html#function_round // See also https://www.postgresql.org/docs/9.5/static/functions-math.html#FUNCTIONS-MATH-FUNC-TABLE //! @note round(X,Y) where Y < 0 is supported only by MySQL so we ignore this case // example: SELECT ROUND(-1.13), ROUND(-5.51), ROUND(5.51), ROUND(1.298, 1), ROUND(1.298, 0), ROUND(7) // result: -1, -6, 6, 1.3, 1, 7 decl->copyReturnTypeFromArg = 0; _SIG(round_1, argAnyNumberOrNull); _SIG(round_2, argAnyNumberOrNull, argAnyIntOrNull); m_functions.insert(QLatin1String("RTRIM"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The rtrim(X,Y) function returns a string formed by removing any and all characters that appear in Y from the right side of X. If the Y argument is omitted, rtrim(X) removes spaces from the right side of X. */ // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_ltrim //! @todo MySQL's RTRIM only supports one arg. TRIM() does not work too //! https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_trim // See also https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-SQL // example: SELECT RTRIM("John Smith ") // result: "John Smith" // example: SELECT RTRIM("a b or c", "orc ") // result: "a b" decl->defaultReturnType = KDbField::LongText; _SIG(rtrim_1, argAnyTextOrNull); _SIG(rtrim_2, argAnyTextOrNull, argAnyTextOrNull); m_functions.insert(QLatin1String("SOUNDEX"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The soundex(X) function returns a string that is the soundex encoding of the string X. The string "?000" is returned if the argument is NULL or contains non-ASCII alphabetic characters. */ // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_soundex // See also https://www.postgresql.org/docs/9.5/static/fuzzystrmatch.html#AEN165853 //! @todo we call drv_executeSql("CREATE EXTENSION IF NOT EXISTS fuzzystrmatch") on connection, //! do that on first use of SOUNDEX() // example: SELECT SOUNDEX("John") // result: "J500" decl->defaultReturnType = KDbField::Text; _SIG(soundex, argAnyTextOrNull); m_functions.insert(QLatin1String("SUBSTR"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The substr(X,Y) returns all characters through the end of the string X beginning with the Y-th. The left-most character of X is number 1. If Y is negative then the first character of the substring is found by counting from the right rather than the left. If Z is negative then the abs(Z) characters preceding the Y-th character are returned. If X is a string then characters indices refer to actual UTF-8 characters. If X is a BLOB then the indices refer to bytes. */ _SIG(substr_2, argAnyTextOrNull, argAnyIntOrNull); /* The substr(X,Y,Z) function returns a substring of input string X that begins with the Y-th character and which is Z characters long. */ _SIG(substr_3, argAnyTextOrNull, argAnyIntOrNull, argAnyIntOrNull); decl->copyReturnTypeFromArg = 0; m_functions.insert(QLatin1String("TRIM"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The trim(X,Y) function returns a string formed by removing any and all characters that appear in Y from both ends of X. If the Y argument is omitted, trim(X) removes spaces from both ends of X. */ // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_trim //! @todo MySQL's TRIM only supports one arg. TRIM() does not work too //! https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_trim // See also https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-SQL // example: SELECT TRIM(" John Smith ") // result: "John Smith" // example: SELECT TRIM("a b or c", "orca ") // result: "b" decl->defaultReturnType = KDbField::LongText; _SIG(trim_1, argAnyTextOrNull); _SIG(trim_2, argAnyTextOrNull, argAnyTextOrNull); m_functions.insert(QLatin1String("UNICODE"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The unicode(X) function returns the numeric unicode code point corresponding to the first character of the string X. If the argument to unicode(X) is not a string then the result is undefined. */ // For MySQL ORD(CONVERT(X USING UTF16)) is used (ORD(X) returns a UTF-16 number) // For PostreSQL ASCII(X) is used. // example: SELECT UNICODE('A'), UNICODE('ą'), UNICODE('Δ'), UNICODE('葉') // result: 65, 261, 916, 33865 decl->defaultReturnType = KDbField::Integer; _SIG(unicode_1, argAnyTextOrNull); m_functions.insert(QLatin1String("UPPER"), decl = new BuiltInFunctionDeclaration); // From https://www.sqlite.org/lang_corefunc.html /* The upper(X) function returns a copy of string X with all characters converted to upper case. */ // Note: SQLite such as 3.8 without ICU extension does not convert non-latin1 characters // too well; Kexi uses ICU extension by default so the results are very good. // See also https://dev.mysql.com/doc/refman/5.1/en/string-functions.html#function_upper // See also https://www.postgresql.org/docs/9.5/static/functions-string.html#FUNCTIONS-STRING-SQL // example: SELECT UPPER("megszentségteleníthetetlenségeskedéseitekért") // result: "MEGSZENTSÉGTELENÍTHETETLENSÉGESKEDÉSEITEKÉRT" decl->defaultReturnType = KDbField::LongText; _SIG(upper_1, argAnyTextOrNull); #ifdef KDB_ENABLE_SQLITE_SPECIFIC_FUNCTIONS m_functions.insert(QLatin1String("GLOB"), decl = new BuiltInFunctionDeclaration); //! @todo GLOB(X,Y) is SQLite-specific and is not present in MySQL so we don't expose it; use GLOB operator instead. //! We may want to address it in raw SQL generation time. // From https://www.sqlite.org/lang_corefunc.html /* The glob(X,Y) function is equivalent to the expression "Y GLOB X". Note that the X and Y arguments are reversed in the glob() function relative to the infix GLOB operator. */ // example: SELECT GLOB("Foo*", "FooBar"), GLOB("Foo*", "foobar") // result: TRUE, FALSE decl->defaultReturnType = KDbField::Boolean; _SIG(glob_2, argAnyTextOrNull, argAnyOrNull /* will be casted to text */); m_functions.insert(QLatin1String("LIKE"), decl = new BuiltInFunctionDeclaration); //! @todo LIKE(X,Y,[Z]) not present in MySQL so we don't expose it; use LIKE operator instead. //! We may want to address it in raw SQL generation time. // From https://www.sqlite.org/lang_corefunc.html /* The like() function is used to implement the "Y LIKE X [ESCAPE Z]" expression. If the optional ESCAPE clause is present, then the like() function is invoked with three arguments. Otherwise, it is invoked with two arguments only. Note that the X and Y parameters are reversed in the like() function relative to the infix LIKE operator.*/ decl->defaultReturnType = KDbField::Boolean; _SIG(like_2, argAnyTextOrNull, argAnyTextOrNull); _SIG(like_3, argAnyTextOrNull, argAnyTextOrNull, argAnyTextOrNull); #endif } BuiltInFunctionDeclaration* BuiltInFunctions::value(const QString &name) const { BuiltInFunctionDeclaration* f = m_functions.value(name); if (!f) { f = m_aliases.value(name); } return f; } QStringList BuiltInFunctions::aliases() const { return m_aliases.keys(); } Q_GLOBAL_STATIC(BuiltInFunctions, _builtInFunctions) //========================================= KDbFunctionExpressionData::KDbFunctionExpressionData() : KDbExpressionData() { ExpressionDebug << "FunctionExpressionData" << ref; setArguments(ExplicitlySharedExpressionDataPointer()); } KDbFunctionExpressionData::KDbFunctionExpressionData(const QString& aName, ExplicitlySharedExpressionDataPointer arguments) : KDbExpressionData() , name(aName) { setArguments(arguments); ExpressionDebug << "FunctionExpressionData" << ref << *args; } KDbFunctionExpressionData::~KDbFunctionExpressionData() { ExpressionDebug << "~FunctionExpressionData" << ref; } KDbFunctionExpressionData* KDbFunctionExpressionData::clone() { ExpressionDebug << "FunctionExpressionData::clone" << *this; KDbFunctionExpressionData *cloned = new KDbFunctionExpressionData(*this); ExpressionDebug << "FunctionExpressionData::clone" << *cloned; cloned->args = args->clone(); return cloned; } void KDbFunctionExpressionData::debugInternal(QDebug dbg, KDb::ExpressionCallStack* callStack) const { dbg.nospace() << "FunctionExp(" << name; if (args.data()) { dbg.nospace() << ','; args.data()->debug(dbg, callStack); } dbg.nospace() << QString::fromLatin1(",type=%1)").arg(KDbDriver::defaultSqlTypeName(type())); } static QByteArray greatestOrLeastName(const QByteArray &name) { if (name == "MAX") { return "GREATEST"; } if (name == "MIN") { return "LEAST"; } return name; } KDbEscapedString KDbFunctionExpressionData::toStringInternal( const KDbDriver *driver, KDbQuerySchemaParameterValueListIterator* params, KDb::ExpressionCallStack* callStack) const { KDbNArgExpressionData *argsData = args->convert(); if (name == QLatin1String("HEX")) { if (driver) { return driver->hexFunctionToString(KDbNArgExpression(args), params, callStack); } } else if (name == QLatin1String("IFNULL")) { if (driver) { return driver->ifnullFunctionToString(KDbNArgExpression(args), params, callStack); } } else if (name == QLatin1String("LENGTH")) { if (driver) { return driver->lengthFunctionToString(KDbNArgExpression(args), params, callStack); } } else if (name == QLatin1String("GREATEST") || name == QLatin1String("MAX") || name == QLatin1String("LEAST") || name == QLatin1String("MIN")) { if (driver) { return driver->greatestOrLeastFunctionToString( QString::fromLatin1(greatestOrLeastName(name.toLatin1())), KDbNArgExpression(args), params, callStack); } // else: don't change MIN/MAX } else if (name == QLatin1String("RANDOM")) { if (driver) { return driver->randomFunctionToString(KDbNArgExpression(args), params, callStack); } } else if (name == QLatin1String("CEILING") || name == QLatin1String("FLOOR")) { if (driver) { return driver->ceilingOrFloorFunctionToString(name, KDbNArgExpression(args), params, callStack); } } else if (name == QLatin1String("UNICODE")) { if (driver) { return driver->unicodeFunctionToString(KDbNArgExpression(args), params, callStack); } } return KDbFunctionExpressionData::toString(name, driver, argsData, params, callStack); } void KDbFunctionExpressionData::getQueryParameters(QList* params) { Q_ASSERT(params); args->getQueryParameters(params); } KDbField::Type KDbFunctionExpressionData::typeInternal(KDb::ExpressionCallStack* callStack) const { Q_UNUSED(callStack); const BuiltInFunctionDeclaration *decl = _builtInFunctions->value(name); if (decl) { return decl->returnType(this, nullptr); } //! @todo return KDbField::InvalidType; } static void setIncorrectNumberOfArgumentsErrorMessage(KDbParseInfo *parseInfo, int count, const std::vector &argCounts, const QString &name) { parseInfo->setErrorMessage( KDbFunctionExpressionData::tr("Incorrect number of arguments (%1)").arg(count)); const int maxArgCount = argCounts[argCounts.size() - 1]; const int minArgCount = argCounts[0]; QString firstSentence; if (count > maxArgCount) { firstSentence = KDbFunctionExpressionData::tr("Too many arguments.%1", "don't use space before %1") .arg(QLatin1String(" ")); } if (count < minArgCount) { firstSentence = KDbFunctionExpressionData::tr("Too few arguments.%1", "don't use space before %1") .arg(QLatin1String(" ")); } if (argCounts.size() == 1) { const int c = argCounts[0]; if (c == 0) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function does not accept any arguments.") .arg(firstSentence, name)); } else if (c == 1) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires 1 argument.") .arg(firstSentence, name)); } else { //~ singular %1%2() function requires %3 argument. //~ plural %1%2() function requires %3 arguments. parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires %3 argument(s).", "", c) .arg(firstSentence, name).arg(c)); } } else if (argCounts.size() == 2) { const int c1 = argCounts[0]; const int c2 = argCounts[1]; if (c2 == 1) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires 0 or 1 argument.", "the function requires zero or one argument") .arg(firstSentence, name)); } else { //~ singular %1%2() function requires %3 or %4 argument. //~ plural %1%2() function requires %3 or %4 arguments. parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires %3 or %4 argument(s).", "", c2) .arg(firstSentence, name).arg(c1).arg(c2)); } } else if (argCounts.size() == 3) { //~ singular %1%2() function requires %3 or %4 or %5 argument. //~ plural %1%2() function requires %3 or %4 or %5 arguments. parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires %3 or %4 or %5 argument(s).", "", argCounts[2]) .arg(firstSentence, name).arg(argCounts[0]) .arg(argCounts[1]).arg(argCounts[2])); } else { QString listCounts; for(std::vector::const_iterator it(argCounts.begin()); it != argCounts.end(); ++it) { if (listCounts.isEmpty()) { listCounts += QString::number(*it); } else { listCounts = KDbFunctionExpressionData::tr("%1 or %2").arg(listCounts).arg(*it); } } parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1%2() function requires %3 argument(s).", "", argCounts[argCounts.size() - 1]) .arg(firstSentence, name, listCounts)); } } static void setIncorrectTypeOfArgumentsErrorMessage(KDbParseInfo *parseInfo, int argNum, KDbField::Type type, int *argTypes, const QString &name) { QString listTypes; int *argType = argTypes; while(*argType != KDbField::InvalidType) { if (!listTypes.isEmpty()) { listTypes += KDbFunctionExpressionData::tr(" or "); } const KDbField::Type realFieldType = KDb::intToFieldType(*argType); if (realFieldType != KDbField::InvalidType) { listTypes += KDbFunctionExpressionData::tr("\"%1\"") .arg(KDbField::typeName(realFieldType)); } else if (*argType == KDbField::Null) { listTypes += KDbFunctionExpressionData::tr("\"%1\"") .arg(KDbField::typeName(KDbField::Null)); } else if (*argType == AnyText) { listTypes += KDbFunctionExpressionData::tr("\"%1\"") .arg(KDbField::typeName(KDbField::Text)); } else if (*argType == AnyInt) { listTypes += KDbFunctionExpressionData::tr("\"%1\"") .arg(KDbField::typeName(KDbField::Integer)); } else if (*argType == AnyFloat) { listTypes += KDbFunctionExpressionData::tr("\"%1\"") .arg(KDbField::typeGroupName(KDbField::FloatGroup)); // better than typeName() in this case } else if (*argType == AnyNumber) { listTypes += KDbFunctionExpressionData::tr("\"Number\""); } else if (*argType == Any) { listTypes += KDbFunctionExpressionData::tr("\"Any\"", "Any data type"); } ++argType; } parseInfo->setErrorMessage(KDbFunctionExpressionData::tr("Incorrect type of argument")); QString lastSentence = KDbFunctionExpressionData::tr("Specified argument is of type \"%1\".") .arg(KDbField::typeName(type)); if (argNum == 0) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's first argument should be of type %2. %3") .arg(name, listTypes, lastSentence)); } else if (argNum == 1) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's second argument should be of type %2. %3") .arg(name, listTypes, lastSentence)); } else if (argNum == 2) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's third argument should be of type %2. %3") .arg(name, listTypes, lastSentence)); } else if (argNum == 3) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's fourth argument should be of type %2. %3") .arg(name, listTypes, lastSentence)); } else if (argNum == 4) { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's fifth argument should be of type %2. %3") .arg(name, listTypes, lastSentence)); } else { parseInfo->setErrorDescription( KDbFunctionExpressionData::tr("%1() function's %2 argument should be of type %3. %4") .arg(name).arg(argNum + 1).arg(listTypes, lastSentence)); } } //! @return true if type rule @a argType matches concrete type @a actualType static bool typeMatches(int argType, KDbField::Type actualType) { if (argType == AnyText) { if (KDbField::isTextType(actualType)) { return true; } } else if (argType == AnyInt) { if (KDbField::isIntegerType(actualType)) { return true; } } else if (argType == AnyFloat) { if (KDbField::isFPNumericType(actualType)) { return true; } } else if (argType == AnyNumber) { if (KDbField::isNumericType(actualType)) { return true; } } else if (argType == Any) { return true; } else { if (argType == actualType) { return true; } } return false; } static int findMatchingType(int *argTypePtr, KDbField::Type actualType) { for (; *argTypePtr != KDbField::InvalidType; ++argTypePtr) { if (typeMatches(*argTypePtr, actualType)) { break; } } return *argTypePtr; } bool KDbFunctionExpressionData::validateInternal(KDbParseInfo *parseInfo, KDb::ExpressionCallStack* callStack) { if (!args->validate(parseInfo, callStack)) { return false; } if (args->token != ',') { // arguments required: NArgExpr with token ',' return false; } if (args->children.count() > KDB_MAX_FUNCTION_ARGS) { parseInfo->setErrorMessage( tr("Too many arguments for function.")); parseInfo->setErrorDescription( tr("Maximum number of arguments for function %1() is %2.") .arg(args->children.count()).arg(KDB_MAX_FUNCTION_ARGS)); return false; } if (!args->validate(parseInfo)) { return false; } if (name.isEmpty()) { return false; } const BuiltInFunctionDeclaration *decl = _builtInFunctions->value(name); if (!decl) { return false; } const KDbNArgExpressionData *argsData = args->convertConst(); if (argsData->containsInvalidArgument()) { return false; } // Find matching signature const int count = args->children.count(); bool properArgCount = false; std::vector argCounts; int i = 0; argCounts.resize(decl->signatures.size()); int **signature = nullptr; bool multipleArgs = false; // special case, e.g. for CHARS(v1, ... vN) for(std::vector::const_iterator it(decl->signatures.begin()); it != decl->signatures.end(); ++it, ++i) { signature = *it; int **arg = signature; int expectedCount = 0; while(*arg && *arg != BuiltInFunctions::multipleArgs) { ++arg; ++expectedCount; } multipleArgs = *arg == BuiltInFunctions::multipleArgs; if (multipleArgs) { ++arg; const int minArgs = arg[0][0]; properArgCount = count >= minArgs; if (!properArgCount) { parseInfo->setErrorMessage( tr("Incorrect number of arguments (%1)").arg(count)); if (minArgs == 1) { parseInfo->setErrorDescription( tr("Too few arguments. %1() function requires " "at least one argument.").arg(name)); } else if (minArgs == 2) { parseInfo->setErrorDescription( tr("Too few arguments. %1() function requires " "at least two arguments.").arg(name)); } else if (minArgs == 3) { parseInfo->setErrorDescription( tr("Too few arguments. %1() function requires " "at least three arguments.").arg(name)); } else { parseInfo->setErrorDescription( tr("Too few arguments. %1() function requires " "at least %2 arguments.").arg(name).arg(minArgs)); } return false; } break; } else if (count == expectedCount) { // arg # matches properArgCount = true; break; } else { argCounts[i] = expectedCount; } } if (!properArgCount) { std::unique(argCounts.begin(), argCounts.end()); std::sort(argCounts.begin(), argCounts.end()); // sort so we can easier check the case setIncorrectNumberOfArgumentsErrorMessage(parseInfo, count, argCounts, name); return false; } // Verify types if (multipleArgs) { // special signature: {typesForAllArgs, [multipleArgs-token], MIN, 0} int **arg = signature; int *typesForAllArgs = arg[0]; int i = 0; foreach(const ExplicitlySharedExpressionDataPointer &expr, args->children) { const KDbField::Type exprType = expr->type(); // cache: evaluating type of expressions can be expensive const bool isQueryParameter = expr->convertConst(); if (!isQueryParameter) { // (query parameter always matches) const int matchingType = findMatchingType(typesForAllArgs, exprType); if (matchingType == KDbField::InvalidType) { setIncorrectTypeOfArgumentsErrorMessage(parseInfo, i, exprType, typesForAllArgs, name); return false; } } ++i; } } else { // typical signature: array of type-lists int **arg = signature; int i=0; foreach(const ExplicitlySharedExpressionDataPointer &expr, args->children) { const KDbField::Type exprType = expr->type(); // cache: evaluating type of expressions can be expensive const bool isQueryParameter = expr->convertConst(); if (!isQueryParameter) { // (query parameter always matches) const int matchingType = findMatchingType(arg[0], exprType); if (matchingType == KDbField::InvalidType) { setIncorrectTypeOfArgumentsErrorMessage(parseInfo, i, exprType, arg[0], name); return false; } } ++arg; ++i; } } // Check type just now. If we checked earlier, possible error message would be less informative. if (decl->returnType(this, parseInfo) == KDbField::InvalidType) { return false; } return true; } void KDbFunctionExpressionData::setArguments(ExplicitlySharedExpressionDataPointer arguments) { args = (arguments && arguments->convert()) ? arguments : ExplicitlySharedExpressionDataPointer(new KDbNArgExpressionData); children.append(args); args->parent = this; args->token = ','; args->expressionClass = KDb::ArgumentListExpression; } //static KDbEscapedString KDbFunctionExpressionData::toString( const QString &name, const KDbDriver *driver, const KDbNArgExpressionData *args, KDbQuerySchemaParameterValueListIterator* params, KDb::ExpressionCallStack* callStack) { return KDbEscapedString(name + QLatin1Char('(')) + args->toString(driver, params, callStack) + KDbEscapedString(')'); } //========================================= inline KDb::ExpressionClass classForFunctionName(const QString& name) { if (KDbFunctionExpression::isBuiltInAggregate(name)) return KDb::AggregationExpression; else return KDb::FunctionExpression; } KDbFunctionExpression::KDbFunctionExpression() : KDbExpression(new KDbFunctionExpressionData) { ExpressionDebug << "KDbFunctionExpression() ctor" << *this; } KDbFunctionExpression::KDbFunctionExpression(const QString& name) : KDbExpression(new KDbFunctionExpressionData(name), classForFunctionName(name), KDbToken()/*undefined*/) { } KDbFunctionExpression::KDbFunctionExpression(const QString& name, const KDbNArgExpression& arguments) : KDbExpression(new KDbFunctionExpressionData(name.toUpper(), arguments.d), classForFunctionName(name), KDbToken()/*undefined*/) { } KDbFunctionExpression::KDbFunctionExpression(const KDbFunctionExpression& expr) : KDbExpression(expr) { } KDbFunctionExpression::KDbFunctionExpression(KDbExpressionData* data) : KDbExpression(data) { ExpressionDebug << "KDbFunctionExpression ctor (KDbExpressionData*)" << *this; } KDbFunctionExpression::KDbFunctionExpression(const ExplicitlySharedExpressionDataPointer &ptr) : KDbExpression(ptr) { } KDbFunctionExpression::~KDbFunctionExpression() { } // static bool KDbFunctionExpression::isBuiltInAggregate(const QString& function) { return _builtInAggregates->data.contains(function.toUpper()); } // static QStringList KDbFunctionExpression::builtInAggregates() { return _builtInAggregates->data.toList(); } //static KDbEscapedString KDbFunctionExpression::toString( const QString &name, const KDbDriver *driver, const KDbNArgExpression& args, KDbQuerySchemaParameterValueListIterator* params, KDb::ExpressionCallStack* callStack) { const KDbNArgExpressionData *argsData = args.d.constData()->convertConst(); return KDbFunctionExpressionData::toString(name, driver, argsData, params, callStack); } QString KDbFunctionExpression::name() const { return d->convert()->name; } void KDbFunctionExpression::setName(const QString &name) { d->convert()->name = name; } KDbNArgExpression KDbFunctionExpression::arguments() { return KDbNArgExpression(d->convert()->args); } void KDbFunctionExpression::setArguments(const KDbNArgExpression &arguments) { d->convert()->setArguments(arguments.d); } // static KDbEscapedString KDbFunctionExpression::greatestOrLeastFunctionUsingCaseToString( const QString &name, const KDbDriver *driver, const KDbNArgExpression &args, KDbQuerySchemaParameterValueListIterator* params, KDb::ExpressionCallStack* callStack) { // (CASE WHEN (v0) IS NULL OR .. OR (vN) IS NULL THEN NULL ELSE F(v0,..,vN) END) if (args.argCount() >= 2) { KDbEscapedString whenSql; whenSql.reserve(256); foreach(const ExplicitlySharedExpressionDataPointer &child, args.d.constData()->children) { if (!whenSql.isEmpty()) { whenSql += " OR "; } whenSql += QLatin1Char('(') + child->toString(driver, params, callStack) + QLatin1String(") IS NULL"); } return KDbEscapedString("(CASE WHEN (") + whenSql + QLatin1String(") THEN NULL ELSE (") + KDbFunctionExpression::toString(name, driver, args, params, callStack) + QLatin1String(") END)"); } return KDbFunctionExpression::toString(name, driver, args, params, callStack); } diff --git a/src/parser/KDbSqlParser.y b/src/parser/KDbSqlParser.y index 6b46791f..3bc8acad 100644 --- a/src/parser/KDbSqlParser.y +++ b/src/parser/KDbSqlParser.y @@ -1,1449 +1,1449 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch Copyright (C) 2004-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // To keep binary compatibility, do not reorder tokens! Add new only at the end. %token SQL_TYPE %token AS %token AS_EMPTY /* used for aliases with skipped AS keyword */ %token ASC %token AUTO_INCREMENT %token BIT %token BITWISE_SHIFT_LEFT %token BITWISE_SHIFT_RIGHT %token BY %token CHARACTER_STRING_LITERAL %token CONCATENATION /* || */ %token CREATE %token DESC %token DISTINCT %token DOUBLE_QUOTED_STRING %token FROM %token JOIN %token KEY %token LEFT %token LESS_OR_EQUAL %token GREATER_OR_EQUAL %token SQL_NULL %token SQL_IS %token SQL_IS_NULL /*helper */ %token SQL_IS_NOT_NULL /*helper */ %token ORDER %token PRIMARY %token SELECT %token INTEGER_CONST %token REAL_CONST %token RIGHT %token SQL_ON %token DATE_CONST %token DATETIME_CONST %token TIME_CONST %token TABLE %token IDENTIFIER %token IDENTIFIER_DOT_ASTERISK %token QUERY_PARAMETER %token VARCHAR %token WHERE %token SQL %token SQL_TRUE %token SQL_FALSE %token UNION %token SCAN_ERROR //%token SQL_ABS //%token ACOS //%token AMPERSAND //%token SQL_ABSOLUTE //%token ADA //%token ADD //%token ADD_DAYS //%token ADD_HOURS //%token ADD_MINUTES //%token ADD_MONTHS //%token ADD_SECONDS //%token ADD_YEARS //%token ALL //%token ALLOCATE //%token ALTER %token AND //%token ANY //%token ARE //%token ASIN //%token ASCII //%token ASSERTION //%token ATAN //%token ATAN2 //%token AUTHORIZATION //%token AVG //%token BEFORE %token BETWEEN %token NOT_BETWEEN //%token SQL_BEGIN //%token BIGINT //%token BINARY //%token BIT_LENGTH //%token BREAK //%token CASCADE //%token CASCADED //%token CASE //%token CAST //%token CATALOG //%token CEILING //%token CENTER //%token SQL_CHAR //%token CHAR_LENGTH //%token CHECK //%token CLOSE //%token COALESCE //%token COBOL //%token COLLATE //%token COLLATION //%token COLUMN //%token COMMIT //%token COMPUTE //%token CONCAT //%token CONNECT //%token CONNECTION //%token CONSTRAINT //%token CONSTRAINTS //%token CONTINUE //%token CONVERT //%token CORRESPONDING //%token COS //%token COT //%token COUNT //%token CURDATE //%token CURRENT //%token CURRENT_DATE //%token CURRENT_TIME //%token CURRENT_TIMESTAMP //%token CURTIME //%token CURSOR //%token DATABASE //%token SQL_DATE //%token DATE_FORMAT //%token DATE_REMAINDER //%token DATE_VALUE //%token DAY //%token DAYOFMONTH //%token DAYOFWEEK //%token DAYOFYEAR //%token DAYS_BETWEEN //%token DEALLOCATE //%token DEC //%token DECLARE //%token DEFAULT //%token DEFERRABLE //%token DEFERRED //%token SQL_DELETE //%token DESCRIBE //%token DESCRIPTOR //%token DIAGNOSTICS //%token DICTIONARY //%token DIRECTORY //%token DISCONNECT //%token DISPLACEMENT //%token DOMAIN_TOKEN //%token SQL_DOUBLE //%token DROP //%token ELSE //%token END //%token END_EXEC //%token ESCAPE %token EXCEPT //%token SQL_EXCEPTION //%token EXEC //%token EXECUTE //%token EXISTS //%token EXP //%token EXPONENT //%token EXTERNAL //%token EXTRACT //%token FETCH //%token FIRST //%token SQL_FLOAT //%token FLOOR //%token FN //%token FOR //%token FOREIGN //%token FORTRAN //%token FOUND //%token FOUR_DIGITS //%token FULL //%token GET //%token GLOBAL //%token GO //%token GOTO //%token GRANT //conflict %token GROUP //%token HAVING //%token HOUR //%token HOURS_BETWEEN //%token IDENTITY //%token IFNULL //%token SQL_IGNORE //%token IMMEDIATE //%token INCLUDE //%token INDEX //%token INDICATOR //%token INITIALLY //%token INNER //%token SQL_INPUT %token SQL_IN //%token INSENSITIVE //%token INSERT //%token INTEGER %token INTERSECT //%token INTERVAL //%token INTO //%token IS //%token ISOLATION //%token JUSTIFY //%token LANGUAGE //%token LAST //%token LCASE //%token LENGTH //%token LEVEL %token LIKE %token ILIKE %token NOT_LIKE //%token LINE_WIDTH //%token LOCAL //%token LOCATE //%token LOG //%token SQL_LONG //%token LOWER //%token LTRIM //%token LTRIP //%token MATCH //%token SQL_MAX //%token MICROSOFT //%token SQL_MIN //%token MINUTE //%token MINUTES_BETWEEN //%token MOD //%token MODIFY //%token MODULE //%token MONTH //%token MONTHS_BETWEEN //%token MUMPS //%token NAMES //%token NATIONAL //%token NCHAR //%token NEXT //%token NODUP //%token NONE %token NOT %token NOT_EQUAL %token NOT_EQUAL2 //%token NOW //%token NULLIF //%token NUMERIC //%token OCTET_LENGTH //%token ODBC //%token OF //%token SQL_OFF //%token ONLY //%token OPEN //%token OPTION //%token OUTER //%token OUTPUT //%token OVERLAPS //%token PAGE //%token PARTIAL //%token SQL_PASCAL //%token PERSISTENT //%token CQL_PI %token OR //%token PLI //%token POSITION //%token PRECISION //%token PREPARE //%token PRESERVE //%token PRIOR //%token PRIVILEGES //%token PROCEDURE //%token PRODUCT //%token PUBLIC //%token QUARTER //%token QUIT //%token RAND //%token READ_ONLY //%token REAL //%token REFERENCES //%token REPEAT //%token REPLACE //%token RESTRICT //%token REVOKE //%token ROLLBACK //%token ROWS //%token RPAD //%token RTRIM //%token SCHEMA //%token SCREEN_WIDTH //%token SCROLL //%token SECOND //%token SECONDS_BETWEEN //%token SEQUENCE //%token SETOPT //%token SET //%token SHOWOPT //%token SIGN %token SIMILAR_TO %token NOT_SIMILAR_TO //%token SIN //%token SQL_SIZE //%token SMALLINT //%token SOME //%token SPACE //%token SQLCA //%token SQLCODE //%token SQLERROR //%token SQLSTATE //%token SQLWARNING //%token SQRT //%token STDEV //%token SUBSTRING //%token SUM //%token SYSDATE //%token SYSDATE_FORMAT //%token SYSTEM //%token TAN //%token TEMPORARY //%token THEN //%token THREE_DIGITS //%token TIME //%token TIMESTAMP //%token TIMEZONE_HOUR //%token TIMEZONE_MINUTE //%token TINYINT //%token TO //%token TO_CHAR //%token TO_DATE //%token TRANSACTION //%token TRANSLATE //%token TRANSLATION //%token TRUNCATE //%token GENERAL_TITLE //%token TWO_DIGITS //%token UCASE //%token UNIQUE //%token SQL_UNKNOWN //%token UNSIGNED_INTEGER //%token UPDATE //%token UPPER //%token USAGE //%token USER //%token ERROR_DIGIT_BEFORE_IDENTIFIER //%token USING //%token VALUE //%token VALUES //%token VARBINARY //%token VARYING //%token VENDOR //%token VIEW //%token WEEK //%token WHEN //%token WHENEVER //%token WHERE_CURRENT_OF //%token WITH //%token WORD_WRAPPED //%token WORK //%token WRAPPED %token XOR //%token YEAR //%token YEARS_BETWEEN %type IDENTIFIER %type IDENTIFIER_DOT_ASTERISK %type QUERY_PARAMETER %type CHARACTER_STRING_LITERAL %type DOUBLE_QUOTED_STRING /* %type ColExpression %type ColView */ %type ColExpression %type ColWildCard //%type ColView %type ColItem %type ColViews %type aExpr %type aExpr2 %type aExpr3 %type aExpr4 %type aExpr5 %type aExpr6 %type aExpr7 %type aExpr8 %type aExpr9 %type aExpr10 %type aExprList %type aExprList2 %type WhereClause %type OrderByClause %type OrderByOption %type OrderByColumnId %type SelectOptions %type FlatTable %type Tables %type FlatTableList %type SelectStatement %type Select /*todo : list*/ %type StatementList /*todo: not onlu select*/ %type Statement %type SQL_TYPE %type INTEGER_CONST %type REAL_CONST /*%type SIGNED_INTEGER */ %{ #include #include #include #include #include //! @todo OK? #ifdef Q_OS_WIN //workaround for bug on msvc # undef LLONG_MIN #endif #ifndef LLONG_MAX # define LLONG_MAX 0x7fffffffffffffffLL #endif #ifndef LLONG_MIN # define LLONG_MIN 0x8000000000000000LL #endif #ifndef LLONG_MAX # define ULLONG_MAX 0xffffffffffffffffLL #endif #ifdef _WIN32 # include #endif #include #include #include #include "KDbConnection.h" #include "KDbExpression.h" #include "KDbField.h" #include "KDbOrderByColumn.h" #include "KDbParser.h" #include "KDbParser_p.h" #include "KDbQuerySchema.h" #include "KDbQuerySchema_p.h" #include "KDbSqlTypes.h" #include "KDbTableSchema.h" #include "kdb_debug.h" struct OrderByColumnInternal; #ifdef Q_OS_SOLARIS #include #endif QDebug operator<<(QDebug dbg, const KDbExpressionPtr& expr) { dbg.nospace() << expr.e; return dbg.space(); } int yylex(); #define YY_NO_UNPUT #define YYSTACK_USE_ALLOCA 1 #define YYMAXDEPTH 255 extern "C" { int yywrap() { return 1; } } %} %union { QString* stringValue; QByteArray* binaryValue; qint64 integerValue; bool booleanValue; KDbOrderByColumn::SortOrder sortOrderValue; KDbField::Type colType; KDbField *field; KDbExpression *expr; KDbNArgExpression *exprList; KDbConstExpression *constExpression; KDbQuerySchema *querySchema; SelectOptionsInternal *selectOptions; QList *orderByColumns; QVariant *variantValue; } /* precedence: lowest to highest */ //%nonassoc SIMILAR //%nonassoc ESCAPE //%nonassoc OVERLAPS //%nonassoc IN_P //%left POSTFIXOP // dummy for postfix Op rules //%left Op OPERATOR // multi-character ops and user-defined operators //%nonassoc NOTNULL //%nonassoc ISNULL //%nonassoc IS // sets precedence for IS NULL, etc //%nonassoc NULL_P //%nonassoc TRUE_P //%nonassoc FALSE_P %token UMINUS // <-- To keep binary compatibility insert new tokens here. /* * These might seem to be low-precedence, but actually they are not part * of the arithmetic hierarchy at all in their use as JOIN operators. * We make them high-precedence to support their use as function names. * They wouldn't be given a precedence at all, were it not that we need * left-associativity among the JOIN rules themselves. */ /* %left JOIN %left UNIONJOIN %left CROSS %left LEFT %left FULL %left RIGHT %left INNER_P %left NATURAL */ %% TopLevelStatement : StatementList { //todo: multiple statements //todo: not only "select" statements KDbParserPrivate::get(globalParser)->setStatementType(KDbParser::Select); KDbParserPrivate::get(globalParser)->setQuerySchema($1); } ; StatementList: Statement ';' StatementList { //todo: multiple statements } | Statement | Statement ';' { $$ = $1; } ; /* Statement CreateTableStatement { YYACCEPT; } | Statement SelectStatement { } */ Statement : /*CreateTableStatement { YYACCEPT; } | */ SelectStatement { $$ = $1; } ; /*CreateTableStatement : CREATE TABLE IDENTIFIER { globalParser->setStatementType(KDbParser::CreateTable); globalParser->createTable($3->toLatin1()); delete $3; } '(' ColDefs ')' ; ColDefs: ColDefs ',' ColDef|ColDef { } ; ColDef: IDENTIFIER ColType { kdbDebug() << "adding field " << *$1; globalField->setName(*$1); globalParser->table()->addField(globalField); globalField = nullptr; delete $1; } | IDENTIFIER ColType ColKeys { kdbDebug() << "adding field " << *$1; globalField->setName(*$1); delete $1; globalParser->table()->addField(globalField); // if(globalField->isPrimaryKey()) // globalParser->table()->addPrimaryKey(globalField->name()); // delete globalField; // globalField = nullptr; } ; ColKeys: ColKeys ColKey|ColKey { } ; ColKey: PRIMARY KEY { globalField->setPrimaryKey(true); kdbDebug() << "primary"; } | NOT SQL_NULL { globalField->setNotNull(true); kdbDebug() << "not_null"; } | AUTO_INCREMENT { globalField->setAutoIncrement(true); kdbDebug() << "ainc"; } ; ColType: SQL_TYPE { globalField = new KDbField(); globalField->setType($1); } | SQL_TYPE '(' INTEGER_CONST ')' { kdbDebug() << "sql + length"; globalField = new KDbField(); globalField->setPrecision($3); globalField->setType($1); } | VARCHAR '(' INTEGER_CONST ')' { globalField = new KDbField(); globalField->setPrecision($3); globalField->setType(KDbField::Text); } | %empty { // SQLITE compatibillity globalField = new KDbField(); globalField->setType(KDbField::InvalidType); } ;*/ SelectStatement: Select { kdbDebug() << "Select"; - if (!($$ = buildSelectQuery( $1, 0 ))) - return 0; + if (!($$ = buildSelectQuery( $1, nullptr ))) + YYABORT; } | Select ColViews { kdbDebug() << "Select ColViews=" << *$2; if (!($$ = buildSelectQuery( $1, $2 ))) - return 0; + YYABORT; } | Select ColViews Tables { if (!($$ = buildSelectQuery( $1, $2, $3 ))) - return 0; + YYABORT; } | Select Tables { kdbDebug() << "Select ColViews Tables"; - if (!($$ = buildSelectQuery( $1, 0, $2 ))) - return 0; + if (!($$ = buildSelectQuery( $1, nullptr, $2 ))) + YYABORT; } | Select ColViews SelectOptions { kdbDebug() << "Select ColViews Conditions"; - if (!($$ = buildSelectQuery( $1, $2, 0, $3 ))) - return 0; + if (!($$ = buildSelectQuery( $1, $2, nullptr, $3 ))) + YYABORT; } | Select Tables SelectOptions { kdbDebug() << "Select Tables SelectOptions"; - if (!($$ = buildSelectQuery( $1, 0, $2, $3 ))) - return 0; + if (!($$ = buildSelectQuery( $1, nullptr, $2, $3 ))) + YYABORT; } | Select ColViews Tables SelectOptions { kdbDebug() << "Select ColViews Tables SelectOptions"; if (!($$ = buildSelectQuery( $1, $2, $3, $4 ))) - return 0; + YYABORT; } ; Select: SELECT { kdbDebug() << "SELECT"; $$ = KDbParserPrivate::get(globalParser)->createQuery(); } ; SelectOptions: /* todo: more options (having, group by, limit...) */ WhereClause { kdbDebug() << "WhereClause"; $$ = new SelectOptionsInternal; $$->whereExpr = *$1; delete $1; } | ORDER BY OrderByClause { kdbDebug() << "OrderByClause"; $$ = new SelectOptionsInternal; $$->orderByColumns = $3; } | WhereClause ORDER BY OrderByClause { kdbDebug() << "WhereClause ORDER BY OrderByClause"; $$ = new SelectOptionsInternal; $$->whereExpr = *$1; delete $1; $$->orderByColumns = $4; } | ORDER BY OrderByClause WhereClause { kdbDebug() << "OrderByClause WhereClause"; $$ = new SelectOptionsInternal; $$->whereExpr = *$4; delete $4; $$->orderByColumns = $3; } ; WhereClause: WHERE aExpr { $$ = $2; } ; /* todo: support "ORDER BY NULL" as described here https://dev.mysql.com/doc/refman/5.1/en/select.html */ /* todo: accept expr and position as well */ OrderByClause: OrderByColumnId { kdbDebug() << "ORDER BY IDENTIFIER"; $$ = new QList; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); $$->append( orderByColumn ); delete $1; } | OrderByColumnId OrderByOption { kdbDebug() << "ORDER BY IDENTIFIER OrderByOption"; $$ = new QList; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); orderByColumn.order = $2; $$->append( orderByColumn ); delete $1; } | OrderByColumnId ',' OrderByClause { $$ = $3; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); $$->append( orderByColumn ); delete $1; } | OrderByColumnId OrderByOption ',' OrderByClause { $$ = $4; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *$1 ); orderByColumn.order = $2; $$->append( orderByColumn ); delete $1; } ; OrderByColumnId: IDENTIFIER { $$ = new QVariant( *$1 ); kdbDebug() << "OrderByColumnId: " << *$$; delete $1; } | IDENTIFIER '.' IDENTIFIER { $$ = new QVariant( *$1 + QLatin1Char('.') + *$3 ); kdbDebug() << "OrderByColumnId: " << *$$; delete $1; delete $3; } | INTEGER_CONST { $$ = new QVariant($1); kdbDebug() << "OrderByColumnId: " << *$$; } OrderByOption: ASC { $$ = KDbOrderByColumn::SortOrder::Ascending; } | DESC { $$ = KDbOrderByColumn::SortOrder::Descending; } ; aExpr: aExpr2 ; /* --- binary logical --- */ aExpr2: aExpr3 AND aExpr2 { // kdbDebug() << "AND " << $3.debugString(); $$ = new KDbBinaryExpression(*$1, KDbToken::AND, *$3); delete $1; delete $3; } | aExpr3 OR aExpr2 { $$ = new KDbBinaryExpression(*$1, KDbToken::OR, *$3); delete $1; delete $3; } | aExpr3 XOR aExpr2 { $$ = new KDbBinaryExpression(*$1, KDbToken::XOR, *$3); delete $1; delete $3; } | aExpr3 ; /* relational op precedence */ aExpr3: aExpr4 '>' %prec GREATER_OR_EQUAL aExpr3 { $$ = new KDbBinaryExpression(*$1, '>', *$3); delete $1; delete $3; } | aExpr4 GREATER_OR_EQUAL aExpr3 { $$ = new KDbBinaryExpression(*$1, KDbToken::GREATER_OR_EQUAL, *$3); delete $1; delete $3; } | aExpr4 '<' %prec LESS_OR_EQUAL aExpr3 { $$ = new KDbBinaryExpression(*$1, '<', *$3); delete $1; delete $3; } | aExpr4 LESS_OR_EQUAL aExpr3 { $$ = new KDbBinaryExpression(*$1, KDbToken::LESS_OR_EQUAL, *$3); delete $1; delete $3; } | aExpr4 '=' aExpr3 { $$ = new KDbBinaryExpression(*$1, '=', *$3); delete $1; delete $3; } | aExpr4 ; /* relational (equality) op precedence */ aExpr4: aExpr5 NOT_EQUAL aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::NOT_EQUAL, *$3); delete $1; delete $3; } | aExpr5 NOT_EQUAL2 aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::NOT_EQUAL2, *$3); delete $1; delete $3; } | aExpr5 LIKE aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::LIKE, *$3); delete $1; delete $3; } | aExpr5 NOT_LIKE aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::NOT_LIKE, *$3); delete $1; delete $3; } | aExpr5 SQL_IN aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::SQL_IN, *$3); delete $1; delete $3; } | aExpr5 SIMILAR_TO aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::SIMILAR_TO, *$3); delete $1; delete $3; } | aExpr5 NOT_SIMILAR_TO aExpr4 { $$ = new KDbBinaryExpression(*$1, KDbToken::NOT_SIMILAR_TO, *$3); delete $1; delete $3; } | aExpr5 BETWEEN aExpr4 AND aExpr4 { $$ = new KDbNArgExpression(KDb::RelationalExpression, KDbToken::BETWEEN_AND); $$->toNArg().append( *$1 ); $$->toNArg().append( *$3 ); $$->toNArg().append( *$5 ); delete $1; delete $3; delete $5; } | aExpr5 NOT_BETWEEN aExpr4 AND aExpr4 { $$ = new KDbNArgExpression(KDb::RelationalExpression, KDbToken::NOT_BETWEEN_AND); $$->toNArg().append( *$1 ); $$->toNArg().append( *$3 ); $$->toNArg().append( *$5 ); delete $1; delete $3; delete $5; } | aExpr5 ; /* --- unary logical right --- */ aExpr5: aExpr5 SQL_IS_NULL { $$ = new KDbUnaryExpression( KDbToken::SQL_IS_NULL, *$1 ); delete $1; } | aExpr5 SQL_IS_NOT_NULL { $$ = new KDbUnaryExpression( KDbToken::SQL_IS_NOT_NULL, *$1 ); delete $1; } | aExpr6 ; /* arithm. lowest precedence */ aExpr6: aExpr7 BITWISE_SHIFT_LEFT aExpr6 { $$ = new KDbBinaryExpression(*$1, KDbToken::BITWISE_SHIFT_LEFT, *$3); delete $1; delete $3; } | aExpr7 BITWISE_SHIFT_RIGHT aExpr6 { $$ = new KDbBinaryExpression(*$1, KDbToken::BITWISE_SHIFT_RIGHT, *$3); delete $1; delete $3; } | aExpr7 ; /* arithm. lower precedence */ aExpr7: aExpr8 '+' aExpr7 { $$ = new KDbBinaryExpression(*$1, '+', *$3); delete $1; delete $3; } | aExpr8 CONCATENATION aExpr7 { $$ = new KDbBinaryExpression(*$1, KDbToken::CONCATENATION, *$3); delete $1; delete $3; } | aExpr8 '-' %prec UMINUS aExpr7 { $$ = new KDbBinaryExpression(*$1, '-', *$3); delete $1; delete $3; } | aExpr8 '&' aExpr7 { $$ = new KDbBinaryExpression(*$1, '&', *$3); delete $1; delete $3; } | aExpr8 '|' aExpr7 { $$ = new KDbBinaryExpression(*$1, '|', *$3); delete $1; delete $3; } | aExpr8 ; /* arithm. higher precedence */ aExpr8: aExpr9 '/' aExpr8 { $$ = new KDbBinaryExpression(*$1, '/', *$3); delete $1; delete $3; } | aExpr9 '*' aExpr8 { $$ = new KDbBinaryExpression(*$1, '*', *$3); delete $1; delete $3; } | aExpr9 '%' aExpr8 { $$ = new KDbBinaryExpression(*$1, '%', *$3); delete $1; delete $3; } | aExpr9 ; /* parenthesis, unary operators, and terminals precedence */ aExpr9: /* --- unary logical left --- */ '-' aExpr9 { $$ = new KDbUnaryExpression( '-', *$2 ); delete $2; } | '+' aExpr9 { $$ = new KDbUnaryExpression( '+', *$2 ); delete $2; } | '~' aExpr9 { $$ = new KDbUnaryExpression( '~', *$2 ); delete $2; } | NOT aExpr9 { $$ = new KDbUnaryExpression( KDbToken::NOT, *$2 ); delete $2; } | IDENTIFIER { $$ = new KDbVariableExpression( *$1 ); //! @todo simplify this later if that's 'only one field name' expression kdbDebug() << " + identifier: " << *$1; delete $1; } | QUERY_PARAMETER { $$ = new KDbQueryParameterExpression( *$1 ); kdbDebug() << " + query parameter:" << *$$; delete $1; } | IDENTIFIER aExprList { kdbDebug() << " + function:" << *$1 << "(" << *$2 << ")"; $$ = new KDbFunctionExpression(*$1, *$2); delete $1; delete $2; } /*! @todo shall we also support db name? */ | IDENTIFIER '.' IDENTIFIER { $$ = new KDbVariableExpression( *$1 + QLatin1Char('.') + *$3 ); kdbDebug() << " + identifier.identifier:" << *$1 << "." << *$3; delete $1; delete $3; } | SQL_NULL { $$ = new KDbConstExpression( KDbToken::SQL_NULL, QVariant() ); kdbDebug() << " + NULL"; // $$ = new KDbField(); //$$->setName(QString::null); } | SQL_TRUE { $$ = new KDbConstExpression( KDbToken::SQL_TRUE, true ); } | SQL_FALSE { $$ = new KDbConstExpression( KDbToken::SQL_FALSE, false ); } | CHARACTER_STRING_LITERAL { $$ = new KDbConstExpression( KDbToken::CHARACTER_STRING_LITERAL, *$1 ); kdbDebug() << " + constant " << $1; delete $1; } | INTEGER_CONST { QVariant val; if ($1 <= INT_MAX && $1 >= INT_MIN) val = (int)$1; else if ($1 <= UINT_MAX && $1 >= 0) val = (uint)$1; else if ($1 <= LLONG_MAX && $1 >= LLONG_MIN) val = (qint64)$1; // if ($1 < ULLONG_MAX) // val = (quint64)$1; //! @todo ok? $$ = new KDbConstExpression( KDbToken::INTEGER_CONST, val ); kdbDebug() << " + int constant: " << val.toString(); } | REAL_CONST { $$ = new KDbConstExpression( KDbToken::REAL_CONST, *$1 ); kdbDebug() << " + real constant: " << *$1; delete $1; } | aExpr10 ; aExpr10: '(' aExpr ')' { kdbDebug() << "(expr)"; $$ = new KDbUnaryExpression('(', *$2); delete $2; } ; aExprList: '(' aExprList2 ')' { $$ = $2; } | '(' ')' { $$ = new KDbNArgExpression(KDb::ArgumentListExpression, ','); } ; aExprList2: aExpr ',' aExprList2 { $$ = $3; $$->prepend( *$1 ); delete $1; } | aExpr { $$ = new KDbNArgExpression(KDb::ArgumentListExpression, ','); $$->append( *$1 ); delete $1; } ; Tables: FROM FlatTableList { $$ = $2; } /* | Tables LEFT JOIN IDENTIFIER SQL_ON ColExpression { kdbDebug() << "LEFT JOIN: '" << *$4 << "' ON " << $6; addTable($4->toQString()); delete $4; } | Tables LEFT OUTER JOIN IDENTIFIER SQL_ON ColExpression { kdbDebug() << "LEFT OUTER JOIN: '" << $5 << "' ON " << $7; addTable($5); } | Tables INNER JOIN IDENTIFIER SQL_ON ColExpression { kdbDebug() << "INNER JOIN: '" << *$4 << "' ON " << $6; addTable($4->toQString()); delete $4; } | Tables RIGHT JOIN IDENTIFIER SQL_ON ColExpression { kdbDebug() << "RIGHT JOIN: '" << *$4 << "' ON " << $6; addTable(*$4); delete $4; } | Tables RIGHT OUTER JOIN IDENTIFIER SQL_ON ColExpression { kdbDebug() << "RIGHT OUTER JOIN: '" << *$5 << "' ON " << $7; addTable($5->toQString()); delete $5; }*/ ; /* FlatTableList: aFlatTableList { $$ } ;*/ FlatTableList: FlatTableList ',' FlatTable { $$ = $1; $$->append(*$3); delete $3; } |FlatTable { $$ = new KDbNArgExpression(KDb::TableListExpression, KDbToken::IDENTIFIER); //ok? $$->append(*$1); delete $1; } ; FlatTable: IDENTIFIER { kdbDebug() << "FROM: '" << *$1 << "'"; $$ = new KDbVariableExpression(*$1); //! @todo this isn't ok for more tables: /* KDbField::ListIterator it = globalParser->query()->fieldsIterator(); for(KDbField *item; (item = it.current()); ++it) { if(item->table() == dummy) { item->setTable(schema); } if(item->table() && !item->isQueryAsterisk()) { KDbField *f = item->table()->field(item->name()); if(!f) { KDbParserError err(KDbParser::tr("Field List Error"), KDbParser::tr("Unknown column '%1' in table '%2'",item->name(),schema->name()), ctoken, current); globalParser->setError(err); yyerror("fieldlisterror"); } } }*/ delete $1; } | IDENTIFIER IDENTIFIER { //table + alias $$ = new KDbBinaryExpression( KDbVariableExpression(*$1), KDbToken::AS_EMPTY, KDbVariableExpression(*$2) ); delete $1; delete $2; } | IDENTIFIER AS IDENTIFIER { //table + alias $$ = new KDbBinaryExpression( KDbVariableExpression(*$1), KDbToken::AS, KDbVariableExpression(*$3) ); delete $1; delete $3; } ; ColViews: ColViews ',' ColItem { $$ = $1; $$->append(*$3); delete $3; kdbDebug() << "ColViews: ColViews , ColItem"; } |ColItem { $$ = new KDbNArgExpression(KDb::FieldListExpression, KDbToken()); $$->append(*$1); delete $1; kdbDebug() << "ColViews: ColItem"; } ; ColItem: ColExpression { // $$ = new KDbField(); // dummy->addField($$); // $$->setExpression( $1 ); // globalParser->query()->addField($$); $$ = $1; kdbDebug() << " added column expr:" << *$1; } | ColWildCard { $$ = $1; kdbDebug() << " added column wildcard:" << *$1; } | ColExpression AS IDENTIFIER { $$ = new KDbBinaryExpression( *$1, KDbToken::AS, KDbVariableExpression(*$3) ); kdbDebug() << " added column expr:" << *$$; delete $1; delete $3; } | ColExpression IDENTIFIER { $$ = new KDbBinaryExpression( *$1, KDbToken::AS_EMPTY, KDbVariableExpression(*$2) ); kdbDebug() << " added column expr:" << *$$; delete $1; delete $2; } ; ColExpression: aExpr { $$ = $1; } /* HANDLED BY 'IDENTIFIER aExprList' | IDENTIFIER '(' ColViews ')' { $$ = new KDbFunctionExpression( $1, $3 ); }*/ //! @todo /* | SUM '(' ColExpression ')' { KDbFunctionExpression( // $$ = new AggregationExpression( SUM, ); // $$->setName("SUM(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait globalParser->query()->grouped(true); }*/ //! @todo /* | SQL_MIN '(' ColExpression ')' { $$ = $3; // $$->setName("MIN(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait globalParser->query()->grouped(true); }*/ //! @todo /* | SQL_MAX '(' ColExpression ')' { $$ = $3; // $$->setName("MAX(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait globalParser->query()->grouped(true); }*/ //! @todo /* | AVG '(' ColExpression ')' { $$ = $3; // $$->setName("AVG(" + $3->name() + ")"); //wait $$->containsGroupingAggregate(true); //wait globalParser->query()->grouped(true); }*/ | DISTINCT '(' ColExpression ')' { $$ = $3; //! @todo DISTINCT '(' ColExpression ')' // $$->setName("DISTINCT(" + $3->name() + ")"); } ; ColWildCard: '*' { $$ = new KDbVariableExpression(QLatin1String("*")); kdbDebug() << "all columns"; // KDbQueryAsterisk *ast = new KDbQueryAsterisk(globalParser->query(), dummy); // globalParser->query()->addAsterisk(ast); // requiresTable = true; } | IDENTIFIER '.' '*' { QString s( *$1 ); s += QLatin1String(".*"); $$ = new KDbVariableExpression(s); kdbDebug() << " + all columns from " << s; delete $1; } /*| ERROR_DIGIT_BEFORE_IDENTIFIER { $$ = new KDbVariableExpression($1); kdbDebug() << " Invalid identifier! " << $1; setError(KDbParser::tr("Invalid identifier \"%1\"",$1)); }*/ ; %% diff --git a/src/parser/generate_parser_code.sh b/src/parser/generate_parser_code.sh index 184271fa..d074b723 100755 --- a/src/parser/generate_parser_code.sh +++ b/src/parser/generate_parser_code.sh @@ -1,390 +1,390 @@ #!/bin/bash # # Copyright (C) 2006-2015 Jarosław Staniek # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. # # Generates parser and lexer code using bison and flex # me=generate_parser_code.sh BISON_MIN=3.0.4 # keep updated for best results BISON_MIN_NUM=30004 # keep updated for best results FLEX_MIN=2.5.37 # keep updated for best results FLEX_MIN_NUM=20537 # keep updated for best results # Check minimum version of bison bisonv=`bison --version | head -n 1| cut -f4 -d" "` bisonv1=`echo $bisonv | cut -f1 -d.` bisonv2=`echo $bisonv | cut -f2 -d.` bisonv3=`echo $bisonv | cut -f3 -d.` if [ -z $bisonv3 ] ; then bisonv3=0; fi bisonvnum=`expr $bisonv1 \* 10000 + $bisonv2 \* 100 + $bisonv3` if [ $bisonvnum -lt $BISON_MIN_NUM ] ; then echo "$bisonv is too old bison version, the minimum is $BISON_MIN." exit 1 fi # Check minimum version of flex flexv=`flex --version | head -n 1| cut -f2 -d" "` flexv1=`echo $flexv | cut -f1 -d.` flexv2=`echo $flexv | cut -f2 -d.` flexv3=`echo $flexv | cut -f3 -d.` flexvnum=`expr $flexv1 \* 10000 + $flexv2 \* 100 + $flexv3` if [ $flexvnum -lt $FLEX_MIN_NUM ] ; then echo "$flexv is too old flex version, the minimum is $FLEX_MIN." exit 1 fi # Generate lexer and parser builddir=$PWD srcdir=`dirname $0` cd $srcdir flex -ogenerated/sqlscanner.cpp KDbSqlScanner.l # Correct a few yy_size_t vs size_t vs int differences that some bisons cause sed --in-place 's/int yyleng/yy_size_t yyleng/g;s/int yyget_leng/yy_size_t yyget_leng/g;s/yyleng = (int)/yyleng = (size_t)/g;' generated/sqlscanner.cpp bison -d KDbSqlParser.y -Wall -fall -rall --report-file=$builddir/KDbSqlParser.output # postprocess cat << EOF > generated/sqlparser.h /**************************************************************************** * Created by $me * WARNING! All changes made in this file will be lost! ****************************************************************************/ #ifndef KDBSQLPARSER_H #define KDBSQLPARSER_H #include "KDbExpression.h" #include "KDbField.h" #include "KDbOrderByColumn.h" struct OrderByColumnInternal; struct SelectOptionsInternal; EOF # Fine-tune the code: extra functions and remove trailing white space cat KDbSqlParser.tab.h >> generated/sqlparser.h echo '#endif' >> generated/sqlparser.h sed --in-place 's/[[:space:]]\+$//;s/\t/ /g' generated/sqlparser.h cat KDbSqlParser.tab.c | sed -e "s/KDbSqlParser\.tab\.c/sqlparser.cpp/g" > generated/sqlparser.cpp cat << EOF >> generated/sqlparser.cpp KDB_TESTING_EXPORT const char* g_tokenName(unsigned int offset) { const int t = YYTRANSLATE(offset); if (t >= YYTRANSLATE(::SQL_TYPE)) { return yytname[t]; } - return 0; + return nullptr; } //static const int KDbToken::maxCharTokenValue = 253; //static const int KDbToken::maxTokenValue = YYMAXUTOK; EOF sed --in-place 's/[[:space:]]\+$//;s/\t/ /g' generated/sqlparser.cpp # Extract table of SQL tokens # unused ./extract_tokens.sh > generated/tokens.cpp rm -f KDbSqlParser.tab.h KDbSqlParser.tab.c # Create KDbToken.h cat << EOF > generated/KDbToken.h /**************************************************************************** * Created by $me * WARNING! All changes made in this file will be lost! ****************************************************************************/ /* This file is part of the KDE project Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KDB_TOKEN_H #define KDB_TOKEN_H #include "kdb_export.h" #include class KDbDriver; /*! @brief A type-safe KDbSQL token It can be used in KDb expressions @see KDbExpression */ class KDB_EXPORT KDbToken { public: //! @todo add KDbToken(const QByteArray &name) //! Creates an invalid token inline KDbToken() : v(0) {} KDbToken(const KDbToken &other) : v(other.v) {} //! Creates a single-character token //! Only characters that belong to the grammar are accepted: EOF (echo -n " //! "; grep "\"'.'\"," generated/sqlparser.cpp \ | sed -e ":a;N;s/\"\('.'\)\",/\1/g;s/\n//g;s/\".*,//g;s/^ *//g;s/ *$//g;") >> generated/KDbToken.h cat << EOF >> generated/KDbToken.h //! Invalid KDbToken is created for character that is not accepted. KDbToken(char charToken); //! @return true if this token is valid inline bool isValid() const { return v != 0; } //! @return name of this token //! Useful for debugging. //! For example "NOT_EQUAL" string is returned for the NOT_EQUAL token. //! A single character is returned for printable single-character tokens. //! A number is returned for non-printable single-character. //! "" is returned for an invalid string. QString name() const; //! @return string interpretation of this token (as visibe to the user) //! For example "<>" is returned for the NOT_EQUAL token. //! Empty string is returned for an invalid string //! The result may depend on the optional @a driver parameter. //! If @a driver is @c nullptr, representation for portable KDbSQL dialect is returned. QString toString(const KDbDriver *driver = nullptr) const; //! Like toString(const KDbDriver *driver) static QString toString(KDbToken token, const KDbDriver *driver = nullptr); //! Maximum character token value (253) static const int maxCharTokenValue; //! Maximum character token value static const int maxTokenValue; //! @return character equivalent of this token //! Only character-based tokens are supported this way (toInt() <= maxCharTokenValue). //! For unsupported tokens @c nullptr is returned. inline char toChar() const { return v <= maxCharTokenValue ? v : 0; } //! @return numeric value of this token inline int value() const { return v; } //! @return true if this token is equal to @a other token inline bool operator==(KDbToken other) const { return v == other.v; } //! @return true if this token is not equal to @a other token inline bool operator!=(KDbToken other) const { return v != other.v; } //! @return true if this token is equal to @a other token inline bool operator==(char charToken) const { return v == charToken; } //! @return true if this token is not equal to @a other token inline bool operator!=(char charToken) const { return v != charToken; } static QList allTokens(); // -- constants go here -- EOF function extractTokens() { grep -E " [A-Z0-9_]+ = [[:digit:]]+" generated/sqlparser.h \ | sed -e "s/^ //g;s/ = / /g;s/,//g" } extractTokens | while read token value; do echo " static const KDbToken $token;" done >> generated/KDbToken.h function customTokens() { cat << EOF BETWEEN_AND 0x1001 NOT_BETWEEN_AND 0x1002 EOF } echo " //! Custom tokens are not used in parser but used as an extension in expression classes." >> generated/KDbToken.h customTokens | while read token value; do echo " static const KDbToken $token;" done >> generated/KDbToken.h cat << EOF >> generated/KDbToken.h // -- end of constants -- class List; private: inline KDbToken(int value) : v(value) {} int v; }; //! Sends information about token @a token to debug output @a dbg. KDB_EXPORT QDebug operator<<(QDebug dbg, KDbToken token); #endif EOF cat << EOF > generated/KDbToken.cpp /**************************************************************************** * Created by $me * WARNING! All changes made in this file will be lost! ****************************************************************************/ /* This file is part of the KDE project Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KDbToken.h" #include "KDbDriver.h" #include "KDbDriver_p.h" #include "KDbDriverBehavior.h" #include "sqlparser.h" #include "parser/KDbParser_p.h" #include KDbToken::KDbToken(char charToken) : v(g_tokenName(charToken) == nullptr ? 0 : charToken) { } QString KDbToken::name() const { if (!isValid()) { return QLatin1String(""); } if (v > maxCharTokenValue) { return QLatin1String(g_tokenName(v)); } if (isprint(v)) { return QString(QLatin1Char(char(v))); } else { return QLatin1String(QByteArray::number(v)); } } QString KDbToken::toString(const KDbDriver *driver) const { if (toChar() > 0) { return name(); } // other arithmetic operations: << >> // NOTE: only include cases that have toString() != name() or are dependent on driver switch (v) { case ::BITWISE_SHIFT_RIGHT: return QLatin1String(">>"); case ::BITWISE_SHIFT_LEFT: return QLatin1String("<<"); // other relational operations: <= >= <> (or !=) LIKE IN case ::NOT_EQUAL: return QLatin1String("<>"); case ::NOT_EQUAL2: return QLatin1String("!="); case ::LESS_OR_EQUAL: return QLatin1String("<="); case ::GREATER_OR_EQUAL: return QLatin1String(">="); case ::LIKE: return driver ? KDbDriverPrivate::behavior(driver)->LIKE_OPERATOR : QLatin1String("LIKE"); case ::NOT_LIKE: return driver ? (QString::fromLatin1("NOT ") + KDbDriverPrivate::behavior(driver)->LIKE_OPERATOR) : QString::fromLatin1("NOT LIKE"); case ::SQL_IN: return QLatin1String("IN"); // other logical operations: OR (or ||) AND (or &&) XOR case ::SIMILAR_TO: return QLatin1String("SIMILAR TO"); case ::NOT_SIMILAR_TO: return QLatin1String("NOT SIMILAR TO"); // other string operations: || (as CONCATENATION) case ::CONCATENATION: return QLatin1String("||"); // SpecialBinary "pseudo operators": /* not handled here */ default:; } const QString s = name(); if (!s.isEmpty()) { return s; } return QString::fromLatin1(" ").arg(v); } //static QString KDbToken::toString(KDbToken token, const KDbDriver *driver) { return token.toString(driver); } KDB_EXPORT QDebug operator<<(QDebug dbg, KDbToken token) { dbg.nospace() << qPrintable(token.name()); return dbg.space(); } //! @internal class KDbToken::List { public: List() { for (int i = 0; i < KDbToken::maxTokenValue; ++i) { if (g_tokenName(i)) { data.append(KDbToken(i)); } } } QList data; }; Q_GLOBAL_STATIC(KDbToken::List, g_allTokens) //static QList KDbToken::allTokens() { return g_allTokens->data; } EOF extractTokens | while read token value; do echo "const KDbToken KDbToken::$token(::$token);" done >> generated/KDbToken.cpp customTokens | while read token value; do echo "const KDbToken KDbToken::$token($value);" done >> generated/KDbToken.cpp diff --git a/src/parser/generated/sqlparser.cpp b/src/parser/generated/sqlparser.cpp index f4e46d8d..ba039607 100644 --- a/src/parser/generated/sqlparser.cpp +++ b/src/parser/generated/sqlparser.cpp @@ -1,2698 +1,2698 @@ /* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Copy the first part of user declarations. */ #line 421 "KDbSqlParser.y" /* yacc.c:339 */ #include #include #include #include #include //! @todo OK? #ifdef Q_OS_WIN //workaround for bug on msvc # undef LLONG_MIN #endif #ifndef LLONG_MAX # define LLONG_MAX 0x7fffffffffffffffLL #endif #ifndef LLONG_MIN # define LLONG_MIN 0x8000000000000000LL #endif #ifndef LLONG_MAX # define ULLONG_MAX 0xffffffffffffffffLL #endif #ifdef _WIN32 # include #endif #include #include #include #include "KDbConnection.h" #include "KDbExpression.h" #include "KDbField.h" #include "KDbOrderByColumn.h" #include "KDbParser.h" #include "KDbParser_p.h" #include "KDbQuerySchema.h" #include "KDbQuerySchema_p.h" #include "KDbSqlTypes.h" #include "KDbTableSchema.h" #include "kdb_debug.h" struct OrderByColumnInternal; #ifdef Q_OS_SOLARIS #include #endif QDebug operator<<(QDebug dbg, const KDbExpressionPtr& expr) { dbg.nospace() << expr.e; return dbg.space(); } int yylex(); #define YY_NO_UNPUT #define YYSTACK_USE_ALLOCA 1 #define YYMAXDEPTH 255 extern "C" { int yywrap() { return 1; } } #line 135 "sqlparser.cpp" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "KDbSqlParser.tab.h". */ #ifndef YY_YY_KDBSQLPARSER_TAB_H_INCLUDED # define YY_YY_KDBSQLPARSER_TAB_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { SQL_TYPE = 258, AS = 259, AS_EMPTY = 260, ASC = 261, AUTO_INCREMENT = 262, BIT = 263, BITWISE_SHIFT_LEFT = 264, BITWISE_SHIFT_RIGHT = 265, BY = 266, CHARACTER_STRING_LITERAL = 267, CONCATENATION = 268, CREATE = 269, DESC = 270, DISTINCT = 271, DOUBLE_QUOTED_STRING = 272, FROM = 273, JOIN = 274, KEY = 275, LEFT = 276, LESS_OR_EQUAL = 277, GREATER_OR_EQUAL = 278, SQL_NULL = 279, SQL_IS = 280, SQL_IS_NULL = 281, SQL_IS_NOT_NULL = 282, ORDER = 283, PRIMARY = 284, SELECT = 285, INTEGER_CONST = 286, REAL_CONST = 287, RIGHT = 288, SQL_ON = 289, DATE_CONST = 290, DATETIME_CONST = 291, TIME_CONST = 292, TABLE = 293, IDENTIFIER = 294, IDENTIFIER_DOT_ASTERISK = 295, QUERY_PARAMETER = 296, VARCHAR = 297, WHERE = 298, SQL = 299, SQL_TRUE = 300, SQL_FALSE = 301, UNION = 302, SCAN_ERROR = 303, AND = 304, BETWEEN = 305, NOT_BETWEEN = 306, EXCEPT = 307, SQL_IN = 308, INTERSECT = 309, LIKE = 310, ILIKE = 311, NOT_LIKE = 312, NOT = 313, NOT_EQUAL = 314, NOT_EQUAL2 = 315, OR = 316, SIMILAR_TO = 317, NOT_SIMILAR_TO = 318, XOR = 319, UMINUS = 320 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 490 "KDbSqlParser.y" /* yacc.c:355 */ QString* stringValue; QByteArray* binaryValue; qint64 integerValue; bool booleanValue; KDbOrderByColumn::SortOrder sortOrderValue; KDbField::Type colType; KDbField *field; KDbExpression *expr; KDbNArgExpression *exprList; KDbConstExpression *constExpression; KDbQuerySchema *querySchema; SelectOptionsInternal *selectOptions; QList *orderByColumns; QVariant *variantValue; #line 258 "sqlparser.cpp" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; int yyparse (void); #endif /* !YY_YY_KDBSQLPARSER_TAB_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 275 "sqlparser.cpp" /* yacc.c:358 */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif #if !defined _Noreturn \ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) # if defined _MSC_VER && 1200 <= _MSC_VER # define _Noreturn __declspec (noreturn) # else # define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 7 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 221 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 82 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 30 /* YYNRULES -- Number of rules. */ #define YYNRULES 101 /* YYNSTATES -- Number of states. */ #define YYNSTATES 162 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 320 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 78, 74, 2, 80, 81, 77, 72, 67, 73, 68, 76, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 66, 70, 71, 69, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 75, 2, 79, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 545, 545, 555, 559, 560, 575, 674, 680, 687, 692, 698, 704, 710, 719, 727, 734, 740, 748, 759, 768, 777, 787, 795, 807, 813, 820, 827, 831, 838, 843, 850, 856, 863, 868, 874, 880, 886, 892, 899, 904, 910, 916, 922, 928, 934, 940, 946, 956, 967, 972, 977, 983, 988, 994, 1001, 1006, 1012, 1018, 1024, 1030, 1037, 1042, 1048, 1054, 1061, 1067, 1072, 1077, 1082, 1087, 1095, 1101, 1109, 1116, 1123, 1127, 1131, 1137, 1154, 1161, 1166, 1175, 1179, 1186, 1192, 1201, 1246, 1252, 1261, 1289, 1299, 1314, 1321, 1331, 1340, 1345, 1355, 1368, 1414, 1423, 1432 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "SQL_TYPE", "AS", "AS_EMPTY", "ASC", "AUTO_INCREMENT", "BIT", "BITWISE_SHIFT_LEFT", "BITWISE_SHIFT_RIGHT", "BY", "CHARACTER_STRING_LITERAL", "CONCATENATION", "CREATE", "DESC", "DISTINCT", "DOUBLE_QUOTED_STRING", "FROM", "JOIN", "KEY", "LEFT", "LESS_OR_EQUAL", "GREATER_OR_EQUAL", "SQL_NULL", "SQL_IS", "SQL_IS_NULL", "SQL_IS_NOT_NULL", "ORDER", "PRIMARY", "SELECT", "INTEGER_CONST", "REAL_CONST", "RIGHT", "SQL_ON", "DATE_CONST", "DATETIME_CONST", "TIME_CONST", "TABLE", "IDENTIFIER", "IDENTIFIER_DOT_ASTERISK", "QUERY_PARAMETER", "VARCHAR", "WHERE", "SQL", "SQL_TRUE", "SQL_FALSE", "UNION", "SCAN_ERROR", "AND", "BETWEEN", "NOT_BETWEEN", "EXCEPT", "SQL_IN", "INTERSECT", "LIKE", "ILIKE", "NOT_LIKE", "NOT", "NOT_EQUAL", "NOT_EQUAL2", "OR", "SIMILAR_TO", "NOT_SIMILAR_TO", "XOR", "UMINUS", "';'", "','", "'.'", "'>'", "'<'", "'='", "'+'", "'-'", "'&'", "'|'", "'/'", "'*'", "'%'", "'~'", "'('", "')'", "$accept", "TopLevelStatement", "StatementList", "Statement", "SelectStatement", "Select", "SelectOptions", "WhereClause", "OrderByClause", "OrderByColumnId", "OrderByOption", "aExpr", "aExpr2", "aExpr3", "aExpr4", "aExpr5", "aExpr6", "aExpr7", "aExpr8", "aExpr9", "aExpr10", "aExprList", "aExprList2", "Tables", "FlatTableList", "FlatTable", "ColViews", "ColItem", "ColExpression", "ColWildCard", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 59, 44, 46, 62, 60, 61, 43, 45, 38, 124, 47, 42, 37, 126, 40, 41 }; # endif #define YYPACT_NINF -127 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-127))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int16 yypact[] = { -15, -127, 39, -127, -21, -127, 12, -127, -15, -127, -11, 29, -127, -127, -127, -45, -127, -127, -127, 125, 125, 125, -127, 125, 125, -127, -127, -23, -5, 158, -127, 52, 58, 23, -127, -12, 9, -127, 10, -127, -127, 82, 15, 8, -127, -27, 1, -127, -13, -127, -127, -127, -127, -4, 125, 125, 125, 125, 125, 125, 125, 125, -127, -127, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 75, 125, -127, 60, 71, -127, -12, 51, -127, 16, 54, -127, 29, -127, -127, -127, 55, 53, 57, -127, -127, -127, -127, -127, -127, -127, -127, -127, 86, 87, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, 17, -127, 127, -127, -127, -127, -127, -127, -127, 125, -127, 125, 125, -127, 73, 96, 5, 17, -127, -127, -127, 103, -127, -127, -127, 17, 78, -127, -127, -127, 17, -127 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 14, 0, 2, 4, 6, 7, 1, 5, 77, 0, 0, 74, 78, 79, 70, 71, 75, 76, 0, 0, 0, 100, 0, 0, 98, 29, 33, 39, 49, 52, 55, 61, 65, 80, 10, 8, 93, 94, 95, 3, 0, 89, 86, 88, 0, 0, 72, 70, 69, 67, 66, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 15, 0, 11, 9, 0, 97, 0, 0, 90, 0, 73, 101, 83, 85, 0, 0, 81, 30, 31, 32, 37, 35, 34, 36, 38, 0, 0, 44, 42, 43, 40, 41, 45, 46, 53, 54, 57, 56, 58, 59, 60, 62, 63, 64, 0, 19, 0, 92, 13, 96, 99, 91, 87, 0, 82, 0, 0, 26, 24, 16, 20, 0, 84, 47, 48, 0, 18, 27, 28, 0, 21, 17, 25, 22, 0, 23 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -127, -127, 138, -127, -127, -127, -26, 2, -126, -127, -127, -24, 64, 115, -63, -127, 31, 102, 44, 88, -127, -127, 13, 117, -127, 63, -127, 72, 119, -127 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 3, 4, 5, 6, 85, 86, 145, 146, 156, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 47, 100, 35, 43, 44, 36, 37, 38, 39 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { 53, 111, 112, 113, 114, 115, 116, 117, 118, 119, 88, 153, 96, 9, 90, 1, 83, 57, 58, 93, 154, 157, 99, 45, 9, 12, 54, 11, 10, 159, 11, 84, 13, 14, 161, 46, 12, 83, 55, 7, 48, 56, 16, 13, 14, 8, 17, 18, 143, 91, 97, 15, 84, 16, 94, 101, 144, 17, 18, 19, 131, 73, 74, 134, 59, 60, 61, 46, 42, 41, 19, 75, 155, 20, 21, 95, 87, 102, 149, 150, 23, 24, 98, 9, 20, 21, 130, 10, 132, 22, 135, 23, 24, 137, 9, 12, 96, 136, 10, 80, 81, 82, 13, 14, 120, 121, 12, 49, 50, 51, 15, 52, 16, 13, 14, 99, 17, 18, 103, 104, 105, 48, 139, 16, 127, 128, 129, 17, 18, 19, 76, 77, 78, 79, 140, 141, 142, 9, 147, 84, 19, 151, 158, 20, 21, 160, 40, 152, 22, 12, 23, 24, 148, 89, 20, 21, 13, 14, 138, 133, 92, 23, 24, 0, 48, 0, 16, 0, 0, 0, 17, 18, 106, 107, 108, 109, 110, 122, 123, 124, 125, 126, 0, 19, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 21, 0, 0, 0, 0, 0, 23, 24, 0, 0, 64, 65, 0, 66, 0, 67, 0, 68, 0, 69, 70, 0, 71, 72 }; static const yytype_int16 yycheck[] = { 24, 64, 65, 66, 67, 68, 69, 70, 71, 72, 36, 6, 39, 12, 4, 30, 28, 22, 23, 4, 15, 147, 46, 68, 12, 24, 49, 18, 16, 155, 18, 43, 31, 32, 160, 80, 24, 28, 61, 0, 39, 64, 41, 31, 32, 66, 45, 46, 31, 39, 77, 39, 43, 41, 39, 68, 39, 45, 46, 58, 84, 9, 10, 89, 69, 70, 71, 80, 39, 80, 58, 13, 67, 72, 73, 67, 67, 81, 141, 142, 79, 80, 81, 12, 72, 73, 11, 16, 28, 77, 39, 79, 80, 39, 12, 24, 39, 81, 16, 76, 77, 78, 31, 32, 73, 74, 24, 19, 20, 21, 39, 23, 41, 31, 32, 139, 45, 46, 54, 55, 56, 39, 67, 41, 80, 81, 82, 45, 46, 58, 72, 73, 74, 75, 81, 49, 49, 12, 11, 43, 58, 68, 39, 72, 73, 67, 8, 145, 77, 24, 79, 80, 139, 36, 72, 73, 31, 32, 95, 87, 41, 79, 80, -1, 39, -1, 41, -1, -1, -1, 45, 46, 57, 58, 59, 60, 61, 75, 76, 77, 78, 79, -1, 58, 26, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 73, -1, -1, -1, -1, -1, 79, 80, -1, -1, 50, 51, -1, 53, -1, 55, -1, 57, -1, 59, 60, -1, 62, 63 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 30, 83, 84, 85, 86, 87, 0, 66, 12, 16, 18, 24, 31, 32, 39, 41, 45, 46, 58, 72, 73, 77, 79, 80, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 105, 108, 109, 110, 111, 84, 80, 39, 106, 107, 68, 80, 103, 39, 101, 101, 101, 101, 93, 49, 61, 64, 22, 23, 69, 70, 71, 26, 27, 50, 51, 53, 55, 57, 59, 60, 62, 63, 9, 10, 13, 72, 73, 74, 75, 76, 77, 78, 28, 43, 88, 89, 67, 88, 105, 4, 39, 110, 4, 39, 67, 39, 77, 81, 93, 104, 68, 81, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 98, 98, 99, 99, 99, 99, 99, 100, 100, 100, 11, 93, 28, 109, 88, 39, 81, 39, 107, 67, 81, 49, 49, 31, 39, 90, 91, 11, 104, 96, 96, 68, 89, 6, 15, 67, 92, 90, 39, 90, 67, 90 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 82, 83, 84, 84, 84, 85, 86, 86, 86, 86, 86, 86, 86, 87, 88, 88, 88, 88, 89, 90, 90, 90, 90, 91, 91, 91, 92, 92, 93, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 98, 98, 98, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 103, 103, 104, 104, 105, 106, 106, 107, 107, 107, 108, 108, 109, 109, 109, 109, 110, 110, 111, 111 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 3, 1, 2, 1, 1, 2, 3, 2, 3, 3, 4, 1, 1, 3, 4, 4, 2, 1, 2, 3, 4, 1, 3, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 1, 2, 2, 1, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 3, 3, 2, 3, 1, 2, 3, 1, 1, 2, 3, 3, 1, 1, 1, 3, 2, 1, 4, 1, 3 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { FILE *yyo = yyoutput; YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { YYFPRINTF (yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YYUSE (yytype); YY_IGNORE_MAYBE_UNINITIALIZED_END } /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ int yyparse (void) { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 546 "KDbSqlParser.y" /* yacc.c:1646 */ { //todo: multiple statements //todo: not only "select" statements KDbParserPrivate::get(globalParser)->setStatementType(KDbParser::Select); KDbParserPrivate::get(globalParser)->setQuerySchema((yyvsp[0].querySchema)); } #line 1504 "sqlparser.cpp" /* yacc.c:1646 */ break; case 3: #line 556 "KDbSqlParser.y" /* yacc.c:1646 */ { //todo: multiple statements } #line 1512 "sqlparser.cpp" /* yacc.c:1646 */ break; case 5: #line 561 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.querySchema) = (yyvsp[-1].querySchema); } #line 1520 "sqlparser.cpp" /* yacc.c:1646 */ break; case 6: #line 576 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.querySchema) = (yyvsp[0].querySchema); } #line 1528 "sqlparser.cpp" /* yacc.c:1646 */ break; case 7: #line 675 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select"; - if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[0].querySchema), 0 ))) - return 0; + if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[0].querySchema), nullptr ))) + YYABORT; } #line 1538 "sqlparser.cpp" /* yacc.c:1646 */ break; case 8: #line 681 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select ColViews=" << *(yyvsp[0].exprList); if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-1].querySchema), (yyvsp[0].exprList) ))) - return 0; + YYABORT; } #line 1549 "sqlparser.cpp" /* yacc.c:1646 */ break; case 9: #line 688 "KDbSqlParser.y" /* yacc.c:1646 */ { if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), (yyvsp[-1].exprList), (yyvsp[0].exprList) ))) - return 0; + YYABORT; } #line 1558 "sqlparser.cpp" /* yacc.c:1646 */ break; case 10: #line 693 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select ColViews Tables"; - if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-1].querySchema), 0, (yyvsp[0].exprList) ))) - return 0; + if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-1].querySchema), nullptr, (yyvsp[0].exprList) ))) + YYABORT; } #line 1568 "sqlparser.cpp" /* yacc.c:1646 */ break; case 11: #line 699 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select ColViews Conditions"; - if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), (yyvsp[-1].exprList), 0, (yyvsp[0].selectOptions) ))) - return 0; + if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), (yyvsp[-1].exprList), nullptr, (yyvsp[0].selectOptions) ))) + YYABORT; } #line 1578 "sqlparser.cpp" /* yacc.c:1646 */ break; case 12: #line 705 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select Tables SelectOptions"; - if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), 0, (yyvsp[-1].exprList), (yyvsp[0].selectOptions) ))) - return 0; + if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-2].querySchema), nullptr, (yyvsp[-1].exprList), (yyvsp[0].selectOptions) ))) + YYABORT; } #line 1588 "sqlparser.cpp" /* yacc.c:1646 */ break; case 13: #line 711 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "Select ColViews Tables SelectOptions"; if (!((yyval.querySchema) = buildSelectQuery( (yyvsp[-3].querySchema), (yyvsp[-2].exprList), (yyvsp[-1].exprList), (yyvsp[0].selectOptions) ))) - return 0; + YYABORT; } #line 1598 "sqlparser.cpp" /* yacc.c:1646 */ break; case 14: #line 720 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "SELECT"; (yyval.querySchema) = KDbParserPrivate::get(globalParser)->createQuery(); } #line 1607 "sqlparser.cpp" /* yacc.c:1646 */ break; case 15: #line 728 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "WhereClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = *(yyvsp[0].expr); delete (yyvsp[0].expr); } #line 1618 "sqlparser.cpp" /* yacc.c:1646 */ break; case 16: #line 735 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "OrderByClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->orderByColumns = (yyvsp[0].orderByColumns); } #line 1628 "sqlparser.cpp" /* yacc.c:1646 */ break; case 17: #line 741 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "WhereClause ORDER BY OrderByClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = *(yyvsp[-3].expr); delete (yyvsp[-3].expr); (yyval.selectOptions)->orderByColumns = (yyvsp[0].orderByColumns); } #line 1640 "sqlparser.cpp" /* yacc.c:1646 */ break; case 18: #line 749 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "OrderByClause WhereClause"; (yyval.selectOptions) = new SelectOptionsInternal; (yyval.selectOptions)->whereExpr = *(yyvsp[0].expr); delete (yyvsp[0].expr); (yyval.selectOptions)->orderByColumns = (yyvsp[-1].orderByColumns); } #line 1652 "sqlparser.cpp" /* yacc.c:1646 */ break; case 19: #line 760 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); } #line 1660 "sqlparser.cpp" /* yacc.c:1646 */ break; case 20: #line 769 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "ORDER BY IDENTIFIER"; (yyval.orderByColumns) = new QList; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[0].variantValue) ); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[0].variantValue); } #line 1673 "sqlparser.cpp" /* yacc.c:1646 */ break; case 21: #line 778 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "ORDER BY IDENTIFIER OrderByOption"; (yyval.orderByColumns) = new QList; OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-1].variantValue) ); orderByColumn.order = (yyvsp[0].sortOrderValue); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-1].variantValue); } #line 1687 "sqlparser.cpp" /* yacc.c:1646 */ break; case 22: #line 788 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.orderByColumns) = (yyvsp[0].orderByColumns); OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-2].variantValue) ); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-2].variantValue); } #line 1699 "sqlparser.cpp" /* yacc.c:1646 */ break; case 23: #line 796 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.orderByColumns) = (yyvsp[0].orderByColumns); OrderByColumnInternal orderByColumn; orderByColumn.setColumnByNameOrNumber( *(yyvsp[-3].variantValue) ); orderByColumn.order = (yyvsp[-2].sortOrderValue); (yyval.orderByColumns)->append( orderByColumn ); delete (yyvsp[-3].variantValue); } #line 1712 "sqlparser.cpp" /* yacc.c:1646 */ break; case 24: #line 808 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant( *(yyvsp[0].stringValue) ); kdbDebug() << "OrderByColumnId: " << *(yyval.variantValue); delete (yyvsp[0].stringValue); } #line 1722 "sqlparser.cpp" /* yacc.c:1646 */ break; case 25: #line 814 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant( *(yyvsp[-2].stringValue) + QLatin1Char('.') + *(yyvsp[0].stringValue) ); kdbDebug() << "OrderByColumnId: " << *(yyval.variantValue); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } #line 1733 "sqlparser.cpp" /* yacc.c:1646 */ break; case 26: #line 821 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.variantValue) = new QVariant((yyvsp[0].integerValue)); kdbDebug() << "OrderByColumnId: " << *(yyval.variantValue); } #line 1742 "sqlparser.cpp" /* yacc.c:1646 */ break; case 27: #line 828 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.sortOrderValue) = KDbOrderByColumn::SortOrder::Ascending; } #line 1750 "sqlparser.cpp" /* yacc.c:1646 */ break; case 28: #line 832 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.sortOrderValue) = KDbOrderByColumn::SortOrder::Descending; } #line 1758 "sqlparser.cpp" /* yacc.c:1646 */ break; case 30: #line 844 "KDbSqlParser.y" /* yacc.c:1646 */ { // kdbDebug() << "AND " << $3.debugString(); (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::AND, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1769 "sqlparser.cpp" /* yacc.c:1646 */ break; case 31: #line 851 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::OR, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1779 "sqlparser.cpp" /* yacc.c:1646 */ break; case 32: #line 857 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::XOR, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1789 "sqlparser.cpp" /* yacc.c:1646 */ break; case 34: #line 869 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '>', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1799 "sqlparser.cpp" /* yacc.c:1646 */ break; case 35: #line 875 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::GREATER_OR_EQUAL, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1809 "sqlparser.cpp" /* yacc.c:1646 */ break; case 36: #line 881 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '<', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1819 "sqlparser.cpp" /* yacc.c:1646 */ break; case 37: #line 887 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::LESS_OR_EQUAL, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1829 "sqlparser.cpp" /* yacc.c:1646 */ break; case 38: #line 893 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '=', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1839 "sqlparser.cpp" /* yacc.c:1646 */ break; case 40: #line 905 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::NOT_EQUAL, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1849 "sqlparser.cpp" /* yacc.c:1646 */ break; case 41: #line 911 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::NOT_EQUAL2, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1859 "sqlparser.cpp" /* yacc.c:1646 */ break; case 42: #line 917 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::LIKE, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1869 "sqlparser.cpp" /* yacc.c:1646 */ break; case 43: #line 923 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::NOT_LIKE, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1879 "sqlparser.cpp" /* yacc.c:1646 */ break; case 44: #line 929 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::SQL_IN, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1889 "sqlparser.cpp" /* yacc.c:1646 */ break; case 45: #line 935 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::SIMILAR_TO, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1899 "sqlparser.cpp" /* yacc.c:1646 */ break; case 46: #line 941 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::NOT_SIMILAR_TO, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1909 "sqlparser.cpp" /* yacc.c:1646 */ break; case 47: #line 947 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbNArgExpression(KDb::RelationalExpression, KDbToken::BETWEEN_AND); (yyval.expr)->toNArg().append( *(yyvsp[-4].expr) ); (yyval.expr)->toNArg().append( *(yyvsp[-2].expr) ); (yyval.expr)->toNArg().append( *(yyvsp[0].expr) ); delete (yyvsp[-4].expr); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1923 "sqlparser.cpp" /* yacc.c:1646 */ break; case 48: #line 957 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbNArgExpression(KDb::RelationalExpression, KDbToken::NOT_BETWEEN_AND); (yyval.expr)->toNArg().append( *(yyvsp[-4].expr) ); (yyval.expr)->toNArg().append( *(yyvsp[-2].expr) ); (yyval.expr)->toNArg().append( *(yyvsp[0].expr) ); delete (yyvsp[-4].expr); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1937 "sqlparser.cpp" /* yacc.c:1646 */ break; case 50: #line 973 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( KDbToken::SQL_IS_NULL, *(yyvsp[-1].expr) ); delete (yyvsp[-1].expr); } #line 1946 "sqlparser.cpp" /* yacc.c:1646 */ break; case 51: #line 978 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( KDbToken::SQL_IS_NOT_NULL, *(yyvsp[-1].expr) ); delete (yyvsp[-1].expr); } #line 1955 "sqlparser.cpp" /* yacc.c:1646 */ break; case 53: #line 989 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::BITWISE_SHIFT_LEFT, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1965 "sqlparser.cpp" /* yacc.c:1646 */ break; case 54: #line 995 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::BITWISE_SHIFT_RIGHT, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1975 "sqlparser.cpp" /* yacc.c:1646 */ break; case 56: #line 1007 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '+', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1985 "sqlparser.cpp" /* yacc.c:1646 */ break; case 57: #line 1013 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), KDbToken::CONCATENATION, *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 1995 "sqlparser.cpp" /* yacc.c:1646 */ break; case 58: #line 1019 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '-', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2005 "sqlparser.cpp" /* yacc.c:1646 */ break; case 59: #line 1025 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '&', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2015 "sqlparser.cpp" /* yacc.c:1646 */ break; case 60: #line 1031 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '|', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2025 "sqlparser.cpp" /* yacc.c:1646 */ break; case 62: #line 1043 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '/', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2035 "sqlparser.cpp" /* yacc.c:1646 */ break; case 63: #line 1049 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '*', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2045 "sqlparser.cpp" /* yacc.c:1646 */ break; case 64: #line 1055 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression(*(yyvsp[-2].expr), '%', *(yyvsp[0].expr)); delete (yyvsp[-2].expr); delete (yyvsp[0].expr); } #line 2055 "sqlparser.cpp" /* yacc.c:1646 */ break; case 66: #line 1068 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( '-', *(yyvsp[0].expr) ); delete (yyvsp[0].expr); } #line 2064 "sqlparser.cpp" /* yacc.c:1646 */ break; case 67: #line 1073 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( '+', *(yyvsp[0].expr) ); delete (yyvsp[0].expr); } #line 2073 "sqlparser.cpp" /* yacc.c:1646 */ break; case 68: #line 1078 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( '~', *(yyvsp[0].expr) ); delete (yyvsp[0].expr); } #line 2082 "sqlparser.cpp" /* yacc.c:1646 */ break; case 69: #line 1083 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbUnaryExpression( KDbToken::NOT, *(yyvsp[0].expr) ); delete (yyvsp[0].expr); } #line 2091 "sqlparser.cpp" /* yacc.c:1646 */ break; case 70: #line 1088 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbVariableExpression( *(yyvsp[0].stringValue) ); //! @todo simplify this later if that's 'only one field name' expression kdbDebug() << " + identifier: " << *(yyvsp[0].stringValue); delete (yyvsp[0].stringValue); } #line 2103 "sqlparser.cpp" /* yacc.c:1646 */ break; case 71: #line 1096 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbQueryParameterExpression( *(yyvsp[0].stringValue) ); kdbDebug() << " + query parameter:" << *(yyval.expr); delete (yyvsp[0].stringValue); } #line 2113 "sqlparser.cpp" /* yacc.c:1646 */ break; case 72: #line 1102 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << " + function:" << *(yyvsp[-1].stringValue) << "(" << *(yyvsp[0].exprList) << ")"; (yyval.expr) = new KDbFunctionExpression(*(yyvsp[-1].stringValue), *(yyvsp[0].exprList)); delete (yyvsp[-1].stringValue); delete (yyvsp[0].exprList); } #line 2124 "sqlparser.cpp" /* yacc.c:1646 */ break; case 73: #line 1110 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbVariableExpression( *(yyvsp[-2].stringValue) + QLatin1Char('.') + *(yyvsp[0].stringValue) ); kdbDebug() << " + identifier.identifier:" << *(yyvsp[-2].stringValue) << "." << *(yyvsp[0].stringValue); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } #line 2135 "sqlparser.cpp" /* yacc.c:1646 */ break; case 74: #line 1117 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbConstExpression( KDbToken::SQL_NULL, QVariant() ); kdbDebug() << " + NULL"; // $$ = new KDbField(); //$$->setName(QString::null); } #line 2146 "sqlparser.cpp" /* yacc.c:1646 */ break; case 75: #line 1124 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbConstExpression( KDbToken::SQL_TRUE, true ); } #line 2154 "sqlparser.cpp" /* yacc.c:1646 */ break; case 76: #line 1128 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbConstExpression( KDbToken::SQL_FALSE, false ); } #line 2162 "sqlparser.cpp" /* yacc.c:1646 */ break; case 77: #line 1132 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbConstExpression( KDbToken::CHARACTER_STRING_LITERAL, *(yyvsp[0].stringValue) ); kdbDebug() << " + constant " << (yyvsp[0].stringValue); delete (yyvsp[0].stringValue); } #line 2172 "sqlparser.cpp" /* yacc.c:1646 */ break; case 78: #line 1138 "KDbSqlParser.y" /* yacc.c:1646 */ { QVariant val; if ((yyvsp[0].integerValue) <= INT_MAX && (yyvsp[0].integerValue) >= INT_MIN) val = (int)(yyvsp[0].integerValue); else if ((yyvsp[0].integerValue) <= UINT_MAX && (yyvsp[0].integerValue) >= 0) val = (uint)(yyvsp[0].integerValue); else if ((yyvsp[0].integerValue) <= LLONG_MAX && (yyvsp[0].integerValue) >= LLONG_MIN) val = (qint64)(yyvsp[0].integerValue); // if ($1 < ULLONG_MAX) // val = (quint64)$1; //! @todo ok? (yyval.expr) = new KDbConstExpression( KDbToken::INTEGER_CONST, val ); kdbDebug() << " + int constant: " << val.toString(); } #line 2193 "sqlparser.cpp" /* yacc.c:1646 */ break; case 79: #line 1155 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbConstExpression( KDbToken::REAL_CONST, *(yyvsp[0].binaryValue) ); kdbDebug() << " + real constant: " << *(yyvsp[0].binaryValue); delete (yyvsp[0].binaryValue); } #line 2203 "sqlparser.cpp" /* yacc.c:1646 */ break; case 81: #line 1167 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "(expr)"; (yyval.expr) = new KDbUnaryExpression('(', *(yyvsp[-1].expr)); delete (yyvsp[-1].expr); } #line 2213 "sqlparser.cpp" /* yacc.c:1646 */ break; case 82: #line 1176 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-1].exprList); } #line 2221 "sqlparser.cpp" /* yacc.c:1646 */ break; case 83: #line 1180 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = new KDbNArgExpression(KDb::ArgumentListExpression, ','); } #line 2229 "sqlparser.cpp" /* yacc.c:1646 */ break; case 84: #line 1187 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[0].exprList); (yyval.exprList)->prepend( *(yyvsp[-2].expr) ); delete (yyvsp[-2].expr); } #line 2239 "sqlparser.cpp" /* yacc.c:1646 */ break; case 85: #line 1193 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = new KDbNArgExpression(KDb::ArgumentListExpression, ','); (yyval.exprList)->append( *(yyvsp[0].expr) ); delete (yyvsp[0].expr); } #line 2249 "sqlparser.cpp" /* yacc.c:1646 */ break; case 86: #line 1202 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[0].exprList); } #line 2257 "sqlparser.cpp" /* yacc.c:1646 */ break; case 87: #line 1247 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-2].exprList); (yyval.exprList)->append(*(yyvsp[0].expr)); delete (yyvsp[0].expr); } #line 2267 "sqlparser.cpp" /* yacc.c:1646 */ break; case 88: #line 1253 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = new KDbNArgExpression(KDb::TableListExpression, KDbToken::IDENTIFIER); //ok? (yyval.exprList)->append(*(yyvsp[0].expr)); delete (yyvsp[0].expr); } #line 2277 "sqlparser.cpp" /* yacc.c:1646 */ break; case 89: #line 1262 "KDbSqlParser.y" /* yacc.c:1646 */ { kdbDebug() << "FROM: '" << *(yyvsp[0].stringValue) << "'"; (yyval.expr) = new KDbVariableExpression(*(yyvsp[0].stringValue)); //! @todo this isn't ok for more tables: /* KDbField::ListIterator it = globalParser->query()->fieldsIterator(); for(KDbField *item; (item = it.current()); ++it) { if(item->table() == dummy) { item->setTable(schema); } if(item->table() && !item->isQueryAsterisk()) { KDbField *f = item->table()->field(item->name()); if(!f) { KDbParserError err(KDbParser::tr("Field List Error"), KDbParser::tr("Unknown column '%1' in table '%2'",item->name(),schema->name()), ctoken, current); globalParser->setError(err); yyerror("fieldlisterror"); } } }*/ delete (yyvsp[0].stringValue); } #line 2309 "sqlparser.cpp" /* yacc.c:1646 */ break; case 90: #line 1290 "KDbSqlParser.y" /* yacc.c:1646 */ { //table + alias (yyval.expr) = new KDbBinaryExpression( KDbVariableExpression(*(yyvsp[-1].stringValue)), KDbToken::AS_EMPTY, KDbVariableExpression(*(yyvsp[0].stringValue)) ); delete (yyvsp[-1].stringValue); delete (yyvsp[0].stringValue); } #line 2323 "sqlparser.cpp" /* yacc.c:1646 */ break; case 91: #line 1300 "KDbSqlParser.y" /* yacc.c:1646 */ { //table + alias (yyval.expr) = new KDbBinaryExpression( KDbVariableExpression(*(yyvsp[-2].stringValue)), KDbToken::AS, KDbVariableExpression(*(yyvsp[0].stringValue)) ); delete (yyvsp[-2].stringValue); delete (yyvsp[0].stringValue); } #line 2337 "sqlparser.cpp" /* yacc.c:1646 */ break; case 92: #line 1315 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = (yyvsp[-2].exprList); (yyval.exprList)->append(*(yyvsp[0].expr)); delete (yyvsp[0].expr); kdbDebug() << "ColViews: ColViews , ColItem"; } #line 2348 "sqlparser.cpp" /* yacc.c:1646 */ break; case 93: #line 1322 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.exprList) = new KDbNArgExpression(KDb::FieldListExpression, KDbToken()); (yyval.exprList)->append(*(yyvsp[0].expr)); delete (yyvsp[0].expr); kdbDebug() << "ColViews: ColItem"; } #line 2359 "sqlparser.cpp" /* yacc.c:1646 */ break; case 94: #line 1332 "KDbSqlParser.y" /* yacc.c:1646 */ { // $$ = new KDbField(); // dummy->addField($$); // $$->setExpression( $1 ); // globalParser->query()->addField($$); (yyval.expr) = (yyvsp[0].expr); kdbDebug() << " added column expr:" << *(yyvsp[0].expr); } #line 2372 "sqlparser.cpp" /* yacc.c:1646 */ break; case 95: #line 1341 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); kdbDebug() << " added column wildcard:" << *(yyvsp[0].expr); } #line 2381 "sqlparser.cpp" /* yacc.c:1646 */ break; case 96: #line 1346 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression( *(yyvsp[-2].expr), KDbToken::AS, KDbVariableExpression(*(yyvsp[0].stringValue)) ); kdbDebug() << " added column expr:" << *(yyval.expr); delete (yyvsp[-2].expr); delete (yyvsp[0].stringValue); } #line 2395 "sqlparser.cpp" /* yacc.c:1646 */ break; case 97: #line 1356 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbBinaryExpression( *(yyvsp[-1].expr), KDbToken::AS_EMPTY, KDbVariableExpression(*(yyvsp[0].stringValue)) ); kdbDebug() << " added column expr:" << *(yyval.expr); delete (yyvsp[-1].expr); delete (yyvsp[0].stringValue); } #line 2409 "sqlparser.cpp" /* yacc.c:1646 */ break; case 98: #line 1369 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[0].expr); } #line 2417 "sqlparser.cpp" /* yacc.c:1646 */ break; case 99: #line 1415 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = (yyvsp[-1].expr); //! @todo DISTINCT '(' ColExpression ')' // $$->setName("DISTINCT(" + $3->name() + ")"); } #line 2427 "sqlparser.cpp" /* yacc.c:1646 */ break; case 100: #line 1424 "KDbSqlParser.y" /* yacc.c:1646 */ { (yyval.expr) = new KDbVariableExpression(QLatin1String("*")); kdbDebug() << "all columns"; // KDbQueryAsterisk *ast = new KDbQueryAsterisk(globalParser->query(), dummy); // globalParser->query()->addAsterisk(ast); // requiresTable = true; } #line 2440 "sqlparser.cpp" /* yacc.c:1646 */ break; case 101: #line 1433 "KDbSqlParser.y" /* yacc.c:1646 */ { QString s( *(yyvsp[-2].stringValue) ); s += QLatin1String(".*"); (yyval.expr) = new KDbVariableExpression(s); kdbDebug() << " + all columns from " << s; delete (yyvsp[-2].stringValue); } #line 2452 "sqlparser.cpp" /* yacc.c:1646 */ break; #line 2456 "sqlparser.cpp" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 1448 "KDbSqlParser.y" /* yacc.c:1906 */ KDB_TESTING_EXPORT const char* g_tokenName(unsigned int offset) { const int t = YYTRANSLATE(offset); if (t >= YYTRANSLATE(::SQL_TYPE)) { return yytname[t]; } - return 0; + return nullptr; } //static const int KDbToken::maxCharTokenValue = 253; //static const int KDbToken::maxTokenValue = YYMAXUTOK; diff --git a/src/parser/generated/sqlparser.h b/src/parser/generated/sqlparser.h index 3fa92ed2..f91291a6 100644 --- a/src/parser/generated/sqlparser.h +++ b/src/parser/generated/sqlparser.h @@ -1,164 +1,164 @@ /**************************************************************************** * Created by generate_parser_code.sh * WARNING! All changes made in this file will be lost! ****************************************************************************/ #ifndef KDBSQLPARSER_H #define KDBSQLPARSER_H #include "KDbExpression.h" #include "KDbField.h" #include "KDbOrderByColumn.h" struct OrderByColumnInternal; struct SelectOptionsInternal; /* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . */ + along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_YY_KDBSQLPARSER_TAB_H_INCLUDED # define YY_YY_KDBSQLPARSER_TAB_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { SQL_TYPE = 258, AS = 259, AS_EMPTY = 260, ASC = 261, AUTO_INCREMENT = 262, BIT = 263, BITWISE_SHIFT_LEFT = 264, BITWISE_SHIFT_RIGHT = 265, BY = 266, CHARACTER_STRING_LITERAL = 267, CONCATENATION = 268, CREATE = 269, DESC = 270, DISTINCT = 271, DOUBLE_QUOTED_STRING = 272, FROM = 273, JOIN = 274, KEY = 275, LEFT = 276, LESS_OR_EQUAL = 277, GREATER_OR_EQUAL = 278, SQL_NULL = 279, SQL_IS = 280, SQL_IS_NULL = 281, SQL_IS_NOT_NULL = 282, ORDER = 283, PRIMARY = 284, SELECT = 285, INTEGER_CONST = 286, REAL_CONST = 287, RIGHT = 288, SQL_ON = 289, DATE_CONST = 290, DATETIME_CONST = 291, TIME_CONST = 292, TABLE = 293, IDENTIFIER = 294, IDENTIFIER_DOT_ASTERISK = 295, QUERY_PARAMETER = 296, VARCHAR = 297, WHERE = 298, SQL = 299, SQL_TRUE = 300, SQL_FALSE = 301, UNION = 302, SCAN_ERROR = 303, AND = 304, BETWEEN = 305, NOT_BETWEEN = 306, EXCEPT = 307, SQL_IN = 308, INTERSECT = 309, LIKE = 310, ILIKE = 311, NOT_LIKE = 312, NOT = 313, NOT_EQUAL = 314, NOT_EQUAL2 = 315, OR = 316, SIMILAR_TO = 317, NOT_SIMILAR_TO = 318, XOR = 319, UMINUS = 320 }; #endif /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 490 "KDbSqlParser.y" /* yacc.c:1909 */ QString* stringValue; QByteArray* binaryValue; qint64 integerValue; bool booleanValue; KDbOrderByColumn::SortOrder sortOrderValue; KDbField::Type colType; KDbField *field; KDbExpression *expr; KDbNArgExpression *exprList; KDbConstExpression *constExpression; KDbQuerySchema *querySchema; SelectOptionsInternal *selectOptions; QList *orderByColumns; QVariant *variantValue; #line 137 "KDbSqlParser.tab.h" /* yacc.c:1909 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE yylval; int yyparse (void); #endif /* !YY_YY_KDBSQLPARSER_TAB_H_INCLUDED */ #endif