summaryrefslogtreecommitdiffstats
path: root/src/sql/drivers
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2009-07-13 15:28:09 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-07-13 15:28:09 (GMT)
commit650353f402d725688821b83904fe6319c8afa4da (patch)
tree943c251676fe006e723e991bd126c002bcbb7bbd /src/sql/drivers
parent4a82d7cc185bd98ec8183fb0ccadefd3196b3ee2 (diff)
parent754d9c2f1d0dce16017b99f766c1fa3e47b0afde (diff)
downloadQt-650353f402d725688821b83904fe6319c8afa4da.zip
Qt-650353f402d725688821b83904fe6319c8afa4da.tar.gz
Qt-650353f402d725688821b83904fe6319c8afa4da.tar.bz2
Merge branch 'master' of scm.dev.nokia.troll.no:qt/qt into qtwebkit-4.6-staging
Conflicts: util/webkit/mkdist-webkit
Diffstat (limited to 'src/sql/drivers')
-rw-r--r--src/sql/drivers/db2/qsql_db2.cpp30
-rw-r--r--src/sql/drivers/ibase/qsql_ibase.cpp52
-rw-r--r--src/sql/drivers/oci/qsql_oci.cpp9
-rw-r--r--src/sql/drivers/odbc/qsql_odbc.cpp46
-rw-r--r--src/sql/drivers/psql/qsql_psql.cpp51
5 files changed, 109 insertions, 79 deletions
diff --git a/src/sql/drivers/db2/qsql_db2.cpp b/src/sql/drivers/db2/qsql_db2.cpp
index 1a82377..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
@@ -111,22 +107,14 @@ public:
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)
@@ -347,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);
@@ -740,7 +724,6 @@ bool QDB2Result::exec()
ind);
break; }
case QVariant::String:
-#ifdef UNICODE
{
QString str(values.at(i).toString());
if (*ind != SQL_NULL_DATA)
@@ -774,8 +757,6 @@ bool QDB2Result::exec()
}
break;
}
-#endif
- // fall through
default: {
QByteArray ba = values.at(i).toString().toAscii();
int len = ba.length() + 1;
@@ -849,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; }
@@ -1542,13 +1520,7 @@ bool QDB2Driver::hasFeature(DriverFeature f) const
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;
}
diff --git a/src/sql/drivers/ibase/qsql_ibase.cpp b/src/sql/drivers/ibase/qsql_ibase.cpp
index d714327..0c4fff0 100644
--- a/src/sql/drivers/ibase/qsql_ibase.cpp
+++ b/src/sql/drivers/ibase/qsql_ibase.cpp
@@ -97,6 +97,7 @@ static bool getIBaseError(QString& msg, ISC_STATUS* status, ISC_LONG &sqlcode, Q
static void createDA(XSQLDA *&sqlda)
{
sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1));
+ if (sqlda == (XSQLDA*)0) return;
sqlda->sqln = 1;
sqlda->sqld = 0;
sqlda->version = SQLDA_CURRENT_VERSION;
@@ -106,8 +107,10 @@ static void createDA(XSQLDA *&sqlda)
static void enlargeDA(XSQLDA *&sqlda, int n)
{
- free(sqlda);
+ if (sqlda != (XSQLDA*)0)
+ free(sqlda);
sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(n));
+ if (sqlda == (XSQLDA*)0) return;
sqlda->sqln = n;
sqlda->version = SQLDA_CURRENT_VERSION;
}
@@ -126,14 +129,14 @@ static void initDA(XSQLDA *sqlda)
case SQL_TYPE_DATE:
case SQL_TEXT:
case SQL_BLOB:
- sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen);
+ sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen];
break;
case SQL_ARRAY:
- sqlda->sqlvar[i].sqldata = (char*)malloc(sizeof(ISC_QUAD));
+ sqlda->sqlvar[i].sqldata = new char[sizeof(ISC_QUAD)];
memset(sqlda->sqlvar[i].sqldata, 0, sizeof(ISC_QUAD));
break;
case SQL_VARYING:
- sqlda->sqlvar[i].sqldata = (char*)malloc(sqlda->sqlvar[i].sqllen + sizeof(short));
+ sqlda->sqlvar[i].sqldata = new char[sqlda->sqlvar[i].sqllen + sizeof(short)];
break;
default:
// not supported - do not bind.
@@ -141,7 +144,7 @@ static void initDA(XSQLDA *sqlda)
break;
}
if (sqlda->sqlvar[i].sqltype & 1) {
- sqlda->sqlvar[i].sqlind = (short*)malloc(sizeof(short));
+ sqlda->sqlvar[i].sqlind = new short[1];
*(sqlda->sqlvar[i].sqlind) = 0;
} else {
sqlda->sqlvar[i].sqlind = 0;
@@ -154,8 +157,8 @@ static void delDA(XSQLDA *&sqlda)
if (!sqlda)
return;
for (int i = 0; i < sqlda->sqld; ++i) {
- free(sqlda->sqlvar[i].sqlind);
- free(sqlda->sqlvar[i].sqldata);
+ delete [] sqlda->sqlvar[i].sqlind;
+ delete [] sqlda->sqlvar[i].sqldata;
}
free(sqlda);
sqlda = 0;
@@ -300,7 +303,7 @@ struct QIBaseEventBuffer {
#endif
ISC_LONG bufferLength;
ISC_LONG eventId;
-
+
enum QIBaseSubscriptionState { Starting, Subscribed, Finished };
QIBaseSubscriptionState subscriptionState;
};
@@ -451,8 +454,8 @@ QVariant QIBaseResultPrivate::fetchBlob(ISC_QUAD *bId)
}
ba.resize(read);
- bool isErr = (status[1] == isc_segstr_eof ? false :
- isError(QT_TRANSLATE_NOOP("QIBaseResult",
+ bool isErr = (status[1] == isc_segstr_eof ? false :
+ isError(QT_TRANSLATE_NOOP("QIBaseResult",
"Unable to read BLOB"),
QSqlError::StatementError));
@@ -519,7 +522,7 @@ static char* readArrayBuffer(QList<QVariant>& list, char *buffer, short curDim,
valList.append(tc->toUnicode(buffer, o));
else
valList.append(QString::fromUtf8(buffer, o));
-
+
buffer += strLen;
}
break; }
@@ -644,8 +647,8 @@ char* fillList<float>(char *buffer, const QList<QVariant> &list, float*)
return buffer;
}
-static char* qFillBufferWithString(char *buffer, const QString& string,
- short buflen, bool varying, bool array,
+static char* qFillBufferWithString(char *buffer, const QString& string,
+ short buflen, bool varying, bool array,
QTextCodec *tc)
{
QByteArray str = encodeString(tc, string); // keep a copy of the string alive in this scope
@@ -885,7 +888,16 @@ bool QIBaseResult::prepare(const QString& query)
setAt(QSql::BeforeFirstRow);
createDA(d->sqlda);
+ if (d->sqlda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ return false;
+ }
+
createDA(d->inda);
+ if (d->inda == (XSQLDA*)0){
+ qWarning()<<"QIOBaseResult: createDA(): failed to allocate memory";
+ return false;
+ }
if (!d->transaction())
return false;
@@ -906,6 +918,10 @@ bool QIBaseResult::prepare(const QString& query)
return false;
if (d->inda->sqld > d->inda->sqln) {
enlargeDA(d->inda, d->inda->sqld);
+ if (d->inda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ return false;
+ }
isc_dsql_describe_bind(d->status, &d->stmt, FBVERSION, d->inda);
if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult",
@@ -916,6 +932,10 @@ bool QIBaseResult::prepare(const QString& query)
if (d->sqlda->sqld > d->sqlda->sqln) {
// need more field descriptors
enlargeDA(d->sqlda, d->sqlda->sqld);
+ if (d->sqlda == (XSQLDA*)0) {
+ qWarning()<<"QIOBaseResult: enlargeDA(): failed to allocate memory";
+ return false;
+ }
isc_dsql_describe(d->status, &d->stmt, FBVERSION, d->sqlda);
if (d->isError(QT_TRANSLATE_NOOP("QIBaseResult", "Could not describe statement"),
@@ -983,14 +1003,14 @@ bool QIBaseResult::exec()
break;
case SQL_LONG:
if (d->inda->sqlvar[para].sqlscale < 0)
- *((long*)d->inda->sqlvar[para].sqldata) =
+ *((long*)d->inda->sqlvar[para].sqldata) =
(long)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
else
*((long*)d->inda->sqlvar[para].sqldata) = (long)val.toLongLong();
break;
case SQL_SHORT:
if (d->inda->sqlvar[para].sqlscale < 0)
- *((short*)d->inda->sqlvar[para].sqldata) =
+ *((short*)d->inda->sqlvar[para].sqldata) =
(short)floor(0.5 + val.toDouble() * pow(10.0, d->inda->sqlvar[para].sqlscale * -1));
else
*((short*)d->inda->sqlvar[para].sqldata) = (short)val.toInt();
@@ -1717,7 +1737,7 @@ static isc_callback qEventCallback(char *result, short length, char *updated)
qMutex()->lock();
QIBaseDriver *driver = qBufferDriverMap()->value(result);
qMutex()->unlock();
-
+
// We use an asynchronous call (i.e., queued connection) because the event callback
// is executed in a different thread than the one in which the driver lives.
if (driver)
diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp
index d5fb10f..617f116 100644
--- a/src/sql/drivers/oci/qsql_oci.cpp
+++ b/src/sql/drivers/oci/qsql_oci.cpp
@@ -609,7 +609,7 @@ static QSqlField qFromOraInf(const OraFieldInfo &ofi)
QSqlField f(ofi.name, ofi.type);
f.setRequired(ofi.oraIsNull == 0);
- if (ofi.type == QVariant::String)
+ if (ofi.type == QVariant::String && ofi.oraType != SQLT_NUM && ofi.oraType != SQLT_VNU)
f.setLength(ofi.oraFieldLength);
else
f.setLength(ofi.oraPrecision == 0 ? 38 : int(ofi.oraPrecision));
@@ -1586,9 +1586,12 @@ void QOCICols::getValues(QVector<QVariant> &v, int index)
} else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
&& (fld.typ == QVariant::LongLong)) {
qint64 qll = 0;
- OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64),
+ int r = OCINumberToInt(d->err, reinterpret_cast<OCINumber *>(fld.data), sizeof(qint64),
OCI_NUMBER_SIGNED, &qll);
- v[index + i] = qll;
+ if(r == OCI_SUCCESS)
+ v[index + i] = qll;
+ else
+ v[index + i] = QVariant();
break;
} else if ((d->q->numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
&& (fld.typ == QVariant::Int)) {
diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp
index 7bda969..fa9031a 100644
--- a/src/sql/drivers/odbc/qsql_odbc.cpp
+++ b/src/sql/drivers/odbc/qsql_odbc.cpp
@@ -89,7 +89,8 @@ public:
enum DefaultCase{Lower, Mixed, Upper, Sensitive};
QODBCDriverPrivate()
: hEnv(0), hDbc(0), useSchema(false), disconnectCount(0), isMySqlServer(false),
- isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false)
+ isMSSqlServer(false), hasSQLFetchScroll(true), hasMultiResultSets(false),
+ isQuoteInitialized(false), quote(QLatin1Char('"'))
{
unicode = false;
}
@@ -116,7 +117,10 @@ public:
QString &schema, QString &table);
DefaultCase defaultCase() const;
QString adjustCase(const QString&) const;
- QChar quoteChar() const;
+ QChar quoteChar();
+private:
+ bool isQuoteInitialized;
+ QChar quote;
};
class QODBCPrivate
@@ -255,9 +259,11 @@ static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSi
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_BIT:
- case SQL_TINYINT:
type = isSigned ? QVariant::Int : QVariant::UInt;
break;
+ case SQL_TINYINT:
+ type = QVariant::UInt;
+ break;
case SQL_BIGINT:
type = isSigned ? QVariant::LongLong : QVariant::ULongLong;
break;
@@ -560,14 +566,12 @@ 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
+QChar QODBCDriverPrivate::quoteChar()
{
- static bool isQuoteInitialized = false;
- static QChar quote = QChar::fromLatin1('"');
if (!isQuoteInitialized) {
char driverResponse[4];
SQLSMALLINT length;
@@ -577,9 +581,9 @@ QChar QODBCDriverPrivate::quoteChar() const
sizeof(driverResponse),
&length);
if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
- quote = QChar::fromLatin1(driverResponse[0]);
+ quote = QLatin1Char(driverResponse[0]);
} else {
- quote = QChar::fromLatin1('"');
+ quote = QLatin1Char('"');
}
isQuoteInitialized = true;
}
@@ -970,7 +974,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));
@@ -989,7 +993,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));
@@ -1020,7 +1024,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));
@@ -1111,7 +1115,7 @@ QVariant QODBCResult::data(int field)
d->fieldCache[i] = qGetBinaryData(d->hStmt, i);
break;
case QVariant::String:
- d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), true);
+ d->fieldCache[i] = qGetStringData(d->hStmt, i, info.length(), d->unicode);
break;
case QVariant::Double:
switch(numericalPrecisionPolicy()) {
@@ -1455,7 +1459,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],
@@ -1536,6 +1540,7 @@ bool QODBCResult::exec()
values[i] = QVariant(QDateTime(QDate(dt.year, dt.month, dt.day),
QTime(dt.hour, dt.minute, dt.second, dt.fraction / 1000000)));
break; }
+ case QVariant::Bool:
case QVariant::Int:
case QVariant::UInt:
case QVariant::Double:
@@ -1745,7 +1750,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
@@ -1755,7 +1760,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,
@@ -1842,14 +1847,7 @@ void QODBCDriverPrivate::checkUnicode()
unicode = false;
return;
#endif
-#if defined(Q_WS_WIN)
- QT_WA(
- {},
- {
- unicode = false;
- return;
- })
-#endif
+
SQLRETURN r;
SQLUINTEGER fFunc;
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp
index d197388..c61c526 100644
--- a/src/sql/drivers/psql/qsql_psql.cpp
+++ b/src/sql/drivers/psql/qsql_psql.cpp
@@ -54,11 +54,33 @@
#include <qstringlist.h>
#include <qmutex.h>
+
#include <libpq-fe.h>
#include <pg_config.h>
#include <stdlib.h>
#include <math.h>
+// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html
+#ifndef isnan
+ # define isnan(x) \
+ (sizeof (x) == sizeof (long double) ? isnan_ld (x) \
+ : sizeof (x) == sizeof (double) ? isnan_d (x) \
+ : isnan_f (x))
+ static inline int isnan_f (float x) { return x != x; }
+ static inline int isnan_d (double x) { return x != x; }
+ static inline int isnan_ld (long double x) { return x != x; }
+#endif
+
+#ifndef isinf
+ # define isinf(x) \
+ (sizeof (x) == sizeof (long double) ? isinf_ld (x) \
+ : sizeof (x) == sizeof (double) ? isinf_d (x) \
+ : isinf_f (x))
+ static inline int isinf_f (float x) { return isnan (x - x); }
+ static inline int isinf_d (double x) { return isnan (x - x); }
+ static inline int isinf_ld (long double x) { return isnan (x - x); }
+#endif
+
// workaround for postgres defining their OIDs in a private header file
#define QBOOLOID 16
@@ -601,10 +623,9 @@ static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection)
{
QPSQLDriver::Protocol serverVersion = QPSQLDriver::Version6;
PGresult* result = PQexec(connection, "select version()");
- int status = PQresultStatus(result);
+ int status = PQresultStatus(result);
if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
QString val = QString::fromAscii(PQgetvalue(result, 0, 0));
- PQclear(result);
QRegExp rx(QLatin1String("(\\d+)\\.(\\d+)"));
rx.setMinimal(true); // enforce non-greedy RegExp
if (rx.indexIn(val) != -1) {
@@ -645,6 +666,7 @@ static QPSQLDriver::Protocol getPSQLVersion(PGconn* connection)
}
}
}
+ PQclear(result);
if (serverVersion < QPSQLDriver::Version71)
qWarning("This version of PostgreSQL is not supported and may not work.");
@@ -831,7 +853,7 @@ bool QPSQLDriver::commitTransaction()
if (d->pro == QPSQLDriver::Version8 ||
d->pro == QPSQLDriver::Version81 ||
d->pro == QPSQLDriver::Version82) {
- transaction_failed = QByteArray(PQcmdStatus(res)) == QByteArray("ROLLBACK")?true:false;
+ transaction_failed = qstrcmp(PQcmdStatus(res), "ROLLBACK") == 0;
}
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK || transaction_failed) {
@@ -1161,6 +1183,21 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
qPQfreemem(data);
break;
}
+ case QVariant::Double: {
+ double val = field.value().toDouble();
+ if (isnan(val))
+ r = QLatin1String("'NaN'");
+ else {
+ int res = isinf(val);
+ if (res == 1)
+ r = QLatin1String("'Infinity'");
+ else if (res == -1)
+ r = QLatin1String("'-Infinity'");
+ else
+ r = QSqlDriver::formatValue(field, trimStrings);
+ }
+ break;
+ }
default:
r = QSqlDriver::formatValue(field, trimStrings);
break;
@@ -1265,15 +1302,15 @@ QStringList QPSQLDriver::subscribedToNotificationsImplementation() const
void QPSQLDriver::_q_handleNotification(int)
{
PQconsumeInput(d->connection);
- PGnotify *notify = PQnotifies(d->connection);
- if (notify) {
- QString name(QLatin1String(notify->relname));
+ PGnotify *notify = 0;
+ while((notify = PQnotifies(d->connection)) != 0) {
+ QString name(QLatin1String(notify->relname));
if (d->seid.contains(name))
emit notification(name);
else
qWarning("QPSQLDriver: received notification for '%s' which isn't subscribed to.",
- qPrintable(name));
+ qPrintable(name));
qPQfreemem(notify);
}