diff options
Diffstat (limited to 'src/sql')
25 files changed, 952 insertions, 420 deletions
diff --git a/src/sql/drivers/db2/qsql_db2.cpp b/src/sql/drivers/db2/qsql_db2.cpp index 3009eed..e4f564a 100644 --- a/src/sql/drivers/db2/qsql_db2.cpp +++ b/src/sql/drivers/db2/qsql_db2.cpp @@ -51,10 +51,6 @@ #include <qvector.h> #include <QDebug> -#ifndef UNICODE -#define UNICODE -#endif - #if defined(Q_CC_BOR) // DB2's sqlsystm.h (included through sqlcli1.h) defines the SQL_BIGINT_TYPE // and SQL_BIGUINT_TYPE to wrong the types for Borland; so do the defines to @@ -84,7 +80,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,27 +103,18 @@ public: SQLHANDLE hStmt; QSqlRecord recInf; QVector<QVariant*> valueCache; - QSql::NumericalPrecisionPolicy precisionPolicy; }; static QString qFromTChar(SQLTCHAR* str) { -#ifdef UNICODE return QString::fromUtf16(str); -#else - return QString::fromLocal8Bit((const char*) str); -#endif } // dangerous!! (but fast). Don't use in functions that // require out parameters! static SQLTCHAR* qToTChar(const QString& str) { -#ifdef UNICODE return (SQLTCHAR*)str.utf16(); -#else - return (unsigned char*) str.ascii(); -#endif } static QString qWarnDB2Handle(int handleType, SQLHANDLE handle) @@ -348,12 +335,8 @@ static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool& is while (true) { r = SQLGetData(hStmt, - column+1, -#ifdef UNICODE + column + 1, SQL_C_WCHAR, -#else - SQL_C_CHAR, -#endif (SQLPOINTER)buf, colSize * sizeof(SQLTCHAR), &lengthIndicator); @@ -741,7 +724,6 @@ bool QDB2Result::exec() ind); break; } case QVariant::String: -#ifdef UNICODE { QString str(values.at(i).toString()); if (*ind != SQL_NULL_DATA) @@ -775,8 +757,6 @@ bool QDB2Result::exec() } break; } -#endif - // fall through default: { QByteArray ba = values.at(i).toString().toAscii(); int len = ba.length() + 1; @@ -850,12 +830,9 @@ bool QDB2Result::exec() case QVariant::ByteArray: break; case QVariant::String: -#ifdef UNICODE if (bindValueType(i) & QSql::Out) values[i] = QString::fromUtf16((ushort*)tmpStorage.takeFirst().constData()); break; -#endif - // fall through default: { values[i] = QString::fromAscii(tmpStorage.takeFirst().constData()); break; } @@ -1044,26 +1021,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 +1120,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 +1155,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 +1178,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 +1210,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 +1222,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 +1252,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 +1297,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 +1329,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 +1412,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 +1449,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 +1509,6 @@ bool QDB2Driver::hasFeature(DriverFeature f) const case BatchOperations: case LastInsertId: case SimpleLocking: - case LowPrecisionNumbers: case EventNotifications: return false; case BLOB: @@ -1490,15 +1516,11 @@ 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: - // select typename, codepage from syscat.datatypes -#ifdef UNICODE return true; -#else - return false; -#endif } return false; } @@ -1612,7 +1634,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 8d34dd8..979eeec 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,12 +1578,12 @@ 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; int r = OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64), @@ -1595,7 +1593,7 @@ void QOCICols::getValues(QVector<QVariant> &v, int index) else v[index + i] = QVariant(); 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; @@ -1621,8 +1619,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), @@ -1904,11 +1901,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); } } @@ -2043,8 +2037,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) @@ -2111,7 +2105,7 @@ bool QOCIDriver::open(const QString & db, setOpen(true); setOpenError(false); - d->user = user.toUpper(); + d->user = user; return true; } @@ -2213,9 +2207,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()); } @@ -2231,7 +2232,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()); } @@ -2250,10 +2251,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; } } @@ -2269,7 +2270,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 @@ -2277,11 +2278,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 @@ -2298,7 +2311,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()); @@ -2330,11 +2343,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); @@ -2358,7 +2383,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(); } @@ -2428,13 +2453,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 4f358ec..50defdf 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)); } @@ -430,6 +433,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; @@ -539,10 +562,33 @@ static int qGetODBCVersion(const QString &connOpts) #ifndef Q_ODBC_VERSION_2 if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive)) return SQL_OV_ODBC3; -#endif +#endif 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 @@ -693,6 +739,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) @@ -867,7 +972,7 @@ bool QODBCResult::fetchFirst() r = SQLFetchScroll(d->hStmt, SQL_FETCH_FIRST, 0); - if (r != SQL_SUCCESS) { + if (r != SQL_SUCCESS) { if (r != SQL_NO_DATA) setLastError(qMakeError(QCoreApplication::translate("QODBCResult", "Unable to fetch first"), QSqlError::ConnectionError, d)); @@ -886,7 +991,7 @@ bool QODBCResult::fetchPrevious() r = SQLFetchScroll(d->hStmt, SQL_FETCH_PRIOR, 0); - if (r != SQL_SUCCESS) { + if (r != SQL_SUCCESS) { if (r != SQL_NO_DATA) setLastError(qMakeError(QCoreApplication::translate("QODBCResult", "Unable to fetch previous"), QSqlError::ConnectionError, d)); @@ -917,7 +1022,7 @@ bool QODBCResult::fetchLast() r = SQLFetchScroll(d->hStmt, SQL_FETCH_LAST, 0); - if (r != SQL_SUCCESS) { + if (r != SQL_SUCCESS) { if (r != SQL_NO_DATA) setLastError(qMakeError(QCoreApplication::translate("QODBCResult", "Unable to fetch last"), QSqlError::ConnectionError, d)); @@ -1011,29 +1116,21 @@ QVariant QODBCResult::data(int field) d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode); 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; @@ -1360,7 +1457,7 @@ bool QODBCResult::exec() if (*ind != SQL_NULL_DATA) *ind = str.length(); int strSize = str.length(); - + r = SQLBindParameter(d->hStmt, i + 1, qParamType[(QFlag)(bindValueType(i)) & QSql::InOut], @@ -1531,10 +1628,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); } @@ -1655,7 +1748,7 @@ bool QODBCDriver::open(const QString & db, // support the "DRIVER={SQL SERVER};SERVER=blah" syntax if (db.contains(QLatin1String(".dsn"), Qt::CaseInsensitive)) connQStr = QLatin1String("FILEDSN=") + db; - else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive) + else if (db.contains(QLatin1String("DRIVER="), Qt::CaseInsensitive) || db.contains(QLatin1String("SERVER="), Qt::CaseInsensitive)) connQStr = db; else @@ -1665,7 +1758,7 @@ bool QODBCDriver::open(const QString & db, connQStr += QLatin1String(";UID=") + user; if (!password.isEmpty()) connQStr += QLatin1String(";PWD=") + password; - + SQLSMALLINT cb; SQLTCHAR connOut[1024]; r = SQLDriverConnect(d->hDbc, @@ -1752,14 +1845,7 @@ void QODBCDriverPrivate::checkUnicode() unicode = false; return; #endif -#if defined(Q_WS_WIN) - QT_WA( - {}, - { - unicode = false; - return; - }) -#endif + SQLRETURN r; SQLUINTEGER fFunc; @@ -2071,6 +2157,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, @@ -2173,6 +2275,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); @@ -2282,12 +2400,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("\".\"")); @@ -2296,4 +2414,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 4b094c6..13e6d82 100644 --- a/src/sql/kernel/qsqlcachedresult.cpp +++ b/src/sql/kernel/qsqlcachedresult.cpp @@ -297,4 +297,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..5aef39e 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); } /*! @@ -949,7 +960,7 @@ bool QSqlDatabase::rollback() connection, set the database name, and call open() again. \note The \e{database name} is not the \e{connection name}. The connection name must be passed to addDatabase() at connection - object create time. + object create time. For the QOCI (Oracle) driver, the database name is the TNS Service Name. @@ -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 |