SQLite: Handle tables and fields with a dot in the name correctly

Fixes: QTBUG-91885
Pick-to: 6.1 6.0 5.15
Change-Id: Iba76bb50266dd4fb5f50e4ea1549d1d2bb6e3431
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Andy Shaw 2021-04-16 16:11:07 +02:00
parent 83eca9a087
commit 66acee69a1
2 changed files with 73 additions and 9 deletions

View File

@ -74,13 +74,18 @@ Q_DECLARE_METATYPE(sqlite3_stmt*)
QT_BEGIN_NAMESPACE
static QString _q_escapeIdentifier(const QString &identifier)
static QString _q_escapeIdentifier(const QString &identifier, QSqlDriver::IdentifierType type)
{
QString res = identifier;
// If it contains [ and ] then we assume it to be escaped properly already as this indicates
// the syntax is exactly how it should be
if (identifier.contains(QLatin1Char('[')) && identifier.contains(QLatin1Char(']')))
return res;
if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"'))) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
if (type == QSqlDriver::TableName)
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
}
return res;
}
@ -912,13 +917,24 @@ static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool only
{
QString schema;
QString table(tableName);
int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
const int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
if (indexOfSeparator > -1) {
schema = tableName.left(indexOfSeparator).append(QLatin1Char('.'));
table = tableName.mid(indexOfSeparator + 1);
const int indexOfCloseBracket = tableName.indexOf(QLatin1Char(']'));
if (indexOfCloseBracket != tableName.size() - 1) {
// Handles a case like databaseName.tableName
schema = tableName.left(indexOfSeparator + 1);
table = tableName.mid(indexOfSeparator + 1);
} else {
const int indexOfOpenBracket = tableName.lastIndexOf(QLatin1Char('['), indexOfCloseBracket);
if (indexOfOpenBracket > 0) {
// Handles a case like databaseName.[tableName]
schema = tableName.left(indexOfOpenBracket);
table = tableName.mid(indexOfOpenBracket);
}
}
}
q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1Char(')'));
q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") +
_q_escapeIdentifier(table, QSqlDriver::TableName) + QLatin1Char(')'));
QSqlIndex ind;
while (q.next()) {
bool isPk = q.value(5).toInt();
@ -980,8 +996,7 @@ QVariant QSQLiteDriver::handle() const
QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
{
Q_UNUSED(type);
return _q_escapeIdentifier(identifier);
return _q_escapeIdentifier(identifier, type);
}
static void handle_sqlite_callback(void *qobj,int aoperation, char const *adbname, char const *atablename,

View File

@ -154,6 +154,9 @@ private slots:
void invalidFilterAndHeaderData_data() { generic_data(); }
void invalidFilterAndHeaderData(); //QTBUG-23879
void sqlite_selectFromIdentifierWithDot_data() { generic_data("QSQLITE"); }
void sqlite_selectFromIdentifierWithDot();
private:
void generic_data(const QString& engine=QString());
void generic_data_with_strategies(const QString& engine=QString());
@ -161,6 +164,7 @@ private:
tst_QSqlTableModel::tst_QSqlTableModel()
{
QVERIFY(dbs.open());
}
tst_QSqlTableModel::~tst_QSqlTableModel()
@ -2160,5 +2164,50 @@ void tst_QSqlTableModel::modelInAnotherThread()
QVERIFY(t.isFinished());
}
void tst_QSqlTableModel::sqlite_selectFromIdentifierWithDot()
{
QFETCH(QString, dbName);
QSqlDatabase db = QSqlDatabase::database(dbName);
CHECK_DATABASE(db);
{
const auto fieldDot = qTableName("fieldDot", __FILE__, db);
tst_Databases::safeDropTable(db, fieldDot);
QSqlQuery qry(db);
QVERIFY_SQL(qry, exec("create table " + fieldDot + " (id int primary key, "
"\"person.firstname\" varchar(20))"));
QVERIFY_SQL(qry, exec("insert into " + fieldDot + " values(1, 'Andy')"));
QSqlTableModel model(0, db);
model.setTable(fieldDot);
QVERIFY_SQL(model, select());
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
}
const auto tableDot = QLatin1Char('[') + qTableName("table.dot", __FILE__, db) + QLatin1Char(']');
{
tst_Databases::safeDropTable(db, tableDot);
QSqlQuery qry(db);
QVERIFY_SQL(qry, exec("create table " + tableDot + " (id int primary key, "
"\"person.firstname\" varchar(20))"));
QVERIFY_SQL(qry, exec("insert into " + tableDot + " values(1, 'Andy')"));
QSqlTableModel model(0, db);
model.setTable(tableDot);
QVERIFY_SQL(model, select());
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
}
{
QSqlDatabase attachedDb = QSqlDatabase::addDatabase("QSQLITE", "attachedDb");
attachedDb.setDatabaseName(db.databaseName().replace("foo.db", "attached.db"));
QVERIFY(attachedDb.open());
QSqlQuery qry(attachedDb);
QVERIFY_SQL(qry, exec(QString("attach '%1' AS 'attached'").arg(db.databaseName())));
QSqlTableModel model(0, attachedDb);
model.setTable(QString("attached.%1").arg(tableDot));
QVERIFY_SQL(model, select());
QCOMPARE(model.data(model.index(0, 0)).toInt(), 1);
QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Andy"));
}
}
QTEST_MAIN(tst_QSqlTableModel)
#include "tst_qsqltablemodel.moc"