From bb1b0d2362d38e1e1ead97ddf21a24c8d4bf76d1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 7 Aug 2020 16:32:27 +1000 Subject: [PATCH] Allow Microsoft style [ ] column references to be handled by QgsSqlStatement --- python/core/auto_generated/qgssqlstatement.sip.in | 7 +++++++ src/core/qgssqlstatement.cpp | 10 ++++++++++ src/core/qgssqlstatement.h | 6 ++++++ src/core/qgssqlstatementlexer.ll | 4 ++++ tests/src/python/test_qgssqlstatement.py | 13 +++++++++++++ 5 files changed, 40 insertions(+) diff --git a/python/core/auto_generated/qgssqlstatement.sip.in b/python/core/auto_generated/qgssqlstatement.sip.in index aac6ac8f949..654f78393f6 100644 --- a/python/core/auto_generated/qgssqlstatement.sip.in +++ b/python/core/auto_generated/qgssqlstatement.sip.in @@ -91,6 +91,13 @@ otherwise the original string. %Docstring Remove double quotes from an identifier. +.. seealso:: :py:func:`quotedIdentifier` +%End + + static QString stripMsQuotedIdentifier( QString text ); +%Docstring +Remove double quotes from an Microsoft style identifier. + .. seealso:: :py:func:`quotedIdentifier` %End diff --git a/src/core/qgssqlstatement.cpp b/src/core/qgssqlstatement.cpp index 70b9448d15c..6179396c058 100644 --- a/src/core/qgssqlstatement.cpp +++ b/src/core/qgssqlstatement.cpp @@ -106,6 +106,16 @@ QString QgsSQLStatement::stripQuotedIdentifier( QString text ) return text; } +QString QgsSQLStatement::stripMsQuotedIdentifier( QString text ) +{ + if ( text.length() >= 2 && text[0] == '[' && text[text.length() - 1] == ']' ) + { + // strip square brackets on start,end + text = text.mid( 1, text.length() - 2 ); + } + return text; +} + QString QgsSQLStatement::quotedString( QString text ) { text.replace( '\'', QLatin1String( "''" ) ); diff --git a/src/core/qgssqlstatement.h b/src/core/qgssqlstatement.h index aabe1d10211..275cfab3cbb 100644 --- a/src/core/qgssqlstatement.h +++ b/src/core/qgssqlstatement.h @@ -107,6 +107,12 @@ class CORE_EXPORT QgsSQLStatement */ static QString stripQuotedIdentifier( QString text ); + /** + * Remove double quotes from an Microsoft style identifier. + * \see quotedIdentifier() + */ + static QString stripMsQuotedIdentifier( QString text ); + /** * Returns a quoted version of a string (in single quotes) * \see quotedIdentifier(), quotedIdentifierIfNeeded() diff --git a/src/core/qgssqlstatementlexer.ll b/src/core/qgssqlstatementlexer.ll index ccf3be149d5..4d58cd7c00a 100644 --- a/src/core/qgssqlstatementlexer.ll +++ b/src/core/qgssqlstatementlexer.ll @@ -94,6 +94,8 @@ identifier {identifier_first}{identifier_next}* identifier_str_char "\"\""|[^\"] identifier_quoted "\""{identifier_str_char}*"\"" +ms_identifier_quoted "\["[^.]*"\]" + dig [0-9] num_int [-]?{dig}+{identifier_first}* num_float [-]?{dig}*(\.{dig}+([eE][-+]?{dig}+)?|[eE][-+]?{dig}+) @@ -190,6 +192,8 @@ string "'"{str_char}*"'" {identifier_quoted} { TEXT_FILTER(QgsSQLStatement::stripQuotedIdentifier); return IDENTIFIER; } +{ms_identifier_quoted} { TEXT_FILTER(QgsSQLStatement::stripMsQuotedIdentifier); return IDENTIFIER; } + {white} /* skip blanks and tabs */ . { return Unknown_CHARACTER; } diff --git a/tests/src/python/test_qgssqlstatement.py b/tests/src/python/test_qgssqlstatement.py index a101d2554c4..13e0546bd9b 100644 --- a/tests/src/python/test_qgssqlstatement.py +++ b/tests/src/python/test_qgssqlstatement.py @@ -218,6 +218,19 @@ class TestQgsSQLStatementCustomFunctions(unittest.TestCase): self.checkFragmentError("FROM b") self.checkFragmentError("ORDER BY a") + def testMsFragment(self): + # Microsoft style identifiers can have a bunch of weird characters in them! + exp = QgsSQLStatementFragment('[col$_# :]') + self.assertFalse(exp.hasParserError()) + self.assertIsInstance(exp.rootNode(), QgsSQLStatement.NodeColumnRef) + self.assertEqual(exp.rootNode().name(), 'col$_# :') + + exp = QgsSQLStatementFragment('[table$_# :].[col$_# :]') + self.assertFalse(exp.hasParserError()) + self.assertIsInstance(exp.rootNode(), QgsSQLStatement.NodeColumnRef) + self.assertEqual(exp.rootNode().name(), 'col$_# :') + self.assertEqual(exp.rootNode().tableName(), 'table$_# :') + if __name__ == "__main__": unittest.main()