567 if (!isSelect() || field >= d->fields.size()) {
568 qCWarning(lcMysql,
"QMYSQLResult::data: column %d out of range", field);
575 my_ulonglong fieldLength = 0;
578 if (d->preparedQuery) {
580 return QVariant(f.type);
581 if (qIsBitfield(f.myField->type)) {
582 return QVariant::fromValue(qDecodeBitfield(f, f.outField));
583 }
else if (qIsInteger(f.type.id())) {
584 QVariant variant(f.type, f.outField);
586 if (f.type.id() == QMetaType::UChar)
587 return variant.toUInt();
588 else if (f.type.id() == QMetaType::Char)
589 return variant.toInt();
595 if (f.type.id() != QMetaType::QTime)
597 if (f.type.id() != QMetaType::QDate)
599 if (f.type.id() == QMetaType::QDateTime)
600 return QDateTime(date, time, QTimeZone::UTC);
601 else if (f.type.id() == QMetaType::QDate)
607 if (f.type.id() != QMetaType::QByteArray)
608 val = QString::fromUtf8(f.outField, f.bufLength);
610 if (d->row[field] ==
nullptr) {
612 return QVariant(f.type);
615 if (qIsBitfield(f.myField->type))
616 return QVariant::fromValue(qDecodeBitfield(f, d->row[field]));
618 fieldLength = mysql_fetch_lengths(d->result)[field];
620 if (f.type.id() != QMetaType::QByteArray)
621 val = QString::fromUtf8(d->row[field], fieldLength);
624 switch (f.type.id()) {
625 case QMetaType::LongLong:
626 return QVariant(val.toLongLong());
627 case QMetaType::ULongLong:
628 return QVariant(val.toULongLong());
629 case QMetaType::Char:
630 case QMetaType::Short:
632 return QVariant(val.toInt());
633 case QMetaType::UChar:
634 case QMetaType::UShort:
635 case QMetaType::UInt:
636 return QVariant(val.toUInt());
637 case QMetaType::Double: {
640 double dbl = val.toDouble(&ok);
641 switch(numericalPrecisionPolicy()) {
642 case QSql::LowPrecisionInt32:
643 v=QVariant(dbl).toInt();
645 case QSql::LowPrecisionInt64:
646 v = QVariant(dbl).toLongLong();
648 case QSql::LowPrecisionDouble:
651 case QSql::HighPrecision:
661 case QMetaType::QDate:
662 return qDateFromString(val);
663 case QMetaType::QTime:
664 return qTimeFromString(val);
665 case QMetaType::QDateTime:
666 return qDateTimeFromString(val);
667 case QMetaType::QByteArray: {
670 if (d->preparedQuery) {
673 ba = QByteArray(d->row[field], fieldLength);
677 case QMetaType::QString:
679 return QVariant(val);
698 if (!driver() || !driver()->isOpen() || driver()->isOpenError())
701 d->preparedQuery =
false;
705 const QByteArray encQuery = query.toUtf8();
706 if (mysql_real_query(d->drv_d_func()->mysql, encQuery.data(), encQuery.size())) {
707 setLastError(qMakeError(QCoreApplication::translate(
"QMYSQLResult",
"Unable to execute query"),
708 QSqlError::StatementError, d->drv_d_func()));
711 d->result = mysql_store_result(d->drv_d_func()->mysql);
712 if (!d->result && mysql_field_count(d->drv_d_func()->mysql) > 0) {
713 setLastError(qMakeError(QCoreApplication::translate(
"QMYSQLResult",
"Unable to store result"),
714 QSqlError::StatementError, d->drv_d_func()));
717 int numFields = mysql_field_count(d->drv_d_func()->mysql);
718 setSelect(numFields != 0);
719 d->fields.resize(numFields);
720 d->rowsAffected = mysql_affected_rows(d->drv_d_func()->mysql);
723 for(
int i = 0; i < numFields; i++) {
724 MYSQL_FIELD* field = mysql_fetch_field_direct(d->result, i);
725 d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
726 d->fields[i].myField = field;
728 setAt(QSql::BeforeFirstRow);
810 if (d->result && isSelect())
811 mysql_free_result(d->result);
815 for (
const QMYSQLResultPrivate::QMyField &f : std::as_const(d->fields))
819 int status = mysql_next_result(d->drv_d_func()->mysql);
821 setLastError(qMakeError(QCoreApplication::translate(
"QMYSQLResult",
"Unable to execute next query"),
822 QSqlError::StatementError, d->drv_d_func()));
824 }
else if (status == -1) {
828 d->result = mysql_store_result(d->drv_d_func()->mysql);
829 unsigned int numFields = mysql_field_count(d->drv_d_func()->mysql);
830 if (!d->result && numFields > 0) {
831 setLastError(qMakeError(QCoreApplication::translate(
"QMYSQLResult",
"Unable to store next result"),
832 QSqlError::StatementError, d->drv_d_func()));
836 setSelect(numFields > 0);
837 d->fields.resize(numFields);
838 d->rowsAffected = mysql_affected_rows(d->drv_d_func()->mysql);
841 for (
unsigned int i = 0; i < numFields; i++) {
842 MYSQL_FIELD *field = mysql_fetch_field_direct(d->result, i);
843 d->fields[i].type = qDecodeMYSQLType(field->type, field->flags);
844 d->fields[i].myField = field;
903 if (!d->preparedQuery)
904 return QSqlResult::exec();
910 QList<QByteArray> stringVector;
911 QList<my_bool> nullVector;
913 const QList<QVariant> values = boundValues();
915 r = mysql_stmt_reset(d->stmt);
917 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
918 "Unable to reset statement"), QSqlError::StatementError, d->stmt));
922 const unsigned long paramCount = mysql_stmt_param_count(d->stmt);
923 if (paramCount > 0 && paramCount ==
static_cast<size_t>(values.size())) {
924 nullVector.resize(values.size());
925 for (qsizetype i = 0; i < values.size(); ++i) {
926 const QVariant &val = boundValues().at(i);
927 void *data =
const_cast<
void *>(val.constData());
929 MYSQL_BIND* currBind = &d->outBinds[i];
931 nullVector[i] =
static_cast<my_bool>(QSqlResultPrivate::isVariantNull(val));
932 currBind->is_null = &nullVector[i];
933 currBind->length = 0;
934 currBind->is_unsigned = 0;
936 switch (val.userType()) {
937 case QMetaType::QByteArray:
938 currBind->buffer_type = MYSQL_TYPE_BLOB;
939 currBind->buffer =
const_cast<
char *>(val.toByteArray().constData());
940 currBind->buffer_length = val.toByteArray().size();
943 case QMetaType::QTime:
944 case QMetaType::QDate:
945 case QMetaType::QDateTime: {
946 auto myTime =
new QT_MYSQL_TIME{};
947 timeVector.append(myTime);
948 currBind->buffer = myTime;
952 int type = val.userType();
953 if (type == QMetaType::QTime) {
955 currBind->buffer_type = MYSQL_TYPE_TIME;
956 myTime->time_type = MYSQL_TIMESTAMP_TIME;
957 }
else if (type == QMetaType::QDate) {
959 currBind->buffer_type = MYSQL_TYPE_DATE;
960 myTime->time_type = MYSQL_TIMESTAMP_DATE;
962 QDateTime dt = val.toDateTime().toUTC();
965 currBind->buffer_type = MYSQL_TYPE_DATETIME;
966 myTime->time_type = MYSQL_TIMESTAMP_DATETIME;
969 if (type == QMetaType::QTime || type == QMetaType::QDateTime) {
970 myTime->hour = time.hour();
971 myTime->minute = time.minute();
972 myTime->second = time.second();
973 myTime->second_part = time.msec() * 1000;
975 if (type == QMetaType::QDate || type == QMetaType::QDateTime) {
976 myTime->year = date.year();
977 myTime->month = date.month();
978 myTime->day = date.day();
981 currBind->length = 0;
983 case QMetaType::UInt:
985 currBind->buffer_type = MYSQL_TYPE_LONG;
986 currBind->buffer = data;
987 currBind->buffer_length =
sizeof(
int);
988 currBind->is_unsigned = (val.userType() != QMetaType::Int);
990 case QMetaType::Bool:
991 currBind->buffer_type = MYSQL_TYPE_TINY;
992 currBind->buffer = data;
993 currBind->buffer_length =
sizeof(
bool);
994 currBind->is_unsigned =
false;
996 case QMetaType::Double:
997 currBind->buffer_type = MYSQL_TYPE_DOUBLE;
998 currBind->buffer = data;
999 currBind->buffer_length =
sizeof(
double);
1001 case QMetaType::LongLong:
1002 case QMetaType::ULongLong:
1003 currBind->buffer_type = MYSQL_TYPE_LONGLONG;
1004 currBind->buffer = data;
1005 currBind->buffer_length =
sizeof(qint64);
1006 currBind->is_unsigned = (val.userType() == QMetaType::ULongLong);
1008 case QMetaType::QString:
1010 QByteArray ba = val.toString().toUtf8();
1011 stringVector.append(ba);
1012 currBind->buffer_type = MYSQL_TYPE_STRING;
1013 currBind->buffer =
const_cast<
char *>(ba.constData());
1014 currBind->buffer_length = ba.size();
1019#if defined(MARIADB_VERSION_ID) || MYSQL_VERSION_ID < 80300
1020 r = mysql_stmt_bind_param(d->stmt, d->outBinds);
1022 r = mysql_stmt_bind_named_param(d->stmt, d->outBinds, paramCount,
nullptr);
1025 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
1026 "Unable to bind value"), QSqlError::StatementError, d->stmt));
1027 qDeleteAll(timeVector);
1031 r = mysql_stmt_execute(d->stmt);
1033 qDeleteAll(timeVector);
1036 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
1037 "Unable to execute statement"), QSqlError::StatementError, d->stmt));
1043 d->rowsAffected = mysql_stmt_affected_rows(d->stmt);
1046 my_bool update_max_length =
true;
1048 r = mysql_stmt_bind_result(d->stmt, d->inBinds);
1050 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
1051 "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
1055 mysql_stmt_attr_set(d->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &update_max_length);
1057 r = mysql_stmt_store_result(d->stmt);
1059 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
1060 "Unable to store statement results"), QSqlError::StatementError, d->stmt));
1069 r = mysql_stmt_bind_result(d->stmt, d->inBinds);
1071 setLastError(qMakeStmtError(QCoreApplication::translate(
"QMYSQLResult",
1072 "Unable to bind outvalues"), QSqlError::StatementError, d->stmt));
1076 setAt(QSql::BeforeFirstRow);
1267bool QMYSQLDriver::open(
const QString &db,
1268 const QString &user,
1269 const QString &password,
1270 const QString &host,
1272 const QString &connOpts)
1278 if (!(d->mysql = mysql_init(
nullptr))) {
1279 setLastError(qMakeError(tr(
"Unable to allocate a MYSQL object"),
1280 QSqlError::ConnectionError, d));
1285 typedef bool (*SetOptionFunc)(MYSQL*, mysql_option, QStringView);
1286 struct mysqloptions {
1287 QLatin1StringView key;
1288 mysql_option option;
1291 const mysqloptions options[] = {
1292 {
"SSL_KEY"_L1, MYSQL_OPT_SSL_KEY, setOptionString},
1293 {
"SSL_CERT"_L1, MYSQL_OPT_SSL_CERT, setOptionString},
1294 {
"SSL_CA"_L1, MYSQL_OPT_SSL_CA, setOptionString},
1295 {
"SSL_CAPATH"_L1, MYSQL_OPT_SSL_CAPATH, setOptionString},
1296 {
"SSL_CIPHER"_L1, MYSQL_OPT_SSL_CIPHER, setOptionString},
1297 {
"MYSQL_OPT_SSL_KEY"_L1, MYSQL_OPT_SSL_KEY, setOptionString},
1298 {
"MYSQL_OPT_SSL_CERT"_L1, MYSQL_OPT_SSL_CERT, setOptionString},
1299 {
"MYSQL_OPT_SSL_CA"_L1, MYSQL_OPT_SSL_CA, setOptionString},
1300 {
"MYSQL_OPT_SSL_CAPATH"_L1, MYSQL_OPT_SSL_CAPATH, setOptionString},
1301 {
"MYSQL_OPT_SSL_CIPHER"_L1, MYSQL_OPT_SSL_CIPHER, setOptionString},
1302 {
"MYSQL_OPT_SSL_CRL"_L1, MYSQL_OPT_SSL_CRL, setOptionString},
1303 {
"MYSQL_OPT_SSL_CRLPATH"_L1, MYSQL_OPT_SSL_CRLPATH, setOptionString},
1304#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50710
1305 {
"MYSQL_OPT_TLS_VERSION"_L1, MYSQL_OPT_TLS_VERSION, setOptionString},
1307#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50711
&& !defined(MARIADB_VERSION_ID)
1308 {
"MYSQL_OPT_SSL_MODE"_L1, MYSQL_OPT_SSL_MODE, setOptionSslMode},
1310#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50711
&& MYSQL_VERSION_ID < 80000
|| defined(MARIADB_VERSION_ID)
1311 {
"MYSQL_OPT_SSL_VERIFY_SERVER_CERT"_L1, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, setOptionBool},
1313 {
"MYSQL_OPT_CONNECT_TIMEOUT"_L1, MYSQL_OPT_CONNECT_TIMEOUT, setOptionInt},
1314 {
"MYSQL_OPT_READ_TIMEOUT"_L1, MYSQL_OPT_READ_TIMEOUT, setOptionInt},
1315 {
"MYSQL_OPT_WRITE_TIMEOUT"_L1, MYSQL_OPT_WRITE_TIMEOUT, setOptionInt},
1316 {
"MYSQL_OPT_RECONNECT"_L1, MYSQL_OPT_RECONNECT, setOptionBool},
1317 {
"MYSQL_OPT_LOCAL_INFILE"_L1, MYSQL_OPT_LOCAL_INFILE, setOptionInt},
1318 {
"MYSQL_OPT_PROTOCOL"_L1, MYSQL_OPT_PROTOCOL, setOptionProtocol},
1319 {
"MYSQL_SHARED_MEMORY_BASE_NAME"_L1, MYSQL_SHARED_MEMORY_BASE_NAME, setOptionString},
1321 auto trySetOption = [&](
const QStringView &key,
const QStringView &value) ->
bool {
1322 for (
const mysqloptions &opt : options) {
1323 if (key == opt.key) {
1324 if (!opt.func(d->mysql, opt.option, value)) {
1325 qCWarning(lcMysql,
"QMYSQLDriver::open: Could not set connect option value "
1327 qUtf16Printable(QString(key)), qUtf16Printable(QString(value)));
1336
1337
1338
1339
1340 unsigned int optionFlags = CLIENT_MULTI_STATEMENTS;
1341 const QList<QStringView> opts(QStringView(connOpts).split(u';', Qt::SkipEmptyParts));
1345 for (
const auto &option : opts) {
1346 const QStringView sv = QStringView(option).trimmed();
1348 if ((idx = sv.indexOf(u'=')) != -1) {
1349 const QStringView key = sv.left(idx).trimmed();
1350 const QStringView val = sv.mid(idx + 1).trimmed();
1351 if (trySetOption(key, val))
1353 else if (key ==
"UNIX_SOCKET"_L1)
1354 unixSocket = val.toString();
1355 else if (val ==
"TRUE"_L1 || val ==
"1"_L1)
1356 setOptionFlag(optionFlags, key);
1358 qCWarning(lcMysql,
"QMYSQLDriver::open: Illegal connect option value '%ls'",
1359 qUtf16Printable(QString(sv)));
1361 setOptionFlag(optionFlags, sv);
1366 static const char wanted_charsets[][8] = {
"utf8mb4",
"utf8" };
1367#if defined(MARIADB_PACKAGE_VERSION_ID)
1368 MARIADB_CHARSET_INFO *cs =
nullptr;
1369 for (
const char *p : wanted_charsets) {
1370 cs = mariadb_get_charset_by_name(p);
1372 d->mysql->charset = cs;
1383 MYSQL *mysql = mysql_real_connect(d->mysql,
1384 host.isNull() ?
nullptr : host.toUtf8().constData(),
1385 user.isNull() ?
nullptr : user.toUtf8().constData(),
1386 password.isNull() ?
nullptr : password.toUtf8().constData(),
1387 db.isNull() ?
nullptr : db.toUtf8().constData(),
1388 (port > -1) ? port : 0,
1389 unixSocket.isNull() ?
nullptr : unixSocket.toUtf8().constData(),
1392 if (mysql != d->mysql) {
1393 setLastError(qMakeError(tr(
"Unable to connect"),
1394 QSqlError::ConnectionError, d));
1395 mysql_close(d->mysql);
1402 if (!cs || mysql_set_character_set(d->mysql, cs->csname) != 0) {
1404 for (
const char *p : wanted_charsets) {
1405 if (mysql_set_character_set(d->mysql, p) == 0) {
1411 qCWarning(lcMysql,
"MySQL: Unable to set the client character set to utf8 (\"%s\"). "
1412 "Using '%s' instead.",
1413 mysql_error(d->mysql),
1414 mysql_character_set_name(d->mysql));
1417 if (!db.isEmpty() && mysql_select_db(d->mysql, db.toUtf8().constData())) {
1418 setLastError(qMakeError(tr(
"Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d));
1419 mysql_close(d->mysql);
1424 d->preparedQuerysEnabled = checkPreparedQueries(d->mysql);
1427 if (d->preparedQuerysEnabled)
1428 setUtcTimeZone(d->mysql);
1430#if QT_CONFIG(thread)
1431 mysql_thread_init();
1435 setOpenError(
false);
1502QSqlRecord QMYSQLDriver::record(
const QString &tablename)
const
1504 Q_D(
const QMYSQLDriver);
1507 QSqlQuery i(createResult());
1508 QString stmt(
"SELECT * FROM %1 LIMIT 0"_L1);
1509 i.exec(stmt.arg(escapeIdentifier(tablename, QSqlDriver::TableName)));
1510 auto r = i.record();
1515 stmt =
"SELECT column_name, column_default FROM information_schema.columns WHERE table_schema = '"_L1
1516 + d->dbName +
"' AND table_name = '%1'"_L1;
1517 const auto baTableName = tablename.toUtf8();
1518 QVarLengthArray<
char> tableNameQuoted(baTableName.size() * 2 + 1);
1519#if defined(MARIADB_PACKAGE_VERSION_ID)
1520 const auto len = mysql_real_escape_string(d->mysql, tableNameQuoted.data(),
1521 baTableName.data(), baTableName.size());
1523 const auto len = mysql_real_escape_string_quote(d->mysql, tableNameQuoted.data(),
1524 baTableName.data(), baTableName.size(),
'\'');
1526 if (i.exec(stmt.arg(QUtf8StringView(tableNameQuoted.data(), len)))) {
1528 const auto colName = i.value(0).toString();
1529 const auto recordIdx = r.indexOf(colName);
1530 if (recordIdx >= 0) {
1531 auto field = r.field(recordIdx);
1532 field.setDefaultValue(i.value(1));
1533 r.replace(recordIdx, field);
1591QString QMYSQLDriver::formatValue(
const QSqlField &field,
bool trimStrings)
const
1593 Q_D(
const QMYSQLDriver);
1595 if (field.isNull()) {
1596 r = QStringLiteral(
"NULL");
1598 switch (field.metaType().id()) {
1599 case QMetaType::Double:
1600 r = QString::number(field.value().toDouble(),
'g', field.precision());
1602 case QMetaType::QString:
1604 r = QSqlDriver::formatValue(field, trimStrings);
1605 r.replace(
"\\"_L1,
"\\\\"_L1);
1607 case QMetaType::QByteArray:
1609 const QByteArray ba = field.value().toByteArray();
1611 QVarLengthArray<
char, 512> buffer(ba.size() * 2 + 1);
1612 auto escapedSize = mysql_real_escape_string(d->mysql, buffer.data(), ba.data(), ba.size());
1613 r.reserve(escapedSize + 3);
1614 r = u'\'' + QString::fromUtf8(buffer.data(), escapedSize) + u'\'';
1617 qCWarning(lcMysql,
"QMYSQLDriver::formatValue: Database not open");
1620 case QMetaType::QDateTime:
1621 if (QDateTime dt = field.value().toDateTime().toUTC(); dt.isValid()) {
1627 dt.date().toString(Qt::ISODate) +
1629 dt.time().toString(Qt::ISODateWithMs) +
1634 r = QSqlDriver::formatValue(field, trimStrings);