summaryrefslogtreecommitdiffstats
path: root/src/sql
diff options
context:
space:
mode:
Diffstat (limited to 'src/sql')
-rw-r--r--src/sql/drivers/db2/qsql_db2.cpp98
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp58
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.cpp51
-rw-r--r--src/sql/drivers/mysql/qsql_mysql.h4
-rw-r--r--src/sql/drivers/oci/qsql_oci.cpp350
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.cpp199
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.h4
-rw-r--r--src/sql/drivers/psql/qsql_psql.cpp75
-rw-r--r--src/sql/drivers/sqlite/qsql_sqlite.cpp33
-rw-r--r--src/sql/drivers/sqlite2/qsql_sqlite2.cpp31
-rw-r--r--src/sql/drivers/tds/qsql_tds.cpp27
-rw-r--r--src/sql/kernel/qsqlcachedresult.cpp13
-rw-r--r--src/sql/kernel/qsqlcachedresult_p.h1
-rw-r--r--src/sql/kernel/qsqldatabase.cpp54
-rw-r--r--src/sql/kernel/qsqldatabase.h2
-rw-r--r--src/sql/kernel/qsqldriver.cpp164
-rw-r--r--src/sql/kernel/qsqldriver.h9
-rw-r--r--src/sql/kernel/qsqlerror.cpp2
-rw-r--r--src/sql/kernel/qsqlfield.cpp4
-rw-r--r--src/sql/kernel/qsqlquery.cpp23
-rw-r--r--src/sql/kernel/qsqlrecord.cpp2
-rw-r--r--src/sql/kernel/qsqlresult.cpp19
-rw-r--r--src/sql/kernel/qsqlresult.h1
-rw-r--r--src/sql/models/qsqlrelationaltablemodel.cpp73
-rw-r--r--src/sql/models/qsqltablemodel.cpp20
25 files changed, 942 insertions, 375 deletions
diff --git a/src/sql/drivers/db2/qsql_db2.cpp b/src/sql/drivers/db2/qsql_db2.cpp
index 3009eed..1a82377 100644
--- a/src/sql/drivers/db2/qsql_db2.cpp
+++ b/src/sql/drivers/db2/qsql_db2.cpp
@@ -84,7 +84,7 @@ public:
class QDB2ResultPrivate
{
public:
- QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0), precisionPolicy(QSql::HighPrecision)
+ QDB2ResultPrivate(const QDB2DriverPrivate* d): dp(d), hStmt(0)
{}
~QDB2ResultPrivate()
{
@@ -107,7 +107,6 @@ public:
SQLHANDLE hStmt;
QSqlRecord recInf;
QVector<QVariant*> valueCache;
- QSql::NumericalPrecisionPolicy precisionPolicy;
};
static QString qFromTChar(SQLTCHAR* str)
@@ -1044,26 +1043,22 @@ QVariant QDB2Result::data(int field)
case QVariant::Double:
{
QString value=qGetStringData(d->hStmt, field, info.length() + 1, isNull);
- bool ok=false;
- switch(d->precisionPolicy) {
+ switch(numericalPrecisionPolicy()) {
case QSql::LowPrecisionInt32:
- v = new QVariant(value.toInt(&ok));
+ v = new QVariant(qGetIntData(d->hStmt, field, isNull));
break;
case QSql::LowPrecisionInt64:
- v = new QVariant(value.toLongLong(&ok));
+ v = new QVariant(qGetBigIntData(d->hStmt, field, isNull));
break;
case QSql::LowPrecisionDouble:
- v = new QVariant(value.toDouble(&ok));
+ v = new QVariant(qGetDoubleData(d->hStmt, field, isNull));
break;
case QSql::HighPrecision:
default:
// length + 1 for the comma
- v = new QVariant(value);
- ok = true;
+ v = new QVariant(qGetStringData(d->hStmt, field, info.length() + 1, isNull));
break;
}
- if(!ok)
- v = new QVariant();
break;
}
case QVariant::String:
@@ -1147,9 +1142,9 @@ void QDB2Result::virtual_hook(int id, void *data)
Q_ASSERT(data);
*static_cast<bool*>(data) = nextResult();
break;
- case QSqlResult::SetNumericalPrecision:
- Q_ASSERT(data);
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
+ case QSqlResult::DetachFromResultSet:
+ if (d->hStmt)
+ SQLCloseCursor(d->hStmt);
break;
default:
QSqlResult::virtual_hook(id, data);
@@ -1182,7 +1177,7 @@ QDB2Driver::~QDB2Driver()
delete d;
}
-bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString&, int,
+bool QDB2Driver::open(const QString& db, const QString& user, const QString& password, const QString& host, int port,
const QString& connOpts)
{
if (isOpen())
@@ -1205,6 +1200,8 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
setOpenError(true);
return false;
}
+
+ QString protocol;
// Set connection attributes
const QStringList opts(connOpts.split(QLatin1Char(';'), QString::SkipEmptyParts));
for (int i = 0; i < opts.count(); ++i) {
@@ -1235,7 +1232,10 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
} else if (opt == QLatin1String("SQL_ATTR_LOGIN_TIMEOUT")) {
v = val.toUInt();
r = SQLSetConnectAttr(d->hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) v, 0);
- } else {
+ } else if (opt.compare(QLatin1String("PROTOCOL"), Qt::CaseInsensitive) == 0) {
+ protocol = tmp;
+ }
+ else {
qWarning("QDB2Driver::open: Unknown connection attribute '%s'",
tmp.toLocal8Bit().constData());
}
@@ -1244,9 +1244,18 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
"Unable to set connection attribute '%1'").arg(opt), d);
}
+ if (protocol.isEmpty())
+ protocol = QLatin1String("PROTOCOL=TCPIP");
+
+ if (port < 0 )
+ port = 50000;
+
QString connQStr;
- connQStr = QLatin1String("DSN=") + db + QLatin1String(";UID=") + user + QLatin1String(";PWD=")
- + password;
+ connQStr = protocol + QLatin1String(";DATABASE=") + db + QLatin1String(";HOSTNAME=") + host
+ + QLatin1String(";PORT=") + QString::number(port) + QLatin1String(";UID=") + user
+ + QLatin1String(";PWD=") + password;
+
+
SQLTCHAR connOut[SQL_MAX_OPTION_STRING_LENGTH];
SQLSMALLINT cb;
@@ -1265,7 +1274,7 @@ bool QDB2Driver::open(const QString& db, const QString& user, const QString& pas
return false;
}
- d->user = user.toUpper();
+ d->user = user;
setOpen(true);
setOpenError(false);
return true;
@@ -1310,10 +1319,25 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
SQLHANDLE hStmt;
QString catalog, schema, table;
- qSplitTableQualifier(tableName.toUpper(), &catalog, &schema, &table);
+ qSplitTableQualifier(tableName, &catalog, &schema, &table);
if (schema.isEmpty())
schema = d->user;
+ if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+ catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+ else
+ catalog = catalog.toUpper();
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = schema.toUpper();
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
+
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
d->hDbc,
&hStmt);
@@ -1327,6 +1351,9 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
(SQLPOINTER) SQL_CURSOR_FORWARD_ONLY,
SQL_IS_UINTEGER);
+
+ //Aside: szSchemaName and szTableName parameters of SQLColumns
+ //are case sensitive search patterns, so no escaping is used.
r = SQLColumns(hStmt,
NULL,
0,
@@ -1407,7 +1434,13 @@ QStringList QDB2Driver::tables(QSql::TableType type) const
bool isNull;
QString fieldVal = qGetStringData(hStmt, 2, -1, isNull);
QString userVal = qGetStringData(hStmt, 1, -1, isNull);
- if (userVal != d->user)
+ QString user = d->user;
+ if ( isIdentifierEscaped(user, QSqlDriver::TableName))
+ user = stripDelimiters(user, QSqlDriver::TableName);
+ else
+ user = user.toUpper();
+
+ if (userVal != user)
fieldVal = userVal + QLatin1Char('.') + fieldVal;
tl.append(fieldVal);
r = SQLFetchScroll(hStmt,
@@ -1438,7 +1471,23 @@ QSqlIndex QDB2Driver::primaryIndex(const QString& tablename) const
return index;
}
QString catalog, schema, table;
- qSplitTableQualifier(tablename.toUpper(), &catalog, &schema, &table);
+ qSplitTableQualifier(tablename, &catalog, &schema, &table);
+
+ if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+ catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+ else
+ catalog = catalog.toUpper();
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = schema.toUpper();
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
+
r = SQLSetStmtAttr(hStmt,
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
@@ -1482,7 +1531,6 @@ bool QDB2Driver::hasFeature(DriverFeature f) const
case BatchOperations:
case LastInsertId:
case SimpleLocking:
- case LowPrecisionNumbers:
case EventNotifications:
return false;
case BLOB:
@@ -1490,6 +1538,8 @@ bool QDB2Driver::hasFeature(DriverFeature f) const
case MultipleResultSets:
case PreparedQueries:
case PositionalPlaceholders:
+ case LowPrecisionNumbers:
+ case FinishQuery:
return true;
case Unicode:
// this is the query that shows the codepage for the types:
@@ -1612,7 +1662,7 @@ QVariant QDB2Driver::handle() const
QString QDB2Driver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
index 0033418..0c4fff0 100644
--- a/src/sql/drivers/ibase/qsql_ibase.cpp
+++ b/src/sql/drivers/ibase/qsql_ibase.cpp
@@ -1123,6 +1123,19 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
// null value
QVariant v;
v.convert(qIBaseTypeName2(d->sqlda->sqlvar[i].sqltype, d->sqlda->sqlvar[i].sqlscale < 0));
+ if(v.type() == QVariant::Double) {
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ v.convert(QVariant::Int);
+ break;
+ case QSql::LowPrecisionInt64:
+ v.convert(QVariant::LongLong);
+ break;
+ case QSql::HighPrecision:
+ v.convert(QVariant::String);
+ break;
+ }
+ }
row[idx] = v;
continue;
}
@@ -1188,6 +1201,27 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
row[idx] = QVariant();
break;
}
+ if (d->sqlda->sqlvar[i].sqlscale < 0) {
+ QVariant v = row[idx];
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ if(v.convert(QVariant::Int))
+ row[idx]=v;
+ break;
+ case QSql::LowPrecisionInt64:
+ if(v.convert(QVariant::LongLong))
+ row[idx]=v;
+ break;
+ case QSql::LowPrecisionDouble:
+ if(v.convert(QVariant::Double))
+ row[idx]=v;
+ break;
+ case QSql::HighPrecision:
+ if(v.convert(QVariant::String))
+ row[idx]=v;
+ break;
+ }
+ }
}
return true;
@@ -1359,7 +1393,6 @@ bool QIBaseDriver::hasFeature(DriverFeature f) const
case LastInsertId:
case BatchOperations:
case SimpleLocking:
- case LowPrecisionNumbers:
case FinishQuery:
case MultipleResultSets:
return false;
@@ -1369,6 +1402,7 @@ bool QIBaseDriver::hasFeature(DriverFeature f) const
case Unicode:
case BLOB:
case EventNotifications:
+ case LowPrecisionNumbers:
return true;
}
return false;
@@ -1578,12 +1612,16 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
QSqlQuery q(createResult());
q.setForwardOnly(true);
-
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
q.exec(QLatin1String("SELECT a.RDB$FIELD_NAME, b.RDB$FIELD_TYPE, b.RDB$FIELD_LENGTH, "
"b.RDB$FIELD_SCALE, b.RDB$FIELD_PRECISION, a.RDB$NULL_FLAG "
"FROM RDB$RELATION_FIELDS a, RDB$FIELDS b "
"WHERE b.RDB$FIELD_NAME = a.RDB$FIELD_SOURCE "
- "AND a.RDB$RELATION_NAME = '") + tablename.toUpper() + QLatin1String("' "
+ "AND a.RDB$RELATION_NAME = '") + table + QLatin1String("' "
"ORDER BY a.RDB$FIELD_POSITION"));
while (q.next()) {
@@ -1611,12 +1649,18 @@ QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
if (!isOpen())
return index;
+ QString tablename = table;
+ if (isIdentifierEscaped(tablename, QSqlDriver::TableName))
+ tablename = stripDelimiters(tablename, QSqlDriver::TableName);
+ else
+ tablename = tablename.toUpper();
+
QSqlQuery q(createResult());
q.setForwardOnly(true);
q.exec(QLatin1String("SELECT a.RDB$INDEX_NAME, b.RDB$FIELD_NAME, d.RDB$FIELD_TYPE, d.RDB$FIELD_SCALE "
"FROM RDB$RELATION_CONSTRAINTS a, RDB$INDEX_SEGMENTS b, RDB$RELATION_FIELDS c, RDB$FIELDS d "
"WHERE a.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
- "AND a.RDB$RELATION_NAME = '") + table.toUpper() +
+ "AND a.RDB$RELATION_NAME = '") + tablename +
QLatin1String(" 'AND a.RDB$INDEX_NAME = b.RDB$INDEX_NAME "
"AND c.RDB$RELATION_NAME = a.RDB$RELATION_NAME "
"AND c.RDB$FIELD_NAME = b.RDB$FIELD_NAME "
@@ -1742,7 +1786,7 @@ bool QIBaseDriver::subscribeToNotificationImplementation(const QString &name)
eBuffer->resultBuffer);
if (status[0] == 1 && status[1]) {
- setLastError(QSqlError(QString(QLatin1String("Could not subscribe to event notifications for %1.")).arg(name)));
+ setLastError(QSqlError(QString::fromLatin1("Could not subscribe to event notifications for %1.").arg(name)));
d->eventBuffers.remove(name);
qFreeEventBuffer(eBuffer);
return false;
@@ -1770,7 +1814,7 @@ bool QIBaseDriver::unsubscribeFromNotificationImplementation(const QString &name
isc_cancel_events(status, &d->ibase, &eBuffer->eventId);
if (status[0] == 1 && status[1]) {
- setLastError(QSqlError(QString(QLatin1String("Could not unsubscribe from event notifications for %1.")).arg(name)));
+ setLastError(QSqlError(QString::fromLatin1("Could not unsubscribe from event notifications for %1.").arg(name)));
return false;
}
@@ -1828,7 +1872,7 @@ void QIBaseDriver::qHandleEventNotification(void *updatedResultBuffer)
QString QIBaseDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp
index dd4127c..bfd65fc 100644
--- a/src/sql/drivers/mysql/qsql_mysql.cpp
+++ b/src/sql/drivers/mysql/qsql_mysql.cpp
@@ -167,12 +167,11 @@ class QMYSQLResultPrivate : public QObject
{
Q_OBJECT
public:
- QMYSQLResultPrivate(const QMYSQLDriver* dp) : driver(dp), result(0),
+ QMYSQLResultPrivate(const QMYSQLDriver* dp, const QMYSQLResult* d) : driver(dp), result(0), q(d),
rowsAffected(0), hasBlobs(false)
#if MYSQL_VERSION_ID >= 40108
, stmt(0), meta(0), inBinds(0), outBinds(0)
#endif
- , precisionPolicy(QSql::HighPrecision)
{
connect(dp, SIGNAL(destroyed()), this, SLOT(driverDestroyed()));
}
@@ -180,6 +179,7 @@ public:
const QMYSQLDriver* driver;
MYSQL_RES *result;
MYSQL_ROW row;
+ const QMYSQLResult* q;
int rowsAffected;
@@ -209,7 +209,6 @@ public:
MYSQL_BIND *inBinds;
MYSQL_BIND *outBinds;
#endif
- QSql::NumericalPrecisionPolicy precisionPolicy;
private Q_SLOTS:
void driverDestroyed() { driver = NULL; }
};
@@ -254,6 +253,10 @@ static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
break;
case FIELD_TYPE_FLOAT :
case FIELD_TYPE_DOUBLE :
+ case FIELD_TYPE_DECIMAL :
+#if defined(FIELD_TYPE_NEWDECIMAL)
+ case FIELD_TYPE_NEWDECIMAL:
+#endif
type = QVariant::Double;
break;
case FIELD_TYPE_DATE :
@@ -277,7 +280,6 @@ static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
default:
case FIELD_TYPE_ENUM :
case FIELD_TYPE_SET :
- case FIELD_TYPE_DECIMAL :
type = QVariant::String;
break;
}
@@ -354,6 +356,7 @@ bool QMYSQLResultPrivate::bindInValues()
while((fieldInfo = mysql_fetch_field(meta))) {
QMyField &f = fields[i];
f.myField = fieldInfo;
+
f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
if (qIsBlob(fieldInfo->type)) {
// the size of a blob-field is available as soon as we call
@@ -384,7 +387,7 @@ bool QMYSQLResultPrivate::bindInValues()
QMYSQLResult::QMYSQLResult(const QMYSQLDriver* db)
: QSqlResult(db)
{
- d = new QMYSQLResultPrivate(db);
+ d = new QMYSQLResultPrivate(db, this);
}
QMYSQLResult::~QMYSQLResult()
@@ -599,15 +602,16 @@ QVariant QMYSQLResult::data(int field)
case QVariant::Double: {
QVariant v;
bool ok=false;
- switch(d->precisionPolicy) {
+ double dbl = val.toDouble(&ok);
+ switch(numericalPrecisionPolicy()) {
case QSql::LowPrecisionInt32:
- v=val.toInt(&ok);
+ v=QVariant(dbl).toInt();
break;
case QSql::LowPrecisionInt64:
- v = val.toLongLong(&ok);
+ v = QVariant(dbl).toLongLong();
break;
case QSql::LowPrecisionDouble:
- v = val.toDouble(&ok);
+ v = QVariant(dbl);
break;
case QSql::HighPrecision:
default:
@@ -620,6 +624,7 @@ QVariant QMYSQLResult::data(int field)
else
return QVariant();
}
+ return QVariant(val.toDouble());
case QVariant::Date:
return qDateFromString(val);
case QVariant::Time:
@@ -679,6 +684,7 @@ bool QMYSQLResult::reset (const QString& query)
setSelect(numFields != 0);
d->fields.resize(numFields);
d->rowsAffected = mysql_affected_rows(d->driver->d->mysql);
+
if (isSelect()) {
for(int i = 0; i < numFields; i++) {
MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
@@ -813,10 +819,6 @@ void QMYSQLResult::virtual_hook(int id, void *data)
Q_ASSERT(data);
*static_cast<bool*>(data) = nextResult();
break;
- case QSqlResult::SetNumericalPrecision:
- Q_ASSERT(data);
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
QSqlResult::virtual_hook(id, data);
}
@@ -1148,7 +1150,6 @@ bool QMYSQLDriver::hasFeature(DriverFeature f) const
case NamedPlaceholders:
case BatchOperations:
case SimpleLocking:
- case LowPrecisionNumbers:
case EventNotifications:
case FinishQuery:
return false;
@@ -1156,6 +1157,7 @@ bool QMYSQLDriver::hasFeature(DriverFeature f) const
case BLOB:
case LastInsertId:
case Unicode:
+ case LowPrecisionNumbers:
return true;
case PreparedQueries:
case PositionalPlaceholders:
@@ -1342,7 +1344,7 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
QSqlQuery i(createResult());
QString stmt(QLatin1String("show index from %1;"));
QSqlRecord fil = record(tablename);
- i.exec(stmt.arg(escapeIdentifier(tablename, QSqlDriver::TableName)));
+ i.exec(stmt.arg(tablename));
while (i.isActive() && i.next()) {
if (i.value(2).toString() == QLatin1String("PRIMARY")) {
idx.append(fil.field(i.value(4).toString()));
@@ -1357,14 +1359,19 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
QSqlRecord QMYSQLDriver::record(const QString& tablename) const
{
+ QString table=tablename;
+ if(isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+
QSqlRecord info;
if (!isOpen())
return info;
- MYSQL_RES* r = mysql_list_fields(d->mysql, tablename.toLocal8Bit().constData(), 0);
+ MYSQL_RES* r = mysql_list_fields(d->mysql, table.toLocal8Bit().constData(), 0);
if (!r) {
return info;
}
MYSQL_FIELD* field;
+
while ((field = mysql_fetch_field(r)))
info.append(qToField(field, d->tc));
mysql_free_result(r);
@@ -1464,13 +1471,21 @@ QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) cons
QString QMYSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('`')) && identifier.right(1) != QString(QLatin1Char('`')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
res.replace(QLatin1Char('.'), QLatin1String("`.`"));
}
return res;
}
+bool QMYSQLDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const
+{
+ Q_UNUSED(type);
+ return identifier.size() > 2
+ && identifier.startsWith(QLatin1Char('`')) //left delimited
+ && identifier.endsWith(QLatin1Char('`')); //right delimited
+}
+
QT_END_NAMESPACE
-#include "qsql_mysql.moc" \ No newline at end of file
+#include "qsql_mysql.moc"
diff --git a/src/sql/drivers/mysql/qsql_mysql.h b/src/sql/drivers/mysql/qsql_mysql.h
index 78ff746..041594b 100644
--- a/src/sql/drivers/mysql/qsql_mysql.h
+++ b/src/sql/drivers/mysql/qsql_mysql.h
@@ -69,6 +69,7 @@ class QSqlRecordInfo;
class QMYSQLResult : public QSqlResult
{
friend class QMYSQLDriver;
+ friend class QMYSQLResultPrivate;
public:
explicit QMYSQLResult(const QMYSQLDriver* db);
~QMYSQLResult();
@@ -123,6 +124,9 @@ public:
QVariant handle() const;
QString escapeIdentifier(const QString &identifier, IdentifierType type) const;
+protected Q_SLOTS:
+ bool isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const;
+
protected:
bool beginTransaction();
bool commitTransaction();
diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp
index bbbbc22..d5fb10f 100644
--- a/src/sql/drivers/oci/qsql_oci.cpp
+++ b/src/sql/drivers/oci/qsql_oci.cpp
@@ -148,7 +148,6 @@ struct QOCIResultPrivate
bool transaction;
int serverVersion;
int prefetchRows, prefetchMem;
- QSql::NumericalPrecisionPolicy precisionPolicy;
void setCharset(OCIBind* hbnd);
void setStatementAttributes();
@@ -405,7 +404,6 @@ struct QOCIDriverPrivate
int serverVersion;
ub4 prefetchRows;
ub2 prefetchMem;
- QSql::NumericalPrecisionPolicy precisionPolicy;
QString user;
void allocErrorHandle();
@@ -413,7 +411,7 @@ struct QOCIDriverPrivate
QOCIDriverPrivate::QOCIDriverPrivate()
: env(0), svc(0), srvhp(0), authp(0), err(0), transaction(false), serverVersion(-1),
- prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM), precisionPolicy(QSql::HighPrecision)
+ prefetchRows(-1), prefetchMem(QOCI_PREFETCH_MEM)
{
}
@@ -621,136 +619,6 @@ static QSqlField qFromOraInf(const OraFieldInfo &ofi)
return f;
}
-static OraFieldInfo qMakeOraField(const QOCIResultPrivate* p, OCIParam* param)
-{
- OraFieldInfo ofi;
- ub2 colType(0);
- text *colName = 0;
- ub4 colNameLen(0);
- sb1 colScale(0);
- ub2 colLength(0);
- ub2 colFieldLength(0);
- sb2 colPrecision(0);
- ub1 colIsNull(0);
- int r(0);
- QVariant::Type type(QVariant::Invalid);
-
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colType,
- 0,
- OCI_ATTR_DATA_TYPE,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colName,
- &colNameLen,
- OCI_ATTR_NAME,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colLength,
- 0,
- OCI_ATTR_DATA_SIZE, /* in bytes */
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-
-#ifdef OCI_ATTR_CHAR_SIZE
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colFieldLength,
- 0,
- OCI_ATTR_CHAR_SIZE,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-#else
- // for Oracle8.
- colFieldLength = colLength;
-#endif
-
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colPrecision,
- 0,
- OCI_ATTR_PRECISION,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colScale,
- 0,
- OCI_ATTR_SCALE,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colType,
- 0,
- OCI_ATTR_DATA_TYPE,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
- r = OCIAttrGet(param,
- OCI_DTYPE_PARAM,
- &colIsNull,
- 0,
- OCI_ATTR_IS_NULL,
- p->err);
- if (r != 0)
- qOraWarning("qMakeOraField:", p->err);
-
- type = qDecodeOCIType(colType, p->precisionPolicy);
-
- if (type == QVariant::Int) {
- if (colLength == 22 && colPrecision == 0 && colScale == 0)
- type = QVariant::String;
- if (colScale > 0)
- type = QVariant::String;
- }
-
- // bind as double if the precision policy asks for it
- if (((colType == SQLT_FLT) || (colType == SQLT_NUM))
- && (p->precisionPolicy == QSql::LowPrecisionDouble)) {
- type = QVariant::Double;
- }
-
- // bind as int32 or int64 if the precision policy asks for it
- if ((colType == SQLT_NUM) || (colType == SQLT_VNU) || (colType == SQLT_UIN)
- || (colType == SQLT_INT)) {
- if (p->precisionPolicy == QSql::LowPrecisionInt64)
- type = QVariant::LongLong;
- else if (p->precisionPolicy == QSql::LowPrecisionInt32)
- type = QVariant::Int;
- }
-
- if (colType == SQLT_BLOB)
- colLength = 0;
-
- // colNameLen is length in bytes
- ofi.name = QString(reinterpret_cast<const QChar*>(colName), colNameLen / 2);
- ofi.type = type;
- ofi.oraType = colType;
- ofi.oraFieldLength = colFieldLength;
- ofi.oraLength = colLength;
- ofi.oraScale = colScale;
- ofi.oraPrecision = colPrecision;
- ofi.oraIsNull = colIsNull;
-
- return ofi;
-}
-
-
/*!
\internal
@@ -806,6 +674,7 @@ public:
private:
char* create(int position, int size);
OCILobLocator ** createLobLocator(int position, OCIEnv* env);
+ OraFieldInfo qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const;
class OraFieldInf
{
@@ -1137,6 +1006,135 @@ int QOCICols::readPiecewise(QVector<QVariant> &values, int index)
return r;
}
+OraFieldInfo QOCICols::qMakeOraField(const QOCIResultPrivate* p, OCIParam* param) const
+{
+ OraFieldInfo ofi;
+ ub2 colType(0);
+ text *colName = 0;
+ ub4 colNameLen(0);
+ sb1 colScale(0);
+ ub2 colLength(0);
+ ub2 colFieldLength(0);
+ sb2 colPrecision(0);
+ ub1 colIsNull(0);
+ int r(0);
+ QVariant::Type type(QVariant::Invalid);
+
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colType,
+ 0,
+ OCI_ATTR_DATA_TYPE,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colName,
+ &colNameLen,
+ OCI_ATTR_NAME,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colLength,
+ 0,
+ OCI_ATTR_DATA_SIZE, /* in bytes */
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+
+#ifdef OCI_ATTR_CHAR_SIZE
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colFieldLength,
+ 0,
+ OCI_ATTR_CHAR_SIZE,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+#else
+ // for Oracle8.
+ colFieldLength = colLength;
+#endif
+
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colPrecision,
+ 0,
+ OCI_ATTR_PRECISION,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colScale,
+ 0,
+ OCI_ATTR_SCALE,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colType,
+ 0,
+ OCI_ATTR_DATA_TYPE,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+ r = OCIAttrGet(param,
+ OCI_DTYPE_PARAM,
+ &colIsNull,
+ 0,
+ OCI_ATTR_IS_NULL,
+ p->err);
+ if (r != 0)
+ qOraWarning("qMakeOraField:", p->err);
+
+ type = qDecodeOCIType(colType, p->q->numericalPrecisionPolicy());
+
+ if (type == QVariant::Int) {
+ if (colLength == 22 && colPrecision == 0 && colScale == 0)
+ type = QVariant::String;
+ if (colScale > 0)
+ type = QVariant::String;
+ }
+
+ // bind as double if the precision policy asks for it
+ if (((colType == SQLT_FLT) || (colType == SQLT_NUM))
+ && (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)) {
+ type = QVariant::Double;
+ }
+
+ // bind as int32 or int64 if the precision policy asks for it
+ if ((colType == SQLT_NUM) || (colType == SQLT_VNU) || (colType == SQLT_UIN)
+ || (colType == SQLT_INT)) {
+ if (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
+ type = QVariant::LongLong;
+ else if (p->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
+ type = QVariant::Int;
+ }
+
+ if (colType == SQLT_BLOB)
+ colLength = 0;
+
+ // colNameLen is length in bytes
+ ofi.name = QString(reinterpret_cast<const QChar*>(colName), colNameLen / 2);
+ ofi.type = type;
+ ofi.oraType = colType;
+ ofi.oraFieldLength = colFieldLength;
+ ofi.oraLength = colLength;
+ ofi.oraScale = colScale;
+ ofi.oraPrecision = colPrecision;
+ ofi.oraIsNull = colIsNull;
+
+ return ofi;
+}
+
struct QOCIBatchColumn
{
inline QOCIBatchColumn()
@@ -1580,19 +1578,19 @@ void QOCICols::getValues(QVector<QVariant> &v, int index)
case QVariant::Double:
case QVariant::Int:
case QVariant::LongLong:
- if (d->precisionPolicy != QSql::HighPrecision) {
- if ((d->precisionPolicy == QSql::LowPrecisionDouble)
+ if (d->q->numericalPrecisionPolicy() != QSql::HighPrecision) {
+ if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
&& (fld.typ == QVariant::Double)) {
v[index + i] = *reinterpret_cast<double *>(fld.data);
break;
- } else if ((d->precisionPolicy == QSql::LowPrecisionInt64)
+ } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
&& (fld.typ == QVariant::LongLong)) {
qint64 qll = 0;
OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64),
OCI_NUMBER_SIGNED, &qll);
v[index + i] = qll;
break;
- } else if ((d->precisionPolicy == QSql::LowPrecisionInt32)
+ } else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
&& (fld.typ == QVariant::Int)) {
v[index + i] = *reinterpret_cast<int *>(fld.data);
break;
@@ -1618,8 +1616,7 @@ void QOCICols::getValues(QVector<QVariant> &v, int index)
QOCIResultPrivate::QOCIResultPrivate(QOCIResult *result, const QOCIDriverPrivate *driver)
: cols(0), q(result), env(driver->env), err(0), svc(const_cast<OCISvcCtx*&>(driver->svc)),
sql(0), transaction(driver->transaction), serverVersion(driver->serverVersion),
- prefetchRows(driver->prefetchRows), prefetchMem(driver->prefetchMem),
- precisionPolicy(driver->precisionPolicy)
+ prefetchRows(driver->prefetchRows), prefetchMem(driver->prefetchMem)
{
int r = OCIHandleAlloc(env,
reinterpret_cast<void **>(&err),
@@ -1901,11 +1898,8 @@ void QOCIResult::virtual_hook(int id, void *data)
case QSqlResult::BatchOperation:
QOCICols::execBatch(d, boundValues(), *reinterpret_cast<bool *>(data));
break;
- case QSqlResult::SetNumericalPrecision:
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
- QSqlResult::virtual_hook(id, data);
+ QSqlCachedResult::virtual_hook(id, data);
}
}
@@ -2040,8 +2034,8 @@ bool QOCIDriver::open(const QString & db,
QString connectionString = db;
if (!hostname.isEmpty())
connectionString =
- QString(QLatin1String("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
- "(CONNECT_DATA=(SID=%3)))")).arg(hostname).arg((port > -1 ? port : 1521)).arg(db);
+ QString::fromLatin1("(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=%1)(Port=%2))"
+ "(CONNECT_DATA=(SID=%3)))").arg(hostname).arg((port > -1 ? port : 1521)).arg(db);
r = OCIHandleAlloc(d->env, reinterpret_cast<void **>(&d->srvhp), OCI_HTYPE_SERVER, 0, 0);
if (r == OCI_SUCCESS)
@@ -2108,7 +2102,7 @@ bool QOCIDriver::open(const QString & db,
setOpen(true);
setOpenError(false);
- d->user = user.toUpper();
+ d->user = user;
return true;
}
@@ -2210,9 +2204,16 @@ QStringList QOCIDriver::tables(QSql::TableType type) const
"and owner != 'WKSYS'"
"and owner != 'CTXSYS'"
"and owner != 'WMSYS'"));
+
+ QString user = d->user;
+ if ( isIdentifierEscaped(user, QSqlDriver::TableName))
+ user = stripDelimiters(user, QSqlDriver::TableName);
+ else
+ user = user.toUpper();
+
while (t.next()) {
- if (t.value(0).toString().toUpper() != d->user.toUpper())
- tl.append(t.value(0).toString() + QLatin1String(".") + t.value(1).toString());
+ if (t.value(0).toString().toUpper() != user.toUpper())
+ tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
@@ -2228,7 +2229,7 @@ QStringList QOCIDriver::tables(QSql::TableType type) const
"and owner != 'WMSYS'"));
while (t.next()) {
if (t.value(0).toString().toUpper() != d->user.toUpper())
- tl.append(t.value(0).toString() + QLatin1String(".") + t.value(1).toString());
+ tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString());
else
tl.append(t.value(1).toString());
}
@@ -2247,10 +2248,10 @@ void qSplitTableAndOwner(const QString & tname, QString * tbl,
{
int i = tname.indexOf(QLatin1Char('.')); // prefixed with owner?
if (i != -1) {
- *tbl = tname.right(tname.length() - i - 1).toUpper();
- *owner = tname.left(i).toUpper();
+ *tbl = tname.right(tname.length() - i - 1);
+ *owner = tname.left(i);
} else {
- *tbl = tname.toUpper();
+ *tbl = tname;
}
}
@@ -2266,7 +2267,7 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
QString stmt(QLatin1String("select column_name, data_type, data_length, "
"data_precision, data_scale, nullable, data_default%1"
"from all_tab_columns "
- "where upper(table_name)=%2"));
+ "where table_name=%2"));
if (d->serverVersion >= 9)
stmt = stmt.arg(QLatin1String(", char_length "));
else
@@ -2274,11 +2275,23 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
bool buildRecordInfo = false;
QString table, owner, tmpStmt;
qSplitTableAndOwner(tablename, &table, &owner);
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
+
tmpStmt = stmt.arg(QLatin1Char('\'') + table + QLatin1Char('\''));
if (owner.isEmpty()) {
owner = d->user;
}
- tmpStmt += QLatin1String(" and upper(owner)='") + owner + QLatin1String("'");
+
+ if (isIdentifierEscaped(owner, QSqlDriver::TableName))
+ owner = stripDelimiters(owner, QSqlDriver::TableName);
+ else
+ owner = owner.toUpper();
+
+ tmpStmt += QLatin1String(" and owner='") + owner + QLatin1Char('\'');
t.setForwardOnly(true);
t.exec(tmpStmt);
if (!t.next()) { // try and see if the tablename is a synonym
@@ -2295,7 +2308,7 @@ QSqlRecord QOCIDriver::record(const QString& tablename) const
<< QLatin1String("BINARY_DOUBLE");
if (buildRecordInfo) {
do {
- QVariant::Type ty = qDecodeOCIType(t.value(1).toString(),t.numericalPrecisionPolicy());
+ QVariant::Type ty = qDecodeOCIType(t.value(1).toString(), t.numericalPrecisionPolicy());
QSqlField f(t.value(0).toString(), ty);
f.setRequired(t.value(5).toString() == QLatin1String("N"));
f.setPrecision(t.value(4).toInt());
@@ -2327,11 +2340,23 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
bool buildIndex = false;
QString table, owner, tmpStmt;
qSplitTableAndOwner(tablename, &table, &owner);
- tmpStmt = stmt + QLatin1String(" and upper(a.table_name)='") + table + QLatin1String("'");
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = table.toUpper();
+
+ tmpStmt = stmt + QLatin1String(" and a.table_name='") + table + QLatin1Char('\'');
if (owner.isEmpty()) {
owner = d->user;
}
- tmpStmt += QLatin1String(" and upper(a.owner)='") + owner + QLatin1String("'");
+
+ if (isIdentifierEscaped(owner, QSqlDriver::TableName))
+ owner = stripDelimiters(owner, QSqlDriver::TableName);
+ else
+ owner = owner.toUpper();
+
+ tmpStmt += QLatin1String(" and a.owner='") + owner + QLatin1Char('\'');
t.setForwardOnly(true);
t.exec(tmpStmt);
@@ -2355,7 +2380,7 @@ QSqlIndex QOCIDriver::primaryIndex(const QString& tablename) const
tt.exec(QLatin1String("select data_type from all_tab_columns where table_name='") +
t.value(2).toString() + QLatin1String("' and column_name='") +
t.value(0).toString() + QLatin1String("' and owner='") +
- owner +QLatin1String("'"));
+ owner + QLatin1Char('\''));
if (!tt.next()) {
return QSqlIndex();
}
@@ -2425,13 +2450,14 @@ QVariant QOCIDriver::handle() const
return qVariantFromValue(d->env);
}
-QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType /* type */) const
+QString QOCIDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
{
QString res = identifier;
- res.replace(QLatin1Char('"'), QLatin1String("\"\""));
- if (identifier.indexOf(QLatin1Char(' ')) != -1)
+ if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type)) {
+ res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
-// res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ res.replace(QLatin1Char('.'), QLatin1String("\".\""));
+ }
return res;
}
diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp
index e0aa9b5..7bda969 100644
--- a/src/sql/drivers/odbc/qsql_odbc.cpp
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp
@@ -86,6 +86,7 @@ static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL
class QODBCDriverPrivate
{
public:
+ enum DefaultCase{Lower, Mixed, Upper, Sensitive};
QODBCDriverPrivate()
: hEnv(0), hDbc(0), useSchema(false), disconnectCount(0), isMySqlServer(false),
isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false)
@@ -113,13 +114,16 @@ public:
bool setConnectionOptions(const QString& connOpts);
void splitTableQualifier(const QString &qualifier, QString &catalog,
QString &schema, QString &table);
+ DefaultCase defaultCase() const;
+ QString adjustCase(const QString&) const;
+ QChar quoteChar() const;
};
class QODBCPrivate
{
public:
QODBCPrivate()
- : hEnv(0), hDbc(0), hStmt(0), useSchema(false), hasSQLFetchScroll(true), precisionPolicy(QSql::HighPrecision)
+ : hEnv(0), hDbc(0), hStmt(0), useSchema(false), hasSQLFetchScroll(true)
{
unicode = false;
}
@@ -139,7 +143,6 @@ public:
int fieldCacheIdx;
int disconnectCount;
bool hasSQLFetchScroll;
- QSql::NumericalPrecisionPolicy precisionPolicy;
bool isStmtHandleValid(const QSqlDriver *driver);
void updateStmtHandleState(const QSqlDriver *driver);
@@ -201,14 +204,14 @@ static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode
static QString qODBCWarn(const QODBCPrivate* odbc, int *nativeCode = 0)
{
- return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1String(" ")
- + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc) + QLatin1String(" ")
+ return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
+ + qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc) + QLatin1Char(' ')
+ qWarnODBCHandle(SQL_HANDLE_STMT, odbc->hStmt, nativeCode));
}
static QString qODBCWarn(const QODBCDriverPrivate* odbc, int *nativeCode = 0)
{
- return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1String(" ")
+ return (qWarnODBCHandle(SQL_HANDLE_ENV, odbc->hEnv) + QLatin1Char(' ')
+ qWarnODBCHandle(SQL_HANDLE_DBC, odbc->hDbc, nativeCode));
}
@@ -428,6 +431,26 @@ static QVariant qGetIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
return uint(intbuf);
}
+static QVariant qGetDoubleData(SQLHANDLE hStmt, int column)
+{
+ SQLDOUBLE dblbuf;
+ QSQLLEN lengthIndicator = 0;
+ SQLRETURN r = SQLGetData(hStmt,
+ column+1,
+ SQL_C_DOUBLE,
+ (SQLPOINTER) &dblbuf,
+ 0,
+ &lengthIndicator);
+ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ return QVariant(QVariant::Invalid);
+ }
+ if(lengthIndicator == SQL_NULL_DATA)
+ return QVariant(QVariant::Double);
+
+ return (double) dblbuf;
+}
+
+
static QVariant qGetBigIntData(SQLHANDLE hStmt, int column, bool isSigned = true)
{
SQLBIGINT lngbuf = 0;
@@ -541,6 +564,29 @@ static int qGetODBCVersion(const QString &connOpts)
return SQL_OV_ODBC2;
}
+QChar QODBCDriverPrivate::quoteChar() const
+{
+ static bool isQuoteInitialized = false;
+ static QChar quote = QChar::fromLatin1('"');
+ if (!isQuoteInitialized) {
+ char driverResponse[4];
+ SQLSMALLINT length;
+ int r = SQLGetInfo(hDbc,
+ SQL_IDENTIFIER_QUOTE_CHAR,
+ &driverResponse,
+ sizeof(driverResponse),
+ &length);
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+ quote = QChar::fromLatin1(driverResponse[0]);
+ } else {
+ quote = QChar::fromLatin1('"');
+ }
+ isQuoteInitialized = true;
+ }
+ return quote;
+}
+
+
bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
{
// Set any connection attributes
@@ -691,6 +737,65 @@ void QODBCDriverPrivate::splitTableQualifier(const QString & qualifier, QString
}
}
+QODBCDriverPrivate::DefaultCase QODBCDriverPrivate::defaultCase() const
+{
+ static bool isInitialized = false;
+ static DefaultCase ret;
+
+ if (!isInitialized) {
+ SQLUSMALLINT casing;
+ int r = SQLGetInfo(hDbc,
+ SQL_IDENTIFIER_CASE,
+ &casing,
+ sizeof(casing),
+ NULL);
+ if ( r != SQL_SUCCESS)
+ ret = Lower;//arbitrary case if driver cannot be queried
+ else {
+ switch (casing) {
+ case (SQL_IC_UPPER):
+ ret = Upper;
+ break;
+ case (SQL_IC_LOWER):
+ ret = Lower;
+ break;
+ case (SQL_IC_SENSITIVE):
+ ret = Sensitive;
+ break;
+ case (SQL_IC_MIXED):
+ ret = Mixed;
+ break;
+ default:
+ ret = Upper;
+ }
+ }
+ isInitialized = true;
+ }
+ return ret;
+}
+
+/*
+ Adjust the casing of an identifier to match what the
+ database engine would have done to it.
+*/
+QString QODBCDriverPrivate::adjustCase(const QString &identifier) const
+{
+ QString ret = identifier;
+ switch(defaultCase()) {
+ case (Lower):
+ ret = identifier.toLower();
+ break;
+ case (Upper):
+ ret = identifier.toUpper();
+ break;
+ case(Mixed):
+ case(Sensitive):
+ default:
+ ret = identifier;
+ }
+ return ret;
+}
+
////////////////////////////////////////////////////////////////////////////
QODBCResult::QODBCResult(const QODBCDriver * db, QODBCDriverPrivate* p)
@@ -1009,29 +1114,21 @@ QVariant QODBCResult::data(int field)
d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), true);
break;
case QVariant::Double:
- {
- QString value=qGetStringData(d->hStmt, i, info.length(), false);
- bool ok=false;
- switch(d->precisionPolicy) {
- case QSql::LowPrecisionInt32:
- d->fieldCache[i] = value.toInt(&ok);
- break;
- case QSql::LowPrecisionInt64:
- d->fieldCache[i] = value.toLongLong(&ok);
- break;
- case QSql::LowPrecisionDouble:
- d->fieldCache[i] = value.toDouble(&ok);
- break;
- case QSql::HighPrecision:
- default:
- d->fieldCache[i] = value;
- ok=true;
- break;
- }
- if(ok==false)
- d->fieldCache[i] = QVariant();
- break;
+ switch(numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ d->fieldCache[i] = qGetIntData(d->hStmt, i);
+ break;
+ case QSql::LowPrecisionInt64:
+ d->fieldCache[i] = qGetBigIntData(d->hStmt, i);
+ break;
+ case QSql::LowPrecisionDouble:
+ d->fieldCache[i] = qGetDoubleData(d->hStmt, i);
+ break;
+ case QSql::HighPrecision:
+ d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), false);
+ break;
}
+ break;
default:
d->fieldCache[i] = QVariant(qGetStringData(d->hStmt, i, info.length(), false));
break;
@@ -1528,10 +1625,6 @@ void QODBCResult::virtual_hook(int id, void *data)
Q_ASSERT(data);
*static_cast<bool*>(data) = nextResult();
break;
- case QSqlResult::SetNumericalPrecision:
- Q_ASSERT(data);
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
QSqlResult::virtual_hook(id, data);
}
@@ -2068,6 +2161,22 @@ QSqlIndex QODBCDriver::primaryIndex(const QString& tablename) const
}
QString catalog, schema, table;
d->splitTableQualifier(tablename, catalog, schema, table);
+
+ if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+ catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+ else
+ catalog = d->adjustCase(catalog);
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = d->adjustCase(schema);
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = d->adjustCase(table);
+
r = SQLSetStmtAttr(hStmt,
SQL_ATTR_CURSOR_TYPE,
(SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
@@ -2170,6 +2279,22 @@ QSqlRecord QODBCDriver::record(const QString& tablename) const
SQLHANDLE hStmt;
QString catalog, schema, table;
d->splitTableQualifier(tablename, catalog, schema, table);
+
+ if (isIdentifierEscaped(catalog, QSqlDriver::TableName))
+ catalog = stripDelimiters(catalog, QSqlDriver::TableName);
+ else
+ catalog = d->adjustCase(catalog);
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = d->adjustCase(schema);
+
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+ else
+ table = d->adjustCase(table);
+
SQLRETURN r = SQLAllocHandle(SQL_HANDLE_STMT,
d->hDbc,
&hStmt);
@@ -2279,12 +2404,12 @@ QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType)
{
QString res = identifier;
if (d->isMySqlServer) {
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('`')) && identifier.right(1) != QString(QLatin1Char('`')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('`')) && !identifier.endsWith(QLatin1Char('`')) ) {
res.prepend(QLatin1Char('`')).append(QLatin1Char('`'));
res.replace(QLatin1Char('.'), QLatin1String("`.`"));
}
} else {
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
@@ -2293,4 +2418,12 @@ QString QODBCDriver::escapeIdentifier(const QString &identifier, IdentifierType)
return res;
}
+bool QODBCDriver::isIdentifierEscapedImplementation(const QString &identifier, IdentifierType) const
+{
+ QChar quote = d->quoteChar();
+ return identifier.size() > 2
+ && identifier.startsWith(quote) //left delimited
+ && identifier.endsWith(quote); //right delimited
+}
+
QT_END_NAMESPACE
diff --git a/src/sql/drivers/odbc/qsql_odbc.h b/src/sql/drivers/odbc/qsql_odbc.h
index 936164e..1207ee1 100644
--- a/src/sql/drivers/odbc/qsql_odbc.h
+++ b/src/sql/drivers/odbc/qsql_odbc.h
@@ -145,10 +145,14 @@ public:
QString escapeIdentifier(const QString &identifier, IdentifierType type) const;
+protected Q_SLOTS:
+ bool isIdentifierEscapedImplementation(const QString &identifier, IdentifierType type) const;
+
protected:
bool beginTransaction();
bool commitTransaction();
bool rollbackTransaction();
+
private:
void init();
bool endTrans();
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp
index 770df4c..0c92013 100644
--- a/src/sql/drivers/psql/qsql_psql.cpp
+++ b/src/sql/drivers/psql/qsql_psql.cpp
@@ -136,13 +136,12 @@ void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
class QPSQLResultPrivate
{
public:
- QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1), precisionPolicy(QSql::HighPrecision) {}
+ QPSQLResultPrivate(QPSQLResult *qq): q(qq), driver(0), result(0), currentSize(-1) {}
QPSQLResult *q;
const QPSQLDriverPrivate *driver;
PGresult *result;
int currentSize;
- QSql::NumericalPrecisionPolicy precisionPolicy;
bool preparedQueriesEnabled;
QString preparedStmtId;
@@ -320,15 +319,16 @@ QVariant QPSQLResult::data(int i)
return atoi(val);
case QVariant::Double:
if (ptype == QNUMERICOID) {
- if (d->precisionPolicy != QSql::HighPrecision) {
+ if (numericalPrecisionPolicy() != QSql::HighPrecision) {
QVariant retval;
bool convert;
- if (d->precisionPolicy == QSql::LowPrecisionInt64)
- retval = QString::fromAscii(val).toLongLong(&convert);
- else if (d->precisionPolicy == QSql::LowPrecisionInt32)
- retval = QString::fromAscii(val).toInt(&convert);
- else if (d->precisionPolicy == QSql::LowPrecisionDouble)
- retval = QString::fromAscii(val).toDouble(&convert);
+ double dbl=QString::fromAscii(val).toDouble(&convert);
+ if (numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
+ retval = (qlonglong)dbl;
+ else if (numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
+ retval = (int)dbl;
+ else if (numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
+ retval = dbl;
if (!convert)
return QVariant();
return retval;
@@ -467,9 +467,6 @@ void QPSQLResult::virtual_hook(int id, void *data)
Q_ASSERT(data);
switch (id) {
- case QSqlResult::SetNumericalPrecision:
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
QSqlResult::virtual_hook(id, data);
}
@@ -541,7 +538,7 @@ bool QPSQLResult::prepare(const QString &query)
qDeallocatePreparedStmt(d);
const QString stmtId = qMakePreparedStmtId();
- const QString stmt = QString(QLatin1String("PREPARE %1 AS ")).arg(stmtId).append(qReplacePlaceholderMarkers(query));
+ const QString stmt = QString::fromLatin1("PREPARE %1 AS ").arg(stmtId).append(qReplacePlaceholderMarkers(query));
PGresult *result = PQexec(d->driver->connection,
d->driver->isUtf8 ? stmt.toUtf8().constData()
@@ -570,9 +567,9 @@ bool QPSQLResult::exec()
QString stmt;
const QString params = qCreateParamString(boundValues(), d->q->driver());
if (params.isEmpty())
- stmt = QString(QLatin1String("EXECUTE %1")).arg(d->preparedStmtId);
+ stmt = QString::fromLatin1("EXECUTE %1").arg(d->preparedStmtId);
else
- stmt = QString(QLatin1String("EXECUTE %1 (%2)")).arg(d->preparedStmtId).arg(params);
+ stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId).arg(params);
d->result = PQexec(d->driver->connection,
d->driver->isUtf8 ? stmt.toUtf8().constData()
@@ -907,6 +904,16 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
QString schema;
qSplitTableName(tbl, schema);
+ if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
+ tbl = stripDelimiters(tbl, QSqlDriver::TableName);
+ else
+ tbl = tbl.toLower();
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = schema.toLower();
+
switch(d->pro) {
case QPSQLDriver::Version6:
stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname "
@@ -939,7 +946,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
"FROM pg_attribute, pg_class "
"WHERE %1 pg_class.oid IN "
"(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
- " (SELECT oid FROM pg_class WHERE lower(relname) = '%2')) "
+ " (SELECT oid FROM pg_class WHERE relname = '%2')) "
"AND pg_attribute.attrelid = pg_class.oid "
"AND pg_attribute.attisdropped = false "
"ORDER BY pg_attribute.attnum");
@@ -947,11 +954,11 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
else
stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema.toLower()));
+ "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema));
break;
}
- i.exec(stmt.arg(tbl.toLower()));
+ i.exec(stmt.arg(tbl));
while (i.isActive() && i.next()) {
QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()));
idx.append(f);
@@ -970,6 +977,16 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
QString schema;
qSplitTableName(tbl, schema);
+ if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
+ tbl = stripDelimiters(tbl, QSqlDriver::TableName);
+ else
+ tbl = tbl.toLower();
+
+ if (isIdentifierEscaped(schema, QSqlDriver::TableName))
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ else
+ schema = schema.toLower();
+
QString stmt;
switch(d->pro) {
case QPSQLDriver::Version6:
@@ -1014,7 +1031,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
"left join pg_attrdef on (pg_attrdef.adrelid = "
"pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
"where %1 "
- "and lower(pg_class.relname) = '%2' "
+ "and pg_class.relname = '%2' "
"and pg_attribute.attnum > 0 "
"and pg_attribute.attrelid = pg_class.oid "
"and pg_attribute.attisdropped = false "
@@ -1023,12 +1040,12 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
else
stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1')").arg(schema.toLower()));
+ "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
break;
}
QSqlQuery query(createResult());
- query.exec(stmt.arg(tbl.toLower()));
+ query.exec(stmt.arg(tbl));
if (d->pro >= QPSQLDriver::Version71) {
while (query.next()) {
int len = query.value(3).toInt();
@@ -1095,12 +1112,12 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
QTime tm = field.value().toDateTime().time();
// msecs need to be right aligned otherwise psql
// interpretes them wrong
- r = QLatin1String("'") + QString::number(dt.year()) + QLatin1String("-")
- + QString::number(dt.month()) + QLatin1String("-")
- + QString::number(dt.day()) + QLatin1String(" ")
- + tm.toString() + QLatin1String(".")
+ r = QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
+ + QString::number(dt.month()) + QLatin1Char('-')
+ + QString::number(dt.day()) + QLatin1Char(' ')
+ + tm.toString() + QLatin1Char('.')
+ QString::number(tm.msec()).rightJustified(3, QLatin1Char('0'))
- + QLatin1String("'");
+ + QLatin1Char('\'');
} else {
r = QLatin1String("NULL");
}
@@ -1155,7 +1172,7 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
QString QPSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
@@ -1188,7 +1205,7 @@ bool QPSQLDriver::subscribeToNotificationImplementation(const QString &name)
int socket = PQsocket(d->connection);
if (socket) {
- QString query = QString(QLatin1String("LISTEN %1")).arg(escapeIdentifier(name, QSqlDriver::TableName));
+ QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
if (PQresultStatus(PQexec(d->connection,
d->isUtf8 ? query.toUtf8().constData()
: query.toLocal8Bit().constData())
@@ -1220,7 +1237,7 @@ bool QPSQLDriver::unsubscribeFromNotificationImplementation(const QString &name)
return false;
}
- QString query = QString(QLatin1String("UNLISTEN %1")).arg(escapeIdentifier(name, QSqlDriver::TableName));
+ QString query = QLatin1String("UNLISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
if (PQresultStatus(PQexec(d->connection,
d->isUtf8 ? query.toUtf8().constData()
: query.toLocal8Bit().constData())
diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp
index 8a38f7d..8e1091b 100644
--- a/src/sql/drivers/sqlite/qsql_sqlite.cpp
+++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp
@@ -115,13 +115,12 @@ public:
uint skipRow: 1; // skip the next fetchNext()?
uint utf8: 1;
QSqlRecord rInf;
- QSql::NumericalPrecisionPolicy precisionPolicy;
};
static const uint initial_cache_size = 128;
QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0),
- stmt(0), skippedStatus(false), skipRow(false), utf8(false), precisionPolicy(QSql::HighPrecision)
+ stmt(0), skippedStatus(false), skipRow(false), utf8(false)
{
}
@@ -212,7 +211,7 @@ bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int i
values[i + idx] = sqlite3_column_int64(stmt, i);
break;
case SQLITE_FLOAT:
- switch(precisionPolicy) {
+ switch(q->numericalPrecisionPolicy()) {
case QSql::LowPrecisionInt32:
values[i + idx] = sqlite3_column_int(stmt, i);
break;
@@ -289,12 +288,8 @@ void QSQLiteResult::virtual_hook(int id, void *data)
if (d->stmt)
sqlite3_reset(d->stmt);
break;
- case QSqlResult::SetNumericalPrecision:
- Q_ASSERT(data);
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
- QSqlResult::virtual_hook(id, data);
+ QSqlCachedResult::virtual_hook(id, data);
}
}
@@ -481,11 +476,11 @@ bool QSQLiteDriver::hasFeature(DriverFeature f) const
case PositionalPlaceholders:
case SimpleLocking:
case FinishQuery:
+ case LowPrecisionNumbers:
return true;
case QuerySize:
case NamedPlaceholders:
case BatchOperations:
- case LowPrecisionNumbers:
case EventNotifications:
case MultipleResultSets:
return false;
@@ -631,9 +626,9 @@ static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool only
{
QString schema;
QString table(tableName);
- int indexOfSeparator = tableName.indexOf(QLatin1String("."));
+ int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
if (indexOfSeparator > -1) {
- schema = tableName.left(indexOfSeparator).append(QLatin1String("."));
+ schema = tableName.left(indexOfSeparator).append(QLatin1Char('.'));
table = tableName.mid(indexOfSeparator + 1);
}
q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info ('") + table + QLatin1String("')"));
@@ -661,9 +656,13 @@ QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
if (!isOpen())
return QSqlIndex();
+ QString table = tblname;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, tblname, true);
+ return qGetTableInfo(q, table, true);
}
QSqlRecord QSQLiteDriver::record(const QString &tbl) const
@@ -671,9 +670,13 @@ QSqlRecord QSQLiteDriver::record(const QString &tbl) const
if (!isOpen())
return QSqlRecord();
+ QString table = tbl;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+
QSqlQuery q(createResult());
q.setForwardOnly(true);
- return qGetTableInfo(q, tbl);
+ return qGetTableInfo(q, table);
}
QVariant QSQLiteDriver::handle() const
@@ -681,10 +684,10 @@ QVariant QSQLiteDriver::handle() const
return qVariantFromValue(d->access);
}
-QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType /*type*/) const
+QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !isIdentifierEscaped(identifier, type) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
diff --git a/src/sql/drivers/sqlite2/qsql_sqlite2.cpp b/src/sql/drivers/sqlite2/qsql_sqlite2.cpp
index 76bef08..1989c45 100644
--- a/src/sql/drivers/sqlite2/qsql_sqlite2.cpp
+++ b/src/sql/drivers/sqlite2/qsql_sqlite2.cpp
@@ -114,13 +114,12 @@ public:
uint skipRow: 1; // skip the next fetchNext()?
uint utf8: 1;
QSqlRecord rInf;
- QSql::NumericalPrecisionPolicy precisionPolicy;
};
static const uint initial_cache_size = 128;
QSQLite2ResultPrivate::QSQLite2ResultPrivate(QSQLite2Result* res) : q(res), access(0), currentTail(0),
- currentMachine(0), skippedStatus(false), skipRow(false), utf8(false), precisionPolicy(QSql::HighPrecision)
+ currentMachine(0), skippedStatus(false), skipRow(false), utf8(false)
{
}
@@ -167,7 +166,15 @@ void QSQLite2ResultPrivate::init(const char **cnames, int numCols)
for (int i = 0; i < numCols; ++i) {
const char* lastDot = strrchr(cnames[i], '.');
const char* fieldName = lastDot ? lastDot + 1 : cnames[i];
- rInf.append(QSqlField(QString::fromAscii(fieldName),
+
+ //remove quotations around the field name if any
+ QString fieldStr = QString::fromAscii(fieldName);
+ QLatin1Char quote('\"');
+ if ( fieldStr.length() > 2 && fieldStr.startsWith(quote) && fieldStr.endsWith(quote)) {
+ fieldStr = fieldStr.mid(1);
+ fieldStr.chop(1);
+ }
+ rInf.append(QSqlField(fieldStr,
nameToType(QString::fromAscii(cnames[i+numCols]))));
}
}
@@ -252,12 +259,8 @@ void QSQLite2Result::virtual_hook(int id, void *data)
case QSqlResult::DetachFromResultSet:
d->finalize();
break;
- case QSqlResult::SetNumericalPrecision:
- Q_ASSERT(data);
- d->precisionPolicy = *reinterpret_cast<QSql::NumericalPrecisionPolicy *>(data);
- break;
default:
- QSqlResult::virtual_hook(id, data);
+ QSqlCachedResult::virtual_hook(id, data);
}
}
@@ -503,8 +506,11 @@ QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const
QSqlQuery q(createResult());
q.setForwardOnly(true);
+ QString table = tblname;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
// finrst find a UNIQUE INDEX
- q.exec(QLatin1String("PRAGMA index_list('") + tblname + QLatin1String("');"));
+ q.exec(QLatin1String("PRAGMA index_list('") + table + QLatin1String("');"));
QString indexname;
while(q.next()) {
if (q.value(2).toInt()==1) {
@@ -517,7 +523,7 @@ QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const
q.exec(QLatin1String("PRAGMA index_info('") + indexname + QLatin1String("');"));
- QSqlIndex index(tblname, indexname);
+ QSqlIndex index(table, indexname);
while(q.next()) {
QString name = q.value(2).toString();
QVariant::Type type = QVariant::Invalid;
@@ -532,6 +538,9 @@ QSqlRecord QSQLite2Driver::record(const QString &tbl) const
{
if (!isOpen())
return QSqlRecord();
+ QString table = tbl;
+ if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
QSqlQuery q(createResult());
q.setForwardOnly(true);
@@ -547,7 +556,7 @@ QVariant QSQLite2Driver::handle() const
QString QSQLite2Driver::escapeIdentifier(const QString &identifier, IdentifierType /*type*/) const
{
QString res = identifier;
- if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) {
+ if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
diff --git a/src/sql/drivers/tds/qsql_tds.cpp b/src/sql/drivers/tds/qsql_tds.cpp
index d0f440c..e100863 100644
--- a/src/sql/drivers/tds/qsql_tds.cpp
+++ b/src/sql/drivers/tds/qsql_tds.cpp
@@ -181,7 +181,7 @@ static int CS_PUBLIC qTdsMsgHandler (DBPROCESS* dbproc,
}
if (severity > 0) {
- QString errMsg = QString(QLatin1String("%1 (%2)")).arg(QString::fromAscii(msgtext)).arg(
+ QString errMsg = QString::fromLatin1("%1 (%2)").arg(QString::fromAscii(msgtext)).arg(
msgstate);
p->addErrorMsg(errMsg);
}
@@ -211,8 +211,8 @@ static int CS_PUBLIC qTdsErrHandler(DBPROCESS* dbproc,
}
- QString errMsg = QString(QLatin1String("%1 %2\n")).arg(QString::fromAscii(dberrstr)).arg(
- QString::fromAscii(oserrstr));
+ QString errMsg = QString::fromLatin1("%1 %2\n").arg(QLatin1String(dberrstr)).arg(
+ QLatin1String(oserrstr));
errMsg += p->getErrorMsgs();
p->lastError = qMakeError(errMsg, QSqlError::UnknownError, dberr);
p->clearErrorMsgs();
@@ -293,6 +293,8 @@ QTDSResult::QTDSResult(const QTDSDriver* db)
// insert d in error handler dict
errs()->insert(d->dbproc, d);
+ dbcmd(d->dbproc, "set quoted_identifier on");
+ dbsqlexec(d->dbproc);
}
QTDSResult::~QTDSResult()
@@ -367,7 +369,7 @@ bool QTDSResult::gotoNext(QSqlCachedResult::ValueCache &values, int index)
if (qIsNull(d->buffer.at(i * 2 + 1)))
values[idx] = QVariant(QVariant::String);
else
- values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i * 2));
+ values[idx] = QString::fromLocal8Bit((const char*)d->buffer.at(i * 2)).trimmed();
break;
case QVariant::ByteArray: {
if (qIsNull(d->buffer.at(i * 2 + 1)))
@@ -698,9 +700,14 @@ QSqlRecord QTDSDriver::record(const QString& tablename) const
return info;
QSqlQuery t(createResult());
t.setForwardOnly(true);
+
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+
QString stmt (QLatin1String("select name, type, length, prec from syscolumns "
"where id = (select id from sysobjects where name = '%1')"));
- t.exec(stmt.arg(tablename));
+ t.exec(stmt.arg(table));
while (t.next()) {
QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()));
f.setLength(t.value(2).toInt());
@@ -770,13 +777,17 @@ QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
{
QSqlRecord rec = record(tablename);
- QSqlIndex idx(tablename);
- if ((!isOpen()) || (tablename.isEmpty()))
+ QString table = tablename;
+ if (isIdentifierEscaped(table, QSqlDriver::TableName))
+ table = stripDelimiters(table, QSqlDriver::TableName);
+
+ QSqlIndex idx(table);
+ if ((!isOpen()) || (table.isEmpty()))
return QSqlIndex();
QSqlQuery t(createResult());
t.setForwardOnly(true);
- t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(tablename));
+ t.exec(QString::fromLatin1("sp_helpindex '%1'").arg(table));
if (t.next()) {
QStringList fNames = t.value(2).toString().simplified().split(QLatin1Char(','));
QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
diff --git a/src/sql/kernel/qsqlcachedresult.cpp b/src/sql/kernel/qsqlcachedresult.cpp
index 3cace06..ca51dc0 100644
--- a/src/sql/kernel/qsqlcachedresult.cpp
+++ b/src/sql/kernel/qsqlcachedresult.cpp
@@ -294,4 +294,17 @@ QSqlCachedResult::ValueCache &QSqlCachedResult::cache()
return d->cache;
}
+void QSqlCachedResult::virtual_hook(int id, void *data)
+{
+ switch (id) {
+ case QSqlResult::DetachFromResultSet:
+ case QSqlResult::SetNumericalPrecision:
+ cleanup();
+ break;
+ default:
+ QSqlResult::virtual_hook(id, data);
+ }
+}
+
+
QT_END_NAMESPACE
diff --git a/src/sql/kernel/qsqlcachedresult_p.h b/src/sql/kernel/qsqlcachedresult_p.h
index d19435c..a384b2e 100644
--- a/src/sql/kernel/qsqlcachedresult_p.h
+++ b/src/sql/kernel/qsqlcachedresult_p.h
@@ -89,6 +89,7 @@ protected:
int colCount() const;
ValueCache &cache();
+ void virtual_hook(int id, void *data);
private:
bool cacheNext();
QSqlCachedResultPrivate *d;
diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp
index 848f213..036f84d 100644
--- a/src/sql/kernel/qsqldatabase.cpp
+++ b/src/sql/kernel/qsqldatabase.cpp
@@ -129,11 +129,16 @@ Q_GLOBAL_STATIC(QConnectionDict, dbDict)
class QSqlDatabasePrivate
{
public:
- QSqlDatabasePrivate(QSqlDriver *dr = 0):
+ QSqlDatabasePrivate(QSqlDatabase *d, QSqlDriver *dr = 0):
+ q(d),
driver(dr),
port(-1)
{
ref = 1;
+ if(driver)
+ precisionPolicy = driver->numericalPrecisionPolicy();
+ else
+ precisionPolicy= QSql::LowPrecisionDouble;
}
QSqlDatabasePrivate(const QSqlDatabasePrivate &other);
~QSqlDatabasePrivate();
@@ -142,6 +147,7 @@ public:
void disable();
QAtomicInt ref;
+ QSqlDatabase *q;
QSqlDriver* driver;
QString dbname;
QString uname;
@@ -151,6 +157,7 @@ public:
int port;
QString connOptions;
QString connName;
+ QSql::NumericalPrecisionPolicy precisionPolicy;
static QSqlDatabasePrivate *shared_null();
static QSqlDatabase database(const QString& name, bool open);
@@ -164,6 +171,7 @@ public:
QSqlDatabasePrivate::QSqlDatabasePrivate(const QSqlDatabasePrivate &other)
{
ref = 1;
+ q = other.q;
dbname = other.dbname;
uname = other.uname;
pword = other.pword;
@@ -172,6 +180,7 @@ QSqlDatabasePrivate::QSqlDatabasePrivate(const QSqlDatabasePrivate &other)
port = other.port;
connOptions = other.connOptions;
driver = other.driver;
+ precisionPolicy = other.precisionPolicy;
}
QSqlDatabasePrivate::~QSqlDatabasePrivate()
@@ -216,7 +225,7 @@ DriverDict &QSqlDatabasePrivate::driverDict()
QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null()
{
static QSqlNullDriver dr;
- static QSqlDatabasePrivate n(&dr);
+ static QSqlDatabasePrivate n(NULL, &dr);
return &n;
}
@@ -281,6 +290,7 @@ QSqlDatabase QSqlDatabasePrivate::database(const QString& name, bool open)
*/
void QSqlDatabasePrivate::copy(const QSqlDatabasePrivate *other)
{
+ q = other->q;
dbname = other->dbname;
uname = other->uname;
pword = other->pword;
@@ -288,6 +298,7 @@ void QSqlDatabasePrivate::copy(const QSqlDatabasePrivate *other)
drvName = other->drvName;
port = other->port;
connOptions = other->connOptions;
+ precisionPolicy = other->precisionPolicy;
}
void QSqlDatabasePrivate::disable()
@@ -658,7 +669,7 @@ QStringList QSqlDatabase::connectionNames()
QSqlDatabase::QSqlDatabase(const QString &type)
{
- d = new QSqlDatabasePrivate();
+ d = new QSqlDatabasePrivate(this);
d->init(type);
}
@@ -670,7 +681,7 @@ QSqlDatabase::QSqlDatabase(const QString &type)
QSqlDatabase::QSqlDatabase(QSqlDriver *driver)
{
- d = new QSqlDatabasePrivate(driver);
+ d = new QSqlDatabasePrivate(this, driver);
}
/*!
@@ -1469,6 +1480,41 @@ QString QSqlDatabase::connectionName() const
return d->connName;
}
+/*!
+ Sets the default numerical precision policy used by queries created
+ on this database connection to \a precisionPolicy.
+
+ Note: Drivers that don't support fetching numerical values with low
+ precision will ignore the precision policy. You can use
+ QSqlDriver::hasFeature() to find out whether a driver supports this
+ feature.
+
+ Note: Setting the default precision policy to \a precisionPolicy
+ doesn't affect any currently active queries.
+
+ \sa QSql::NumericalPrecisionPolicy, numericalPrecisionPolicy(), QSqlQuery::setNumericalPrecisionPolicy(), QSqlQuery::numericalPrecisionPolicy()
+*/
+void QSqlDatabase::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy)
+{
+ if(driver())
+ driver()->setNumericalPrecisionPolicy(precisionPolicy);
+ d->precisionPolicy = precisionPolicy;
+}
+
+/*!
+ Returns the current default precision policy for the database connection.
+
+ \sa QSql::NumericalPrecisionPolicy, setNumericalPrecisionPolicy(), QSqlQuery::numericalPrecisionPolicy(), QSqlQuery::setNumericalPrecisionPolicy()
+*/
+QSql::NumericalPrecisionPolicy QSqlDatabase::numericalPrecisionPolicy() const
+{
+ if(driver())
+ return driver()->numericalPrecisionPolicy();
+ else
+ return d->precisionPolicy;
+}
+
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QSqlDatabase &d)
{
diff --git a/src/sql/kernel/qsqldatabase.h b/src/sql/kernel/qsqldatabase.h
index 74e6315..d28c888 100644
--- a/src/sql/kernel/qsqldatabase.h
+++ b/src/sql/kernel/qsqldatabase.h
@@ -120,6 +120,8 @@ public:
int port() const;
QString connectOptions() const;
QString connectionName() const;
+ void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy);
+ QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const;
QSqlDriver* driver() const;
diff --git a/src/sql/kernel/qsqldriver.cpp b/src/sql/kernel/qsqldriver.cpp
index 135ec306..77e389f 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:
@@ -61,10 +72,11 @@ public:
uint isOpen : 1;
uint isOpenError : 1;
QSqlError error;
+ QSql::NumericalPrecisionPolicy precisionPolicy;
};
inline QSqlDriverPrivate::QSqlDriverPrivate()
- : QObjectPrivate(), isOpen(false), isOpenError(false)
+ : QObjectPrivate(), isOpen(false), isOpenError(false), precisionPolicy(QSql::LowPrecisionDouble)
{
}
@@ -372,6 +384,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 +392,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 +459,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 +478,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 +493,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,17 +511,17 @@ 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("?"));
+ vals.append(QLatin1Char('?'));
else
vals.append(formatValue(rec.field(i)));
vals.append(QLatin1String(", "));
@@ -469,7 +531,7 @@ QString QSqlDriver::sqlStatement(StatementType type, const QString &tableName,
} else {
vals.chop(2); // remove trailing comma
s[s.length() - 2] = QLatin1Char(')');
- s.append(QLatin1String("VALUES (")).append(vals).append(QLatin1String(")"));
+ s.append(QLatin1String("VALUES (")).append(vals).append(QLatin1Char(')'));
}
break; }
}
@@ -564,10 +626,7 @@ QString QSqlDriver::formatValue(const QSqlField &field, bool trimStrings) const
break;
}
case QVariant::Bool:
- if (field.value().toBool())
- r = QLatin1String("1");
- else
- r = QLatin1String("0");
+ r = QString::number(field.value().toBool());
break;
case QVariant::ByteArray : {
if (hasFeature(BLOB)) {
@@ -805,4 +864,77 @@ 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);
+ return identifier.size() > 2
+ && identifier.startsWith(QLatin1Char('"')) //left delimited
+ && identifier.endsWith(QLatin1Char('"')); //right delimited
+}
+
+/*!
+ 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;
+}
+
+/*!
+ Sets the default numerical precision policy used by queries created
+ by this driver to \a precisionPolicy.
+
+ Note: Setting the default precision policy to \a precisionPolicy
+ doesn't affect any currently active queries.
+
+ \sa QSql::NumericalPrecisionPolicy, numericalPrecisionPolicy(), QSqlQuery::setNumericalPrecisionPolicy(), QSqlQuery::numericalPrecisionPolicy()
+*/
+void QSqlDriver::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy)
+{
+ d_func()->precisionPolicy = precisionPolicy;
+}
+
+/*!
+ Returns the current default precision policy for the database connection.
+
+ \sa QSql::NumericalPrecisionPolicy, setNumericalPrecisionPolicy(), QSqlQuery::numericalPrecisionPolicy(), QSqlQuery::setNumericalPrecisionPolicy()
+*/
+QSql::NumericalPrecisionPolicy QSqlDriver::numericalPrecisionPolicy() const
+{
+ return d_func()->precisionPolicy;
+}
+
QT_END_NAMESPACE
diff --git a/src/sql/kernel/qsqldriver.h b/src/sql/kernel/qsqldriver.h
index 9e3d291..3052be6 100644
--- a/src/sql/kernel/qsqldriver.h
+++ b/src/sql/kernel/qsqldriver.h
@@ -127,6 +127,12 @@ 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
+
+ void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy);
+ QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const;
+
Q_SIGNALS:
void notification(const QString &name);
@@ -140,6 +146,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)
};
diff --git a/src/sql/kernel/qsqlerror.cpp b/src/sql/kernel/qsqlerror.cpp
index b59e7bc..bb2e7a1 100644
--- a/src/sql/kernel/qsqlerror.cpp
+++ b/src/sql/kernel/qsqlerror.cpp
@@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE
QDebug operator<<(QDebug dbg, const QSqlError &s)
{
dbg.nospace() << "QSqlError(" << s.number() << ", " << s.driverText() <<
- ", " << s.databaseText() << ")";
+ ", " << s.databaseText() << ')';
return dbg.space();
}
#endif
diff --git a/src/sql/kernel/qsqlfield.cpp b/src/sql/kernel/qsqlfield.cpp
index f4124b2..b528850 100644
--- a/src/sql/kernel/qsqlfield.cpp
+++ b/src/sql/kernel/qsqlfield.cpp
@@ -514,8 +514,8 @@ QDebug operator<<(QDebug dbg, const QSqlField &f)
if (f.typeID() >= 0)
dbg.nospace() << ", typeID: " << f.typeID();
if (!f.defaultValue().isNull())
- dbg.nospace() << ", auto-value: \"" << f.defaultValue() << "\"";
- dbg.nospace() << ")";
+ dbg.nospace() << ", auto-value: \"" << f.defaultValue() << '\"';
+ dbg.nospace() << ')';
return dbg.space();
#else
qWarning("This compiler doesn't support streaming QSqlField to QDebug");
diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp
index 778fd28..f55b86e 100644
--- a/src/sql/kernel/qsqlquery.cpp
+++ b/src/sql/kernel/qsqlquery.cpp
@@ -61,7 +61,6 @@ public:
~QSqlQueryPrivate();
QAtomicInt ref;
QSqlResult* sqlResult;
- QSql::NumericalPrecisionPolicy precisionPolicy;
static QSqlQueryPrivate* shared_null();
};
@@ -81,7 +80,7 @@ QSqlQueryPrivate* QSqlQueryPrivate::shared_null()
\internal
*/
QSqlQueryPrivate::QSqlQueryPrivate(QSqlResult* result)
- : ref(1), sqlResult(result), precisionPolicy(QSql::HighPrecision)
+ : ref(1), sqlResult(result)
{
if (!sqlResult)
sqlResult = nullResult();
@@ -351,14 +350,14 @@ bool QSqlQuery::exec(const QString& query)
if (d->ref != 1) {
bool fo = isForwardOnly();
*this = QSqlQuery(driver()->createResult());
- d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy);
+ d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy());
setForwardOnly(fo);
} else {
d->sqlResult->clear();
d->sqlResult->setActive(false);
d->sqlResult->setLastError(QSqlError());
d->sqlResult->setAt(QSql::BeforeFirstRow);
- d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy);
+ d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy());
}
d->sqlResult->setQuery(query.trimmed());
if (!driver()->isOpen() || driver()->isOpenError()) {
@@ -891,12 +890,12 @@ bool QSqlQuery::prepare(const QString& query)
bool fo = isForwardOnly();
*this = QSqlQuery(driver()->createResult());
setForwardOnly(fo);
- d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy);
+ d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy());
} else {
d->sqlResult->setActive(false);
d->sqlResult->setLastError(QSqlError());
d->sqlResult->setAt(QSql::BeforeFirstRow);
- d->sqlResult->setNumericalPrecisionPolicy(d->precisionPolicy);
+ d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy());
}
if (!driver()) {
qWarning("QSqlQuery::prepare: no driver");
@@ -1126,10 +1125,10 @@ QVariant QSqlQuery::lastInsertId() const
Instruct the database driver to return numerical values with a
precision specified by \a precisionPolicy.
- The Oracle driver, for example, retrieves numerical values as
- strings by default to prevent the loss of precision. If the high
- precision doesn't matter, use this method to increase execution
- speed by bypassing string conversions.
+ The Oracle driver, for example, can retrieve numerical values as
+ strings to prevent the loss of precision. If high precision doesn't
+ matter, use this method to increase execution speed by bypassing
+ string conversions.
Note: Drivers that don't support fetching numerical values with low
precision will ignore the precision policy. You can use
@@ -1144,7 +1143,7 @@ QVariant QSqlQuery::lastInsertId() const
*/
void QSqlQuery::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy precisionPolicy)
{
- d->precisionPolicy = precisionPolicy;
+ d->sqlResult->setNumericalPrecisionPolicy(precisionPolicy);
}
/*!
@@ -1154,7 +1153,7 @@ void QSqlQuery::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy preci
*/
QSql::NumericalPrecisionPolicy QSqlQuery::numericalPrecisionPolicy() const
{
- return d->precisionPolicy;
+ return d->sqlResult->numericalPrecisionPolicy();
}
/*!
diff --git a/src/sql/kernel/qsqlrecord.cpp b/src/sql/kernel/qsqlrecord.cpp
index e599d37..64e52be 100644
--- a/src/sql/kernel/qsqlrecord.cpp
+++ b/src/sql/kernel/qsqlrecord.cpp
@@ -589,7 +589,7 @@ void QSqlRecord::detach()
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QSqlRecord &r)
{
- dbg << "QSqlRecord(" << r.count() << ")";
+ dbg << "QSqlRecord(" << r.count() << ')';
for (int i = 0; i < r.count(); ++i)
dbg << '\n' << QString::fromLatin1("%1:").arg(i, 2) << r.field(i) << r.value(i).toString();
return dbg;
diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp
index 932bcf1..93c9d9f 100644
--- a/src/sql/kernel/qsqlresult.cpp
+++ b/src/sql/kernel/qsqlresult.cpp
@@ -48,6 +48,7 @@
#include "qsqlresult.h"
#include "qvector.h"
#include "qsqldriver.h"
+#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -64,7 +65,7 @@ class QSqlResultPrivate
public:
QSqlResultPrivate(QSqlResult* d)
: q(d), sqldriver(0), idx(QSql::BeforeFirstRow), active(false),
- isSel(false), forwardOnly(false), bindCount(0), binds(QSqlResult::PositionalBinding)
+ isSel(false), forwardOnly(false), precisionPolicy(QSql::LowPrecisionDouble), bindCount(0), binds(QSqlResult::PositionalBinding)
{}
void clearValues()
@@ -104,6 +105,7 @@ public:
bool isSel;
QSqlError error;
bool forwardOnly;
+ QSql::NumericalPrecisionPolicy precisionPolicy;
int bindCount;
QSqlResult::BindingSyntax binds;
@@ -249,6 +251,9 @@ QSqlResult::QSqlResult(const QSqlDriver *db)
{
d = new QSqlResultPrivate(this);
d->sqldriver = db;
+ if(db) {
+ setNumericalPrecisionPolicy(db->numericalPrecisionPolicy());
+ }
}
/*!
@@ -900,7 +905,6 @@ QVariant QSqlResult::lastInsertId() const
*/
void QSqlResult::virtual_hook(int, void *)
{
- Q_ASSERT(false);
}
/*! \internal
@@ -965,8 +969,15 @@ void QSqlResult::detachFromResultSet()
*/
void QSqlResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
{
- if (driver()->hasFeature(QSqlDriver::LowPrecisionNumbers))
- virtual_hook(SetNumericalPrecision, &policy);
+ d->precisionPolicy = policy;
+ virtual_hook(SetNumericalPrecision, &policy);
+}
+
+/*! \internal
+ */
+QSql::NumericalPrecisionPolicy QSqlResult::numericalPrecisionPolicy() const
+{
+ return d->precisionPolicy;
}
/*! \internal
diff --git a/src/sql/kernel/qsqlresult.h b/src/sql/kernel/qsqlresult.h
index 94e7f0e..6d8f42e 100644
--- a/src/sql/kernel/qsqlresult.h
+++ b/src/sql/kernel/qsqlresult.h
@@ -135,6 +135,7 @@ protected:
bool execBatch(bool arrayBind = false);
void detachFromResultSet();
void setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy);
+ QSql::NumericalPrecisionPolicy numericalPrecisionPolicy() const;
bool nextResult();
private:
diff --git a/src/sql/models/qsqlrelationaltablemodel.cpp b/src/sql/models/qsqlrelationaltablemodel.cpp
index 93d1733..d261b82 100644
--- a/src/sql/models/qsqlrelationaltablemodel.cpp
+++ b/src/sql/models/qsqlrelationaltablemodel.cpp
@@ -182,10 +182,21 @@ void QRelation::populateDictionary()
populateModel();
QSqlRecord record;
+ QString indexColumn;
+ QString displayColumn;
for (int i=0; i < model->rowCount(); ++i) {
record = model->record(i);
- dictionary[record.field(rel.indexColumn()).value().toString()] =
- record.field(rel.displayColumn()).value();
+
+ indexColumn = rel.indexColumn();
+ if (m_parent->database().driver()->isIdentifierEscaped(indexColumn, QSqlDriver::FieldName))
+ indexColumn = m_parent->database().driver()->stripDelimiters(indexColumn, QSqlDriver::FieldName);
+
+ displayColumn = rel.displayColumn();
+ if (m_parent->database().driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
+ displayColumn = m_parent->database().driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
+
+ dictionary[record.field(indexColumn).value().toString()] =
+ record.field(displayColumn).value();
}
m_dictInitialized = true;
}
@@ -215,7 +226,7 @@ public:
QSqlRelationalTableModelPrivate()
: QSqlTableModelPrivate()
{}
- QString escapedRelationField(const QString &tableName, const QString &fieldName) const;
+ QString relationField(const QString &tableName, const QString &fieldName) const;
int nameToIndex(const QString &name) const;
mutable QVector<QRelation> relations;
@@ -255,7 +266,10 @@ void QSqlRelationalTableModelPrivate::revertCachedRow(int row)
int QSqlRelationalTableModelPrivate::nameToIndex(const QString &name) const
{
- return baseRec.indexOf(name);
+ QString fieldname = name;
+ if (db.driver()->isIdentifierEscaped(fieldname, QSqlDriver::FieldName))
+ fieldname = db.driver()->stripDelimiters(fieldname, QSqlDriver::FieldName);
+ return baseRec.indexOf(fieldname);
}
void QSqlRelationalTableModelPrivate::clearEditBuffer()
@@ -481,14 +495,14 @@ QSqlRelation QSqlRelationalTableModel::relation(int column) const
return d->relations.value(column).rel;
}
-QString QSqlRelationalTableModelPrivate::escapedRelationField(const QString &tableName,
+QString QSqlRelationalTableModelPrivate::relationField(const QString &tableName,
const QString &fieldName) const
{
- QString esc;
- esc.reserve(tableName.size() + fieldName.size() + 1);
- esc.append(tableName).append(QLatin1Char('.')).append(fieldName);
+ QString ret;
+ ret.reserve(tableName.size() + fieldName.size() + 1);
+ ret.append(tableName).append(QLatin1Char('.')).append(fieldName);
- return db.driver()->escapeIdentifier(esc, QSqlDriver::FieldName);
+ return ret;
}
/*!
@@ -514,15 +528,29 @@ QString QSqlRelationalTableModel::selectStatement() const
// Count how many times each field name occurs in the record
QHash<QString, int> fieldNames;
+ QStringList fieldList;
for (int i = 0; i < rec.count(); ++i) {
QSqlRelation relation = d->relations.value(i, nullRelation).rel;
QString name;
if (relation.isValid())
+ {
// Count the display column name, not the original foreign key
name = relation.displayColumn();
+ if (d->db.driver()->isIdentifierEscaped(name, QSqlDriver::FieldName))
+ name = d->db.driver()->stripDelimiters(name, QSqlDriver::FieldName);
+
+ QSqlRecord rec = database().record(relation.tableName());
+ for (int i = 0; i < rec.count(); ++i) {
+ if (name.compare(rec.fieldName(i), Qt::CaseInsensitive) == 0) {
+ name = rec.fieldName(i);
+ break;
+ }
+ }
+ }
else
name = rec.fieldName(i);
fieldNames.insert(name, fieldNames.value(name, 0) + 1);
+ fieldList.append(name);
}
for (int i = 0; i < rec.count(); ++i) {
@@ -531,27 +559,30 @@ QString QSqlRelationalTableModel::selectStatement() const
QString relTableAlias = QString::fromLatin1("relTblAl_%1").arg(i);
if (!fList.isEmpty())
fList.append(QLatin1String(", "));
- fList.append(d->escapedRelationField(relTableAlias, relation.displayColumn()));
+ fList.append(d->relationField(relTableAlias,relation.displayColumn()));
// If there are duplicate field names they must be aliased
- if (fieldNames.value(relation.displayColumn()) > 1) {
- fList.append(QString::fromLatin1(" AS %1_%2").arg(relation.tableName()).arg(relation.displayColumn()));
+ if (fieldNames.value(fieldList[i]) > 1) {
+ QString relTableName = relation.tableName();
+ if (d->db.driver()->isIdentifierEscaped(relTableName, QSqlDriver::TableName))
+ relTableName = d->db.driver()->stripDelimiters(relTableName, QSqlDriver::TableName);
+ QString displayColumn = relation.displayColumn();
+ if (d->db.driver()->isIdentifierEscaped(displayColumn, QSqlDriver::FieldName))
+ displayColumn = d->db.driver()->stripDelimiters(displayColumn, QSqlDriver::FieldName);
+ fList.append(QString::fromLatin1(" AS %1_%2").arg(relTableName).arg(displayColumn));
}
// this needs fixing!! the below if is borken.
- if (!tables.contains(relation.tableName()))
- tables.append(d->db.driver()->escapeIdentifier(relation.tableName(),QSqlDriver::TableName)
- .append(QLatin1String(" "))
- .append(d->db.driver()->escapeIdentifier(relTableAlias, QSqlDriver::TableName)));
+ tables.append(relation.tableName().append(QLatin1Char(' ')).append(relTableAlias));
if(!where.isEmpty())
where.append(QLatin1String(" AND "));
- where.append(d->escapedRelationField(tableName(), rec.fieldName(i)));
+ where.append(d->relationField(tableName(), d->db.driver()->escapeIdentifier(rec.fieldName(i), QSqlDriver::FieldName)));
where.append(QLatin1String(" = "));
- where.append(d->escapedRelationField(relTableAlias, relation.indexColumn()));
+ where.append(d->relationField(relTableAlias, relation.indexColumn()));
} else {
if (!fList.isEmpty())
fList.append(QLatin1String(", "));
- fList.append(d->escapedRelationField(tableName(), rec.fieldName(i)));
+ fList.append(d->relationField(tableName(), d->db.driver()->escapeIdentifier(rec.fieldName(i), QSqlDriver::FieldName)));
}
}
if (!tables.isEmpty())
@@ -560,7 +591,7 @@ QString QSqlRelationalTableModel::selectStatement() const
return query;
if(!tList.isEmpty())
tList.prepend(QLatin1String(", "));
- tList.prepend(d->db.driver()->escapeIdentifier(tableName(),QSqlDriver::TableName));
+ tList.prepend(tableName());
query.append(QLatin1String("SELECT "));
query.append(fList).append(QLatin1String(" FROM ")).append(tList);
qAppendWhereClause(query, where, filter());
@@ -690,7 +721,7 @@ QString QSqlRelationalTableModel::orderByClause() const
return QSqlTableModel::orderByClause();
QString s = QLatin1String("ORDER BY ");
- s.append(d->escapedRelationField(QLatin1String("relTblAl_") + QString::number(d->sortColumn),
+ s.append(d->relationField(QLatin1String("relTblAl_") + QString::number(d->sortColumn),
rel.displayColumn()));
s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC");
return s;
diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp
index 1acc846..156af26 100644
--- a/src/sql/models/qsqltablemodel.cpp
+++ b/src/sql/models/qsqltablemodel.cpp
@@ -98,7 +98,10 @@ bool QSqlTableModelPrivate::setRecord(int row, const QSqlRecord &record)
int QSqlTableModelPrivate::nameToIndex(const QString &name) const
{
- return rec.indexOf(name);
+ QString fieldname = name;
+ if (db.driver()->isIdentifierEscaped(fieldname, QSqlDriver::FieldName))
+ fieldname = db.driver()->stripDelimiters(fieldname, QSqlDriver::FieldName);
+ return rec.indexOf(fieldname);
}
void QSqlTableModelPrivate::initRecordAndPrimaryIndex()
@@ -367,10 +370,7 @@ void QSqlTableModel::setTable(const QString &tableName)
{
Q_D(QSqlTableModel);
clear();
- if(d->db.tables().contains(tableName.toUpper()))
- d->tableName = tableName.toUpper();
- else
- d->tableName = tableName;
+ d->tableName = tableName;
d->initRecordAndPrimaryIndex();
d->initColOffsets(d->rec.count());
@@ -976,7 +976,9 @@ QString QSqlTableModel::orderByClause() const
if (!f.isValid())
return s;
- QString table = d->db.driver()->escapeIdentifier(d->tableName, QSqlDriver::TableName);
+ QString table = d->tableName;
+ //we can safely escape the field because it would have been obtained from the database
+ //and have the correct case
QString field = d->db.driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName);
s.append(QLatin1String("ORDER BY ")).append(table).append(QLatin1Char('.')).append(field);
s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC");
@@ -1317,8 +1319,12 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &record)
mrow.rec = d->rec;
mrow.primaryValues = d->primaryValues(indexInQuery(createIndex(row, 0)).row());
}
+ QString fieldName;
for (int i = 0; i < record.count(); ++i) {
- int idx = mrow.rec.indexOf(record.fieldName(i));
+ fieldName = record.fieldName(i);
+ if (d->db.driver()->isIdentifierEscaped(fieldName, QSqlDriver::FieldName))
+ fieldName = d->db.driver()->stripDelimiters(fieldName, QSqlDriver::FieldName);
+ int idx = mrow.rec.indexOf(fieldName);
if (idx == -1)
isOk = false;
else