summaryrefslogtreecommitdiffstats
path: root/src/sql/kernel
diff options
context:
space:
mode:
authorabcd <qt-info@nokia.com>2009-04-15 10:16:48 +1000
committerabcd <qt-info@nokia.com>2009-04-15 10:16:48 +1000
commitbb7bddc47dd0748b45d22180d9e3c8e5209010b3 (patch)
treea0f6fb8bb72ba54e626b25f5d34c926aace9c13b /src/sql/kernel
parenta94b601866740e483f093233f59f43b022a68735 (diff)
Fix the behaviour of sql classes regarding quoted identifiers
If no quotes around identifiers are provided by the programmer, identifiers are treated identically to how the underlying engine would behave. i.e. some engines uppercase the identifiers others lowercase them. If the programmer wants case sensitivty and/or use whitespaces they will need to quote their identifiers. The previous (incorrect) behaviour always quoted the identifiers. Reviewed-by: Bill King
Diffstat (limited to 'src/sql/kernel')
-rw-r--r--src/sql/kernel/qsqldriver.cpp131
-rw-r--r--src/sql/kernel/qsqldriver.h6
2 files changed, 128 insertions, 9 deletions
diff --git a/src/sql/kernel/qsqldriver.cpp b/src/sql/kernel/qsqldriver.cpp
index a99500566b..40bc0dfcf9 100644
--- a/src/sql/kernel/qsqldriver.cpp
+++ b/src/sql/kernel/qsqldriver.cpp
@@ -49,6 +49,17 @@
QT_BEGIN_NAMESPACE
+static QString prepareIdentifier(const QString &identifier,
+ QSqlDriver::IdentifierType type, const QSqlDriver *driver)
+{
+ Q_ASSERT( driver != NULL );
+ QString ret = identifier;
+ if (!driver->isIdentifierEscaped(identifier, type)) {
+ ret = driver->escapeIdentifier(identifier, type);
+ }
+ return ret;
+}
+
class QSqlDriverPrivate : public QObjectPrivate
{
public:
@@ -372,6 +383,7 @@ QSqlRecord QSqlDriver::record(const QString & /* tableName */) const
on \a type.
The default implementation does nothing.
+ \sa isIdentifierEscaped()
*/
QString QSqlDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
@@ -379,6 +391,55 @@ QString QSqlDriver::escapeIdentifier(const QString &identifier, IdentifierType)
}
/*!
+ Returns whether \a identifier is escaped according to the database rules.
+ \a identifier can either be a table name or field name, dependent
+ on \a type.
+
+ \warning Because of binary compatability constraints, this function is not virtual.
+ If you want to provide your own implementation in your QSqlDriver subclass,
+ reimplement the isIdentifierEscapedImplementation() slot in your subclass instead.
+ The isIdentifierEscapedFunction() will dynamically detect the slot and call it.
+
+ \sa stripDelimiters(), escapeIdentifier()
+ */
+bool QSqlDriver::isIdentifierEscaped(const QString &identifier, IdentifierType type) const
+{
+ bool result;
+ QMetaObject::invokeMethod(const_cast<QSqlDriver*>(this),
+ "isIdentifierEscapedImplementation", Qt::DirectConnection,
+ Q_RETURN_ARG(bool, result),
+ Q_ARG(QString, identifier),
+ Q_ARG(IdentifierType, type));
+ return result;
+}
+
+/*!
+ Returns the \a identifier with the leading and trailing delimiters removed,
+ \a identifier can either be a table name or field name,
+ dependent on \a type. If \a identifier does not have leading
+ and trailing delimiter characters, \a identifier is returned without
+ modification.
+
+ \warning Because of binary compatability constraints, this function is not virtual,
+ If you want to provide your own implementation in your QSqlDriver subclass,
+ reimplement the stripDelimitersImplementation() slot in your subclass instead.
+ The stripDelimiters() function will dynamically detect the slot and call it.
+
+ \since 4.5
+ \sa isIdentifierEscaped()
+ */
+QString QSqlDriver::stripDelimiters(const QString &identifier, IdentifierType type) const
+{
+ QString result;
+ QMetaObject::invokeMethod(const_cast<QSqlDriver*>(this),
+ "stripDelimitersImplementation", Qt::DirectConnection,
+ Q_RETURN_ARG(QString, result),
+ Q_ARG(QString, identifier),
+ Q_ARG(IdentifierType, type));
+ return result;
+}
+
+/*!
Returns a SQL statement of type \a type for the table \a tableName
with the values from \a rec. If \a preparedStatement is true, the
string will contain placeholders instead of values.
@@ -397,17 +458,17 @@ QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
case SelectStatement:
for (i = 0; i < rec.count(); ++i) {
if (rec.isGenerated(i))
- s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1String(", "));
+ s.append(prepareIdentifier(rec.fieldName(i), QSqlDriver::FieldName, this)).append(QLatin1String(", "));
}
if (s.isEmpty())
return s;
s.chop(2);
- s.prepend(QLatin1String("SELECT ")).append(QLatin1String(" FROM ")).append(escapeIdentifier(tableName, TableName));
+ s.prepend(QLatin1String("SELECT ")).append(QLatin1String(" FROM ")).append(tableName);
break;
case WhereStatement:
if (preparedStatement) {
for (int i = 0; i < rec.count(); ++i) {
- s.append(escapeIdentifier(rec.fieldName(i), FieldName));
+ s.append(prepareIdentifier(rec.fieldName(i), FieldName,this));
if (rec.isNull(i))
s.append(QLatin1String(" IS NULL"));
else
@@ -416,7 +477,7 @@ QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
}
} else {
for (i = 0; i < rec.count(); ++i) {
- s.append(escapeIdentifier(rec.fieldName(i), FieldName));
+ s.append(prepareIdentifier(rec.fieldName(i), QSqlDriver::FieldName, this));
QString val = formatValue(rec.field(i));
if (val == QLatin1String("NULL"))
s.append(QLatin1String(" IS NULL"));
@@ -431,12 +492,12 @@ QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
}
break;
case UpdateStatement:
- s.append(QLatin1String("UPDATE ")).append(escapeIdentifier(tableName, TableName)).append(
+ s.append(QLatin1String("UPDATE ")).append(tableName).append(
QLatin1String(" SET "));
for (i = 0; i < rec.count(); ++i) {
if (!rec.isGenerated(i) || !rec.value(i).isValid())
continue;
- s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1Char('='));
+ s.append(prepareIdentifier(rec.fieldName(i), QSqlDriver::FieldName, this)).append(QLatin1Char('='));
if (preparedStatement)
s.append(QLatin1Char('?'));
else
@@ -449,15 +510,15 @@ QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
s.clear();
break;
case DeleteStatement:
- s.append(QLatin1String("DELETE FROM ")).append(escapeIdentifier(tableName, TableName));
+ s.append(QLatin1String("DELETE FROM ")).append(tableName);
break;
case InsertStatement: {
- s.append(QLatin1String("INSERT INTO ")).append(escapeIdentifier(tableName, TableName)).append(QLatin1String(" ("));
+ s.append(QLatin1String("INSERT INTO ")).append(tableName).append(QLatin1String(" ("));
QString vals;
for (i = 0; i < rec.count(); ++i) {
if (!rec.isGenerated(i) || !rec.value(i).isValid())
continue;
- s.append(escapeIdentifier(rec.fieldName(i), FieldName)).append(QLatin1String(", "));
+ s.append(prepareIdentifier(rec.fieldName(i), QSqlDriver::FieldName, this)).append(QLatin1String(", "));
if (preparedStatement)
vals.append(QLatin1String("?"));
else
@@ -805,4 +866,56 @@ QStringList QSqlDriver::subscribedToNotificationsImplementation() const
return QStringList();
}
+/*!
+ This slot returns whether \a identifier is escaped according to the database rules.
+ \a identifier can either be a table name or field name, dependent
+ on \a type.
+
+ Because of binary compatability constraints, isIdentifierEscaped() function
+ (introduced in Qt 4.5) is not virtual. Instead, isIdentifierEscaped() will
+ dynamically detect and call \e this slot. The default implementation
+ assumes the escape/delimiter character is a double quote. Reimplement this
+ slot in your own QSqlDriver if your database engine uses a different
+ delimiter character.
+
+ \since 4.5
+ \sa isIdentifierEscaped()
+ */
+bool QSqlDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const
+{
+ Q_UNUSED(type);
+ bool isLeftDelimited = identifier.left(1) == QString(QLatin1Char('"'));
+ bool isRightDelimited = identifier.right(1) == QString(QLatin1Char('"'));
+ if( identifier.size() > 2 && isLeftDelimited && isRightDelimited )
+ return true;
+ else
+ return false;
+}
+
+/*!
+ This slot returns \a identifier with the leading and trailing delimiters removed,
+ \a identifier can either be a tablename or field name, dependent on \a type.
+ If \a identifier does not have leading and trailing delimiter characters, \a
+ identifier is returned without modification.
+
+ Because of binary compatability constraints, the stripDelimiters() function
+ (introduced in Qt 4.5) is not virtual. Instead, stripDelimiters() will
+ dynamically detect and call \e this slot. It generally unnecessary
+ to reimplement this slot.
+
+ \since 4.5
+ \sa stripDelimiters()
+ */
+QString QSqlDriver::stripDelimitersImplementation(const QString &identifier, IdentifierType type) const
+{
+ QString ret;
+ if (this->isIdentifierEscaped(identifier, type)) {
+ ret = identifier.mid(1);
+ ret.chop(1);
+ } else {
+ ret = identifier;
+ }
+ return ret;
+}
+
QT_END_NAMESPACE
diff --git a/src/sql/kernel/qsqldriver.h b/src/sql/kernel/qsqldriver.h
index e763719ef4..8ac1471db1 100644
--- a/src/sql/kernel/qsqldriver.h
+++ b/src/sql/kernel/qsqldriver.h
@@ -127,6 +127,9 @@ public:
bool unsubscribeFromNotification(const QString &name); // ### Qt 5: make virtual
QStringList subscribedToNotifications() const; // ### Qt 5: make virtual
+ bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const; // ### Qt 5: make virtual
+ QString stripDelimiters(const QString &identifier, IdentifierType type) const; // ### Qt 5: make virtual
+
Q_SIGNALS:
void notification(const QString &name);
@@ -140,6 +143,9 @@ protected Q_SLOTS:
bool unsubscribeFromNotificationImplementation(const QString &name); // ### Qt 5: eliminate, see unsubscribeFromNotification()
QStringList subscribedToNotificationsImplementation() const; // ### Qt 5: eliminate, see subscribedNotifications()
+ bool isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const; // ### Qt 5: eliminate, see isIdentifierEscaped()
+ QString stripDelimitersImplementation(const QString &identifier, IdentifierType type) const; // ### Qt 5: eliminate, see stripDelimiters()
+
private:
Q_DISABLE_COPY(QSqlDriver)
};