From d5987bf266912bea9b7a3e2dba50de8d6d3d23bb Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Sun, 13 Sep 2026 00:28:45 +0530 Subject: [PATCH 1/7] Introduce SqlFieldType Abstraction and Enhance MySQL Result Handling; --- orm_lib/inc/drogon/orm/Field.h | 29 +++++ orm_lib/inc/drogon/orm/Result.h | 104 ++++++++++++++++ orm_lib/src/Result.cc | 25 ++++ orm_lib/src/ResultImpl.h | 7 ++ orm_lib/src/mysql_impl/MysqlResultImpl.cc | 5 + orm_lib/src/mysql_impl/MysqlResultImpl.h | 144 +++++++++++++++++++++- 6 files changed, 310 insertions(+), 4 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index 53b2dbc73f..e682636fb9 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -66,6 +66,35 @@ class DROGON_EXPORT Field return result_.getLength(row_, column_); } + SqlFieldType sqlType() const noexcept + { + return result_.getSqlType(column_); + } + + /// SQL type name (VARCHAR, INT, DECIMAL, etc.) + const std::string &typeName() const noexcept + { + return result_.getTypeName(column_); + } + + /// Character length (VARCHAR) + int columnLength() const noexcept + { + return result_.getColumnLength(column_); + } + + /// Numeric precision (DECIMAL / NUMERIC) + int precision() const noexcept + { + return result_.getPrecision(column_); + } + + /// Numeric scale (DECIMAL / NUMERIC) + int scale() const noexcept + { + return result_.getScale(column_); + } + /// Convert to a type T value template T as() const diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index ab6389682d..3ac6620f32 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -34,12 +34,40 @@ class Row; class ResultImpl; using ResultImplPtr = std::shared_ptr; +enum class SqlFieldType : uint8_t +{ + Unknown = 0, + Bool, + Int, + BigInt, + Float, + Double, + Decimal, + Varchar, + Text, + Date, + Time, + DateTime, + Blob, + Json, + Binary +}; + enum class SqlStatus { Ok, End }; +struct ColumnMeta +{ + SqlFieldType sqlType{SqlFieldType::Unknown}; + std::string typeName; + int length{0}; + int precision{0}; + int scale{0}; +}; + /// Result set containing data returned by a query or command. /** This behaves as a container (as defined by the C++ standard library) and * provides random access const iterators to iterate over its rows. A row @@ -139,6 +167,82 @@ class DROGON_EXPORT Result */ unsigned long long insertId() const noexcept; + /** + * @brief Get the logical SQL type of the specified column. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return The abstracted SQL field type for the given column, or + * SqlFieldType::Unknown if the type cannot be determined. + * + * @note Type metadata may only be fully populated for some database + * backends (for example, MySQL). For other backends, the result + * may be limited or fall back to SqlFieldType::Unknown. + */ + SqlFieldType getSqlType(SizeType column) const; + + /** + * @brief Get the database-specific type name of the specified column. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return A reference to a string containing the type name as reported + * by the underlying database driver (for example, "INT", + * "VARCHAR", "DECIMAL(10,2)", etc.). + * + * @note Type-name metadata may only be fully populated for some database + * backends (for example, MySQL). On other backends, the returned + * string may be empty or use a backend-specific representation. + */ + const std::string &getTypeName(SizeType column) const; + + /** + * @brief Get the defined maximum length of the specified column. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return The maximum length for the column in characters or bytes, as + * reported by the underlying database driver, or 0 if this + * information is not available. + * + * @note Length metadata may only be populated for some database backends + * (for example, MySQL) and for certain column types (such as + * character and binary types). + */ + int getColumnLength(SizeType column) const; + + /** + * @brief Get the numeric precision for the specified column. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return The precision (total number of significant digits) for the + * column, as reported by the underlying database driver, or 0 if + * not applicable or not available. + * + * @note Precision is generally only meaningful for numeric types such as + * DECIMAL or NUMERIC. On other types, the value may be 0 or + * unspecified, and metadata may only be provided by some backends + * (for example, MySQL). + */ + int getPrecision(SizeType column) const; + + /** + * @brief Get the numeric scale for the specified column. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return The scale (number of fractional digits) for the column, as + * reported by the underlying database driver, or 0 if not + * applicable or not available. + * + * @note Scale is generally only meaningful for numeric types such as + * DECIMAL or NUMERIC. On other types, the value may be 0 or + * unspecified, and metadata may only be provided by some backends + * (for example, MySQL). + */ + int getScale(SizeType column) const; + #ifdef _MSC_VER Result() noexcept = default; #endif diff --git a/orm_lib/src/Result.cc b/orm_lib/src/Result.cc index fe45ebb314..309884f90e 100644 --- a/orm_lib/src/Result.cc +++ b/orm_lib/src/Result.cc @@ -170,6 +170,31 @@ unsigned long long Result::insertId() const noexcept return resultPtr_->insertId(); } +SqlFieldType Result::getSqlType(SizeType column) const +{ + return resultPtr_->columnMeta(column).sqlType; +} + +const std::string &Result::getTypeName(SizeType column) const +{ + return resultPtr_->columnMeta(column).typeName; +} + +int Result::getColumnLength(SizeType column) const +{ + return resultPtr_->columnMeta(column).length; +} + +int Result::getPrecision(SizeType column) const +{ + return resultPtr_->columnMeta(column).precision; +} + +int Result::getScale(SizeType column) const +{ + return resultPtr_->columnMeta(column).scale; +} + int Result::oid(RowSizeType column) const noexcept { return resultPtr_->oid(column); diff --git a/orm_lib/src/ResultImpl.h b/orm_lib/src/ResultImpl.h index fb8e1d0379..5322b9819b 100644 --- a/orm_lib/src/ResultImpl.h +++ b/orm_lib/src/ResultImpl.h @@ -37,6 +37,13 @@ class ResultImpl : public trantor::NonCopyable virtual bool isNull(SizeType row, RowSizeType column) const = 0; virtual FieldSizeType getLength(SizeType row, RowSizeType column) const = 0; + virtual const ColumnMeta &columnMeta(SizeType column) const + { + (void)column; + static const ColumnMeta dummy{}; + return dummy; + } + virtual unsigned long long insertId() const noexcept { return 0; diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.cc b/orm_lib/src/mysql_impl/MysqlResultImpl.cc index 20b4a42768..ff664b0dd8 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.cc +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.cc @@ -83,3 +83,8 @@ unsigned long long MysqlResultImpl::insertId() const noexcept { return insertId_; } + +const ColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const +{ + return columnMeta_.at(column); +} diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 64013f0271..057a9c5219 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -26,6 +26,116 @@ namespace drogon { namespace orm { + +inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) +{ + switch (t) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + return SqlFieldType::Int; + + case MYSQL_TYPE_LONGLONG: + return SqlFieldType::BigInt; + + case MYSQL_TYPE_FLOAT: + return SqlFieldType::Float; + + case MYSQL_TYPE_DOUBLE: + return SqlFieldType::Double; + + case MYSQL_TYPE_NEWDECIMAL: + return SqlFieldType::Decimal; + + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + if (flags & BINARY_FLAG) + return SqlFieldType::Binary; + return SqlFieldType::Varchar; + + case MYSQL_TYPE_STRING: + if (flags & BINARY_FLAG) + return SqlFieldType::Binary; + return SqlFieldType::Varchar; + + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + return SqlFieldType::Blob; + + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + return SqlFieldType::Varchar; + + case MYSQL_TYPE_BIT: + return SqlFieldType::Bool; + + case MYSQL_TYPE_DATE: + return SqlFieldType::Date; + + case MYSQL_TYPE_TIME: + return SqlFieldType::Time; + + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + return SqlFieldType::DateTime; + + case MYSQL_TYPE_JSON: + return SqlFieldType::Json; + + default: + return SqlFieldType::Unknown; + } +} + +inline const char *mysqlFieldTypeToName(enum enum_field_types t, + unsigned int flags) +{ + switch (t) + { + case MYSQL_TYPE_TINY: + return "TINYINT"; + case MYSQL_TYPE_SHORT: + return "SMALLINT"; + case MYSQL_TYPE_LONG: + return "INT"; + case MYSQL_TYPE_LONGLONG: + return "BIGINT"; + case MYSQL_TYPE_FLOAT: + return "FLOAT"; + case MYSQL_TYPE_DOUBLE: + return "DOUBLE"; + case MYSQL_TYPE_NEWDECIMAL: + return "DECIMAL"; + + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + return (flags & BINARY_FLAG) ? "VARBINARY" : "VARCHAR"; + + case MYSQL_TYPE_STRING: + return (flags & BINARY_FLAG) ? "BINARY" : "CHAR"; + + case MYSQL_TYPE_BLOB: + return (flags & BINARY_FLAG) ? "BLOB" : "TEXT"; + + case MYSQL_TYPE_DATE: + return "DATE"; + case MYSQL_TYPE_TIME: + return "TIME"; + case MYSQL_TYPE_DATETIME: + return "DATETIME"; + case MYSQL_TYPE_TIMESTAMP: + return "TIMESTAMP"; + case MYSQL_TYPE_JSON: + return "JSON"; + + default: + return "UNKNOWN"; + } +} + class MysqlResultImpl : public ResultImpl { public: @@ -39,20 +149,44 @@ class MysqlResultImpl : public ResultImpl affectedRows_(affectedRows), insertId_(insertId) { - if (fieldsNumber_ > 0) + if (fieldArray_ && fieldsNumber_ > 0) { + columnMeta_.resize(fieldsNumber_); + fieldsMapPtr_ = std::make_shared< std::unordered_map>(); - for (RowSizeType i = 0; i < fieldsNumber_; ++i) + fieldsMapPtr_->reserve(fieldsNumber_); + + for (unsigned int i = 0; i < fieldsNumber_; ++i) { - std::string fieldName = fieldArray_[i].name; + const MYSQL_FIELD &f = fieldArray_[i]; + auto &meta = columnMeta_[i]; + + meta.sqlType = mysqlTypeToSql(f.type, f.flags); + + const char *typeName = mysqlFieldTypeToName(f.type, f.flags); + meta.typeName = typeName ? typeName : "UNKNOWN"; + + meta.length = static_cast(f.length); + + meta.precision = (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.length) + : 0; + + meta.scale = (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.decimals) + : 0; + + std::string fieldName = f.name; std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), - [](unsigned char c) { return tolower(c); }); + [](unsigned char c) { return std::tolower(c); }); + (*fieldsMapPtr_)[fieldName] = i; } } + if (size() > 0) { rowsPtr_ = std::make_shared< @@ -80,11 +214,13 @@ class MysqlResultImpl : public ResultImpl bool isNull(SizeType row, RowSizeType column) const override; FieldSizeType getLength(SizeType row, RowSizeType column) const override; unsigned long long insertId() const noexcept override; + const ColumnMeta &columnMeta(SizeType column) const override; private: const std::shared_ptr result_; const Result::SizeType rowsNumber_; const MYSQL_FIELD *fieldArray_; + std::vector columnMeta_; const Result::RowSizeType fieldsNumber_; const SizeType affectedRows_; const unsigned long long insertId_; From b6bf135eaa6d7d827f5fdc5f4aacdc4bb4a4703f Mon Sep 17 00:00:00 2001 From: Karthikeyan Muthusamy Date: Sun, 13 Sep 2026 01:03:47 +0530 Subject: [PATCH 2/7] MyResultImpl.h updated --- orm_lib/src/mysql_impl/MysqlResultImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 057a9c5219..70bfd336d7 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -157,7 +157,7 @@ class MysqlResultImpl : public ResultImpl std::unordered_map>(); fieldsMapPtr_->reserve(fieldsNumber_); - for (unsigned int i = 0; i < fieldsNumber_; ++i) + for (RowSizeType i = 0; i < fieldsNumber_; ++i) { const MYSQL_FIELD &f = fieldArray_[i]; auto &meta = columnMeta_[i]; From 948d899f1e69f27afd614306915168a9205aad52 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 14 Sep 2026 16:37:57 +0530 Subject: [PATCH 3/7] Added comprehensive MySQL field type mapping and type-name conversion, including numeric, text, binary, date/time, JSON, ENUM/SET, BOOLEAN, and GEOMETRY types. --- orm_lib/inc/drogon/orm/Result.h | 39 ++++++- orm_lib/src/mysql_impl/MysqlResultImpl.h | 133 ++++++++++++++++++++--- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 3ac6620f32..841056330f 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -37,20 +37,51 @@ using ResultImplPtr = std::shared_ptr; enum class SqlFieldType : uint8_t { Unknown = 0, + + // Numeric Bool, + TinyInt, + SmallInt, + MediumInt, Int, BigInt, + Bit, Float, Double, Decimal, - Varchar, + + // Character + VarChar, + Char, + TinyText, Text, + MediumText, + LongText, + + // Binary + Binary, + VarBinary, + TinyBlob, + Blob, + MediumBlob, + LongBlob, + + // Other + Json, + + // Date / Time Date, Time, + Year, DateTime, - Blob, - Json, - Binary + Timestamp, + + // Spatial + Geometry, + + // MySQL-specific + Enum, + Set }; enum class SqlStatus diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 70bfd336d7..d1f731afca 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -32,59 +32,105 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) switch (t) { case MYSQL_TYPE_TINY: + return SqlFieldType::TinyInt; + +#ifdef MYSQL_TYPE_BOOL + case MYSQL_TYPE_BOOL: + return SqlFieldType::Bool; +#endif + case MYSQL_TYPE_SHORT: + return SqlFieldType::SmallInt; + + case MYSQL_TYPE_INT24: + return SqlFieldType::MediumInt; + case MYSQL_TYPE_LONG: return SqlFieldType::Int; case MYSQL_TYPE_LONGLONG: return SqlFieldType::BigInt; + case MYSQL_TYPE_BIT: + return SqlFieldType::Bit; + case MYSQL_TYPE_FLOAT: return SqlFieldType::Float; case MYSQL_TYPE_DOUBLE: return SqlFieldType::Double; + case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return SqlFieldType::Decimal; - case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: - if (flags & BINARY_FLAG) - return SqlFieldType::Binary; - return SqlFieldType::Varchar; + case MYSQL_TYPE_VAR_STRING: + return (flags & BINARY_FLAG) ? SqlFieldType::VarBinary + : SqlFieldType::VarChar; case MYSQL_TYPE_STRING: - if (flags & BINARY_FLAG) - return SqlFieldType::Binary; - return SqlFieldType::Varchar; + return (flags & BINARY_FLAG) ? SqlFieldType::Binary + : SqlFieldType::Char; case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::TinyBlob + : SqlFieldType::TinyText; + case MYSQL_TYPE_BLOB: - return SqlFieldType::Blob; + return (flags & BINARY_FLAG) ? SqlFieldType::Blob + : SqlFieldType::Text; - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - return SqlFieldType::Varchar; + case MYSQL_TYPE_MEDIUM_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::MediumBlob + : SqlFieldType::MediumText; - case MYSQL_TYPE_BIT: - return SqlFieldType::Bool; + case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::LongBlob + : SqlFieldType::LongText; case MYSQL_TYPE_DATE: return SqlFieldType::Date; case MYSQL_TYPE_TIME: +#ifdef MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: +#endif return SqlFieldType::Time; + case MYSQL_TYPE_YEAR: + return SqlFieldType::Year; + case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: +#endif return SqlFieldType::DateTime; + case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: +#endif + return SqlFieldType::Timestamp; + case MYSQL_TYPE_JSON: return SqlFieldType::Json; + case MYSQL_TYPE_ENUM: + return SqlFieldType::Enum; + + case MYSQL_TYPE_SET: + return SqlFieldType::Set; + + /** --------------------------------------------------------- + * Spatial + * + * POINT, LINESTRING, POLYGON, MULTI* and GEOMETRYCOLLECTION + * are all exposed through MYSQL_TYPE_GEOMETRY. + */ + case MYSQL_TYPE_GEOMETRY: + return SqlFieldType::Geometry; + default: return SqlFieldType::Unknown; } @@ -97,16 +143,34 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, { case MYSQL_TYPE_TINY: return "TINYINT"; + +#ifdef MYSQL_TYPE_BOOL + case MYSQL_TYPE_BOOL: + return "BOOLEAN"; +#endif + case MYSQL_TYPE_SHORT: return "SMALLINT"; + + case MYSQL_TYPE_INT24: + return "MEDIUMINT"; + case MYSQL_TYPE_LONG: return "INT"; + case MYSQL_TYPE_LONGLONG: return "BIGINT"; + + case MYSQL_TYPE_BIT: + return "BIT"; + case MYSQL_TYPE_FLOAT: return "FLOAT"; + case MYSQL_TYPE_DOUBLE: return "DOUBLE"; + + case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return "DECIMAL"; @@ -117,20 +181,57 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, case MYSQL_TYPE_STRING: return (flags & BINARY_FLAG) ? "BINARY" : "CHAR"; + case MYSQL_TYPE_TINY_BLOB: + return (flags & BINARY_FLAG) ? "TINYBLOB" : "TINYTEXT"; + case MYSQL_TYPE_BLOB: return (flags & BINARY_FLAG) ? "BLOB" : "TEXT"; + case MYSQL_TYPE_MEDIUM_BLOB: + return (flags & BINARY_FLAG) ? "MEDIUMBLOB" : "MEDIUMTEXT"; + + case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? "LONGBLOB" : "LONGTEXT"; + case MYSQL_TYPE_DATE: return "DATE"; + case MYSQL_TYPE_TIME: +#ifdef MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: +#endif return "TIME"; + + case MYSQL_TYPE_YEAR: + return "YEAR"; + case MYSQL_TYPE_DATETIME: +#ifdef MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: +#endif return "DATETIME"; + case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: +#endif return "TIMESTAMP"; + case MYSQL_TYPE_JSON: return "JSON"; + case MYSQL_TYPE_ENUM: + return "ENUM"; + + case MYSQL_TYPE_SET: + return "SET"; + + case MYSQL_TYPE_GEOMETRY: + return "GEOMETRY"; + + case MYSQL_TYPE_NULL: + return "NULL"; + default: return "UNKNOWN"; } From abe95492fc36b2077d2df906011ec62efaf8684d Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 14 Sep 2026 16:41:17 +0530 Subject: [PATCH 4/7] Revert "Added comprehensive MySQL field type mapping and type-name conversion, including numeric, text, binary, date/time, JSON, ENUM/SET, BOOLEAN, and GEOMETRY types." This reverts commit 948d899f1e69f27afd614306915168a9205aad52. --- orm_lib/inc/drogon/orm/Result.h | 39 +------ orm_lib/src/mysql_impl/MysqlResultImpl.h | 133 +++-------------------- 2 files changed, 20 insertions(+), 152 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 841056330f..3ac6620f32 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -37,51 +37,20 @@ using ResultImplPtr = std::shared_ptr; enum class SqlFieldType : uint8_t { Unknown = 0, - - // Numeric Bool, - TinyInt, - SmallInt, - MediumInt, Int, BigInt, - Bit, Float, Double, Decimal, - - // Character - VarChar, - Char, - TinyText, + Varchar, Text, - MediumText, - LongText, - - // Binary - Binary, - VarBinary, - TinyBlob, - Blob, - MediumBlob, - LongBlob, - - // Other - Json, - - // Date / Time Date, Time, - Year, DateTime, - Timestamp, - - // Spatial - Geometry, - - // MySQL-specific - Enum, - Set + Blob, + Json, + Binary }; enum class SqlStatus diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index d1f731afca..70bfd336d7 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -32,105 +32,59 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) switch (t) { case MYSQL_TYPE_TINY: - return SqlFieldType::TinyInt; - -#ifdef MYSQL_TYPE_BOOL - case MYSQL_TYPE_BOOL: - return SqlFieldType::Bool; -#endif - case MYSQL_TYPE_SHORT: - return SqlFieldType::SmallInt; - - case MYSQL_TYPE_INT24: - return SqlFieldType::MediumInt; - case MYSQL_TYPE_LONG: return SqlFieldType::Int; case MYSQL_TYPE_LONGLONG: return SqlFieldType::BigInt; - case MYSQL_TYPE_BIT: - return SqlFieldType::Bit; - case MYSQL_TYPE_FLOAT: return SqlFieldType::Float; case MYSQL_TYPE_DOUBLE: return SqlFieldType::Double; - case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return SqlFieldType::Decimal; - case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: - return (flags & BINARY_FLAG) ? SqlFieldType::VarBinary - : SqlFieldType::VarChar; + case MYSQL_TYPE_VARCHAR: + if (flags & BINARY_FLAG) + return SqlFieldType::Binary; + return SqlFieldType::Varchar; case MYSQL_TYPE_STRING: - return (flags & BINARY_FLAG) ? SqlFieldType::Binary - : SqlFieldType::Char; + if (flags & BINARY_FLAG) + return SqlFieldType::Binary; + return SqlFieldType::Varchar; case MYSQL_TYPE_TINY_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::TinyBlob - : SqlFieldType::TinyText; - + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::Blob - : SqlFieldType::Text; + return SqlFieldType::Blob; - case MYSQL_TYPE_MEDIUM_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::MediumBlob - : SqlFieldType::MediumText; + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + return SqlFieldType::Varchar; - case MYSQL_TYPE_LONG_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::LongBlob - : SqlFieldType::LongText; + case MYSQL_TYPE_BIT: + return SqlFieldType::Bool; case MYSQL_TYPE_DATE: return SqlFieldType::Date; case MYSQL_TYPE_TIME: -#ifdef MYSQL_TYPE_TIME2 - case MYSQL_TYPE_TIME2: -#endif return SqlFieldType::Time; - case MYSQL_TYPE_YEAR: - return SqlFieldType::Year; - case MYSQL_TYPE_DATETIME: -#ifdef MYSQL_TYPE_DATETIME2 - case MYSQL_TYPE_DATETIME2: -#endif - return SqlFieldType::DateTime; - case MYSQL_TYPE_TIMESTAMP: -#ifdef MYSQL_TYPE_TIMESTAMP2 - case MYSQL_TYPE_TIMESTAMP2: -#endif - return SqlFieldType::Timestamp; + return SqlFieldType::DateTime; case MYSQL_TYPE_JSON: return SqlFieldType::Json; - case MYSQL_TYPE_ENUM: - return SqlFieldType::Enum; - - case MYSQL_TYPE_SET: - return SqlFieldType::Set; - - /** --------------------------------------------------------- - * Spatial - * - * POINT, LINESTRING, POLYGON, MULTI* and GEOMETRYCOLLECTION - * are all exposed through MYSQL_TYPE_GEOMETRY. - */ - case MYSQL_TYPE_GEOMETRY: - return SqlFieldType::Geometry; - default: return SqlFieldType::Unknown; } @@ -143,34 +97,16 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, { case MYSQL_TYPE_TINY: return "TINYINT"; - -#ifdef MYSQL_TYPE_BOOL - case MYSQL_TYPE_BOOL: - return "BOOLEAN"; -#endif - case MYSQL_TYPE_SHORT: return "SMALLINT"; - - case MYSQL_TYPE_INT24: - return "MEDIUMINT"; - case MYSQL_TYPE_LONG: return "INT"; - case MYSQL_TYPE_LONGLONG: return "BIGINT"; - - case MYSQL_TYPE_BIT: - return "BIT"; - case MYSQL_TYPE_FLOAT: return "FLOAT"; - case MYSQL_TYPE_DOUBLE: return "DOUBLE"; - - case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return "DECIMAL"; @@ -181,57 +117,20 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, case MYSQL_TYPE_STRING: return (flags & BINARY_FLAG) ? "BINARY" : "CHAR"; - case MYSQL_TYPE_TINY_BLOB: - return (flags & BINARY_FLAG) ? "TINYBLOB" : "TINYTEXT"; - case MYSQL_TYPE_BLOB: return (flags & BINARY_FLAG) ? "BLOB" : "TEXT"; - case MYSQL_TYPE_MEDIUM_BLOB: - return (flags & BINARY_FLAG) ? "MEDIUMBLOB" : "MEDIUMTEXT"; - - case MYSQL_TYPE_LONG_BLOB: - return (flags & BINARY_FLAG) ? "LONGBLOB" : "LONGTEXT"; - case MYSQL_TYPE_DATE: return "DATE"; - case MYSQL_TYPE_TIME: -#ifdef MYSQL_TYPE_TIME2 - case MYSQL_TYPE_TIME2: -#endif return "TIME"; - - case MYSQL_TYPE_YEAR: - return "YEAR"; - case MYSQL_TYPE_DATETIME: -#ifdef MYSQL_TYPE_DATETIME2 - case MYSQL_TYPE_DATETIME2: -#endif return "DATETIME"; - case MYSQL_TYPE_TIMESTAMP: -#ifdef MYSQL_TYPE_TIMESTAMP2 - case MYSQL_TYPE_TIMESTAMP2: -#endif return "TIMESTAMP"; - case MYSQL_TYPE_JSON: return "JSON"; - case MYSQL_TYPE_ENUM: - return "ENUM"; - - case MYSQL_TYPE_SET: - return "SET"; - - case MYSQL_TYPE_GEOMETRY: - return "GEOMETRY"; - - case MYSQL_TYPE_NULL: - return "NULL"; - default: return "UNKNOWN"; } From e5b8afd8c47198f58f92f21bf1f4d1c410638342 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 14 Sep 2026 16:45:41 +0530 Subject: [PATCH 5/7] Added comprehensive MySQL field type mapping and type-name conversion, including numeric, text, binary, date/time, JSON, ENUM/SET, BOOLEAN, and GEOMETRY types. --- orm_lib/inc/drogon/orm/Result.h | 39 ++++++- orm_lib/src/mysql_impl/MysqlResultImpl.h | 133 ++++++++++++++++++++--- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 3ac6620f32..841056330f 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -37,20 +37,51 @@ using ResultImplPtr = std::shared_ptr; enum class SqlFieldType : uint8_t { Unknown = 0, + + // Numeric Bool, + TinyInt, + SmallInt, + MediumInt, Int, BigInt, + Bit, Float, Double, Decimal, - Varchar, + + // Character + VarChar, + Char, + TinyText, Text, + MediumText, + LongText, + + // Binary + Binary, + VarBinary, + TinyBlob, + Blob, + MediumBlob, + LongBlob, + + // Other + Json, + + // Date / Time Date, Time, + Year, DateTime, - Blob, - Json, - Binary + Timestamp, + + // Spatial + Geometry, + + // MySQL-specific + Enum, + Set }; enum class SqlStatus diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 70bfd336d7..d1f731afca 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -32,59 +32,105 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) switch (t) { case MYSQL_TYPE_TINY: + return SqlFieldType::TinyInt; + +#ifdef MYSQL_TYPE_BOOL + case MYSQL_TYPE_BOOL: + return SqlFieldType::Bool; +#endif + case MYSQL_TYPE_SHORT: + return SqlFieldType::SmallInt; + + case MYSQL_TYPE_INT24: + return SqlFieldType::MediumInt; + case MYSQL_TYPE_LONG: return SqlFieldType::Int; case MYSQL_TYPE_LONGLONG: return SqlFieldType::BigInt; + case MYSQL_TYPE_BIT: + return SqlFieldType::Bit; + case MYSQL_TYPE_FLOAT: return SqlFieldType::Float; case MYSQL_TYPE_DOUBLE: return SqlFieldType::Double; + case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return SqlFieldType::Decimal; - case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: - if (flags & BINARY_FLAG) - return SqlFieldType::Binary; - return SqlFieldType::Varchar; + case MYSQL_TYPE_VAR_STRING: + return (flags & BINARY_FLAG) ? SqlFieldType::VarBinary + : SqlFieldType::VarChar; case MYSQL_TYPE_STRING: - if (flags & BINARY_FLAG) - return SqlFieldType::Binary; - return SqlFieldType::Varchar; + return (flags & BINARY_FLAG) ? SqlFieldType::Binary + : SqlFieldType::Char; case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::TinyBlob + : SqlFieldType::TinyText; + case MYSQL_TYPE_BLOB: - return SqlFieldType::Blob; + return (flags & BINARY_FLAG) ? SqlFieldType::Blob + : SqlFieldType::Text; - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - return SqlFieldType::Varchar; + case MYSQL_TYPE_MEDIUM_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::MediumBlob + : SqlFieldType::MediumText; - case MYSQL_TYPE_BIT: - return SqlFieldType::Bool; + case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? SqlFieldType::LongBlob + : SqlFieldType::LongText; case MYSQL_TYPE_DATE: return SqlFieldType::Date; case MYSQL_TYPE_TIME: +#ifdef MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: +#endif return SqlFieldType::Time; + case MYSQL_TYPE_YEAR: + return SqlFieldType::Year; + case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: +#endif return SqlFieldType::DateTime; + case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: +#endif + return SqlFieldType::Timestamp; + case MYSQL_TYPE_JSON: return SqlFieldType::Json; + case MYSQL_TYPE_ENUM: + return SqlFieldType::Enum; + + case MYSQL_TYPE_SET: + return SqlFieldType::Set; + + /** --------------------------------------------------------- + * Spatial + * + * POINT, LINESTRING, POLYGON, MULTI* and GEOMETRYCOLLECTION + * are all exposed through MYSQL_TYPE_GEOMETRY. + */ + case MYSQL_TYPE_GEOMETRY: + return SqlFieldType::Geometry; + default: return SqlFieldType::Unknown; } @@ -97,16 +143,34 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, { case MYSQL_TYPE_TINY: return "TINYINT"; + +#ifdef MYSQL_TYPE_BOOL + case MYSQL_TYPE_BOOL: + return "BOOLEAN"; +#endif + case MYSQL_TYPE_SHORT: return "SMALLINT"; + + case MYSQL_TYPE_INT24: + return "MEDIUMINT"; + case MYSQL_TYPE_LONG: return "INT"; + case MYSQL_TYPE_LONGLONG: return "BIGINT"; + + case MYSQL_TYPE_BIT: + return "BIT"; + case MYSQL_TYPE_FLOAT: return "FLOAT"; + case MYSQL_TYPE_DOUBLE: return "DOUBLE"; + + case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return "DECIMAL"; @@ -117,20 +181,57 @@ inline const char *mysqlFieldTypeToName(enum enum_field_types t, case MYSQL_TYPE_STRING: return (flags & BINARY_FLAG) ? "BINARY" : "CHAR"; + case MYSQL_TYPE_TINY_BLOB: + return (flags & BINARY_FLAG) ? "TINYBLOB" : "TINYTEXT"; + case MYSQL_TYPE_BLOB: return (flags & BINARY_FLAG) ? "BLOB" : "TEXT"; + case MYSQL_TYPE_MEDIUM_BLOB: + return (flags & BINARY_FLAG) ? "MEDIUMBLOB" : "MEDIUMTEXT"; + + case MYSQL_TYPE_LONG_BLOB: + return (flags & BINARY_FLAG) ? "LONGBLOB" : "LONGTEXT"; + case MYSQL_TYPE_DATE: return "DATE"; + case MYSQL_TYPE_TIME: +#ifdef MYSQL_TYPE_TIME2 + case MYSQL_TYPE_TIME2: +#endif return "TIME"; + + case MYSQL_TYPE_YEAR: + return "YEAR"; + case MYSQL_TYPE_DATETIME: +#ifdef MYSQL_TYPE_DATETIME2 + case MYSQL_TYPE_DATETIME2: +#endif return "DATETIME"; + case MYSQL_TYPE_TIMESTAMP: +#ifdef MYSQL_TYPE_TIMESTAMP2 + case MYSQL_TYPE_TIMESTAMP2: +#endif return "TIMESTAMP"; + case MYSQL_TYPE_JSON: return "JSON"; + case MYSQL_TYPE_ENUM: + return "ENUM"; + + case MYSQL_TYPE_SET: + return "SET"; + + case MYSQL_TYPE_GEOMETRY: + return "GEOMETRY"; + + case MYSQL_TYPE_NULL: + return "NULL"; + default: return "UNKNOWN"; } From 0cf227d48f2253afef0ced1fe9f81ef083deaaed Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Wed, 16 Sep 2026 19:35:37 +0530 Subject: [PATCH 6/7] Add native and logical type mapping, column attributes, DECIMAL precision/scale handling, and isNullable() / isUnsigned() accessors. --- orm_lib/inc/drogon/orm/Field.h | 20 ++- orm_lib/inc/drogon/orm/Result.h | 128 ++++++++++++------- orm_lib/src/Result.cc | 24 +++- orm_lib/src/mysql_impl/MysqlResultImpl.h | 156 ++++++++++++----------- 4 files changed, 196 insertions(+), 132 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index e682636fb9..450b25e50a 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -66,15 +66,15 @@ class DROGON_EXPORT Field return result_.getLength(row_, column_); } - SqlFieldType sqlType() const noexcept + SqlType sqlType() const noexcept { return result_.getSqlType(column_); } - /// SQL type name (VARCHAR, INT, DECIMAL, etc.) - const std::string &typeName() const noexcept + /// Native database type name (VARCHAR, INT, DECIMAL, TINYINT, BIT, etc.) + const std::string &nativeTypeName() const noexcept { - return result_.getTypeName(column_); + return result_.getNativeTypeName(column_); } /// Character length (VARCHAR) @@ -95,6 +95,18 @@ class DROGON_EXPORT Field return result_.getScale(column_); } + /// Is this column nullable? + bool isNullable() const noexcept + { + return result_.isNullable(column_); + } + + /// Is this column unsigned? + bool isUnsigned() const noexcept + { + return result_.isUnsigned(column_); + } + /// Convert to a type T value template T as() const diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 841056330f..ea443a6133 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace drogon { @@ -34,69 +35,84 @@ class Row; class ResultImpl; using ResultImplPtr = std::shared_ptr; -enum class SqlFieldType : uint8_t +enum class SqlType : uint8_t { Unknown = 0, - // Numeric Bool, - TinyInt, - SmallInt, - MediumInt, - Int, - BigInt, + Integer, Bit, Float, Double, Decimal, - // Character - VarChar, - Char, - TinyText, - Text, - MediumText, - LongText, - - // Binary + String, Binary, - VarBinary, - TinyBlob, - Blob, - MediumBlob, - LongBlob, - // Other Json, - // Date / Time Date, Time, Year, DateTime, Timestamp, - // Spatial - Geometry, - - // MySQL-specific - Enum, - Set + Geometry }; +// Kept for source compatibility with the original metadata API. +using SqlFieldType = SqlType; + enum class SqlStatus { Ok, End }; +/** + * @brief Column metadata from the database. + * + * Separates: + * - Logical type (type): Application-level abstraction for type handling + * - Native type (nativeType): Actual database type name (VARCHAR, DECIMAL, + * etc.) + * - Type attributes: length, precision, scale, unsigned, nullable + * + * Examples: + * DECIMAL(10,2) + * type = Decimal, nativeType = "DECIMAL", precision = 10, scale = 2 + * + * VARCHAR(100) + * type = VarChar, nativeType = "VARCHAR", length = 100 + * + * BIT(1) + * type = Bool, nativeType = "BIT", length = 1 + * + * TINYINT(1) + * type = Bool, nativeType = "TINYINT", length = 1 + * + * BIT(8) + * type = Bit, nativeType = "BIT", length = 8 + */ struct ColumnMeta { - SqlFieldType sqlType{SqlFieldType::Unknown}; - std::string typeName; - int length{0}; - int precision{0}; - int scale{0}; + // Logical type. + SqlType type{SqlType::Unknown}; + + // Database-specific native type name. + std::string nativeType; + + // Type-specific attributes. + // length is type-dependent: character and binary types use + // driver-reported byte width, while BIT uses number of bits. + // It is not decimal precision; DECIMAL uses precision and scale. + std::optional length; + + std::optional precision; + std::optional scale; + + bool nullable{false}; + bool unsigned_{false}; }; /// Result set containing data returned by a query or command. @@ -204,37 +220,37 @@ class DROGON_EXPORT Result * @param column Zero-based index of the column (must be less than * columns()). * @return The abstracted SQL field type for the given column, or - * SqlFieldType::Unknown if the type cannot be determined. + * SqlType::Unknown if the type cannot be determined. * * @note Type metadata may only be fully populated for some database * backends (for example, MySQL). For other backends, the result - * may be limited or fall back to SqlFieldType::Unknown. + * may be limited or fall back to SqlType::Unknown. */ - SqlFieldType getSqlType(SizeType column) const; + SqlType getSqlType(SizeType column) const; /** - * @brief Get the database-specific type name of the specified column. + * @brief Get the native database type name of the specified column. * * @param column Zero-based index of the column (must be less than * columns()). - * @return A reference to a string containing the type name as reported - * by the underlying database driver (for example, "INT", - * "VARCHAR", "DECIMAL(10,2)", etc.). + * @return A reference to the native type name reported by the underlying + * database driver, for example, "INT", "VARCHAR", or "DECIMAL". * * @note Type-name metadata may only be fully populated for some database - * backends (for example, MySQL). On other backends, the returned - * string may be empty or use a backend-specific representation. + * backends. On other backends, the returned string may be empty or + * use a backend-specific representation. */ - const std::string &getTypeName(SizeType column) const; + const std::string &getNativeTypeName(SizeType column) const; /** * @brief Get the defined maximum length of the specified column. * * @param column Zero-based index of the column (must be less than * columns()). - * @return The maximum length for the column in characters or bytes, as - * reported by the underlying database driver, or 0 if this - * information is not available. + * @return The type-specific length, or 0 if unavailable. For MySQL, + * VARCHAR, CHAR, and BLOB-family values are reported in bytes + * (not characters), while BIT is reported in bits. It is not + * populated for DECIMAL; use getPrecision() and getScale(). * * @note Length metadata may only be populated for some database backends * (for example, MySQL) and for certain column types (such as @@ -274,6 +290,24 @@ class DROGON_EXPORT Result */ int getScale(SizeType column) const; + /** + * @brief Check if the specified column is nullable. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return True if the column is nullable, false otherwise. + */ + bool isNullable(SizeType column) const; + + /** + * @brief Check if the specified column is unsigned. + * + * @param column Zero-based index of the column (must be less than + * columns()). + * @return True if the column is unsigned, false otherwise. + */ + bool isUnsigned(SizeType column) const; + #ifdef _MSC_VER Result() noexcept = default; #endif diff --git a/orm_lib/src/Result.cc b/orm_lib/src/Result.cc index 309884f90e..60928291a6 100644 --- a/orm_lib/src/Result.cc +++ b/orm_lib/src/Result.cc @@ -170,29 +170,39 @@ unsigned long long Result::insertId() const noexcept return resultPtr_->insertId(); } -SqlFieldType Result::getSqlType(SizeType column) const +SqlType Result::getSqlType(SizeType column) const { - return resultPtr_->columnMeta(column).sqlType; + return resultPtr_->columnMeta(column).type; } -const std::string &Result::getTypeName(SizeType column) const +const std::string &Result::getNativeTypeName(SizeType column) const { - return resultPtr_->columnMeta(column).typeName; + return resultPtr_->columnMeta(column).nativeType; } int Result::getColumnLength(SizeType column) const { - return resultPtr_->columnMeta(column).length; + return resultPtr_->columnMeta(column).length.value_or(0); } int Result::getPrecision(SizeType column) const { - return resultPtr_->columnMeta(column).precision; + return resultPtr_->columnMeta(column).precision.value_or(0); } int Result::getScale(SizeType column) const { - return resultPtr_->columnMeta(column).scale; + return resultPtr_->columnMeta(column).scale.value_or(0); +} + +bool Result::isNullable(SizeType column) const +{ + return resultPtr_->columnMeta(column).nullable; +} + +bool Result::isUnsigned(SizeType column) const +{ + return resultPtr_->columnMeta(column).unsigned_; } int Result::oid(RowSizeType column) const noexcept diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index d1f731afca..50d6e1500a 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -27,128 +27,99 @@ namespace drogon namespace orm { -inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) +inline SqlType mysqlTypeToSql(enum enum_field_types type, + unsigned int flags, + unsigned int length) { - switch (t) + switch (type) { case MYSQL_TYPE_TINY: - return SqlFieldType::TinyInt; - -#ifdef MYSQL_TYPE_BOOL - case MYSQL_TYPE_BOOL: - return SqlFieldType::Bool; -#endif + // MySQL represents BOOL/BOOLEAN and TINYINT(1) as MYSQL_TYPE_TINY. + // The client protocol does not preserve the original declaration, + // so length == 1 is treated as Bool by convention. + return length == 1 ? SqlType::Bool : SqlType::Integer; case MYSQL_TYPE_SHORT: - return SqlFieldType::SmallInt; - case MYSQL_TYPE_INT24: - return SqlFieldType::MediumInt; - case MYSQL_TYPE_LONG: - return SqlFieldType::Int; - case MYSQL_TYPE_LONGLONG: - return SqlFieldType::BigInt; + return SqlType::Integer; case MYSQL_TYPE_BIT: - return SqlFieldType::Bit; + // BIT(1) is commonly used as a boolean value. + // BIT(n > 1) remains a Bit type. + return length == 1 ? SqlType::Bool : SqlType::Bit; case MYSQL_TYPE_FLOAT: - return SqlFieldType::Float; + return SqlType::Float; case MYSQL_TYPE_DOUBLE: - return SqlFieldType::Double; + return SqlType::Double; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - return SqlFieldType::Decimal; + return SqlType::Decimal; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: - return (flags & BINARY_FLAG) ? SqlFieldType::VarBinary - : SqlFieldType::VarChar; - case MYSQL_TYPE_STRING: - return (flags & BINARY_FLAG) ? SqlFieldType::Binary - : SqlFieldType::Char; + return (flags & BINARY_FLAG) ? SqlType::Binary : SqlType::String; case MYSQL_TYPE_TINY_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::TinyBlob - : SqlFieldType::TinyText; - case MYSQL_TYPE_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::Blob - : SqlFieldType::Text; - case MYSQL_TYPE_MEDIUM_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::MediumBlob - : SqlFieldType::MediumText; - case MYSQL_TYPE_LONG_BLOB: - return (flags & BINARY_FLAG) ? SqlFieldType::LongBlob - : SqlFieldType::LongText; + return (flags & BINARY_FLAG) ? SqlType::Binary : SqlType::String; case MYSQL_TYPE_DATE: - return SqlFieldType::Date; + return SqlType::Date; case MYSQL_TYPE_TIME: #ifdef MYSQL_TYPE_TIME2 case MYSQL_TYPE_TIME2: #endif - return SqlFieldType::Time; + return SqlType::Time; case MYSQL_TYPE_YEAR: - return SqlFieldType::Year; + return SqlType::Year; case MYSQL_TYPE_DATETIME: #ifdef MYSQL_TYPE_DATETIME2 case MYSQL_TYPE_DATETIME2: #endif - return SqlFieldType::DateTime; + return SqlType::DateTime; case MYSQL_TYPE_TIMESTAMP: #ifdef MYSQL_TYPE_TIMESTAMP2 case MYSQL_TYPE_TIMESTAMP2: #endif - return SqlFieldType::Timestamp; + return SqlType::Timestamp; case MYSQL_TYPE_JSON: - return SqlFieldType::Json; + return SqlType::Json; case MYSQL_TYPE_ENUM: - return SqlFieldType::Enum; - case MYSQL_TYPE_SET: - return SqlFieldType::Set; - - /** --------------------------------------------------------- - * Spatial - * - * POINT, LINESTRING, POLYGON, MULTI* and GEOMETRYCOLLECTION - * are all exposed through MYSQL_TYPE_GEOMETRY. - */ + // ENUM/SET are represented as strings at the logical layer. + // The exact MySQL type is preserved by mysqlFieldTypeToName(). + return SqlType::String; + case MYSQL_TYPE_GEOMETRY: - return SqlFieldType::Geometry; + return SqlType::Geometry; default: - return SqlFieldType::Unknown; + return SqlType::Unknown; } } -inline const char *mysqlFieldTypeToName(enum enum_field_types t, +inline const char *mysqlFieldTypeToName(enum enum_field_types type, unsigned int flags) { - switch (t) + switch (type) { case MYSQL_TYPE_TINY: return "TINYINT"; -#ifdef MYSQL_TYPE_BOOL - case MYSQL_TYPE_BOOL: - return "BOOLEAN"; -#endif - case MYSQL_TYPE_SHORT: return "SMALLINT"; @@ -262,27 +233,64 @@ class MysqlResultImpl : public ResultImpl { const MYSQL_FIELD &f = fieldArray_[i]; auto &meta = columnMeta_[i]; + meta.type = mysqlTypeToSql(f.type, f.flags, f.length); - meta.sqlType = mysqlTypeToSql(f.type, f.flags); - + // Store native type name const char *typeName = mysqlFieldTypeToName(f.type, f.flags); - meta.typeName = typeName ? typeName : "UNKNOWN"; - - meta.length = static_cast(f.length); - - meta.precision = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.length) - : 0; - - meta.scale = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.decimals) - : 0; + meta.nativeType = typeName ? typeName : "UNKNOWN"; + + // Extract type attributes based on native type + meta.nullable = !(f.flags & NOT_NULL_FLAG); + meta.unsigned_ = (f.flags & UNSIGNED_FLAG) != 0; + + // MYSQL_FIELD::length is not a universal unit. For strings + // and blobs it is the driver's maximum byte width; for BIT + // it is the declared number of bits. + if (f.type == MYSQL_TYPE_VARCHAR || + f.type == MYSQL_TYPE_VAR_STRING || + f.type == MYSQL_TYPE_STRING || f.type == MYSQL_TYPE_BLOB || + f.type == MYSQL_TYPE_TINY_BLOB || + f.type == MYSQL_TYPE_MEDIUM_BLOB || + f.type == MYSQL_TYPE_LONG_BLOB || f.type == MYSQL_TYPE_BIT) + { + meta.length = static_cast(f.length); + } + + // DECIMAL metadata is represented in the MySQL result metadata + // as display width and scale. MYSQL_FIELD::length includes + // formatting overhead such as the sign position for signed + // values and the decimal point when scale is non-zero. + // + // Therefore, precision is inferred by removing these formatting + // characters from the display width. The scale is taken + // directly from MYSQL_FIELD::decimals. + // + // Note: For result expressions or derived columns, this + // represents the precision inferred from result metadata and + // may not correspond to an explicitly declared DECIMAL(M,D) + // schema definition. + if (f.type == MYSQL_TYPE_DECIMAL || + f.type == MYSQL_TYPE_NEWDECIMAL) + { + meta.scale = static_cast(f.decimals); + const unsigned int displayWidth = f.length; + const unsigned int signWidth = + (f.flags & UNSIGNED_FLAG) ? 0U : 1U; + const unsigned int decimalPointWidth = + f.decimals > 0 ? 1U : 0U; + const unsigned int overhead = signWidth + decimalPointWidth; + if (displayWidth >= overhead + f.decimals) + { + meta.precision = + static_cast(displayWidth - overhead); + } + } std::string fieldName = f.name; std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), - [](unsigned char c) { return std::tolower(c); }); + [](unsigned char c) { return tolower(c); }); (*fieldsMapPtr_)[fieldName] = i; } From 34f7db1b75a2bd56a990416d0e856289534e3bce Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Wed, 16 Sep 2026 19:56:34 +0530 Subject: [PATCH 7/7] Small changes --- orm_lib/inc/drogon/orm/Result.h | 5 +---- orm_lib/src/mysql_impl/MysqlResultImpl.h | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index ea443a6133..013f6ca6e6 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -40,7 +40,7 @@ enum class SqlType : uint8_t Unknown = 0, Bool, - Integer, + Int, Bit, Float, Double, @@ -60,9 +60,6 @@ enum class SqlType : uint8_t Geometry }; -// Kept for source compatibility with the original metadata API. -using SqlFieldType = SqlType; - enum class SqlStatus { Ok, diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 50d6e1500a..221ca652f3 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -37,13 +37,13 @@ inline SqlType mysqlTypeToSql(enum enum_field_types type, // MySQL represents BOOL/BOOLEAN and TINYINT(1) as MYSQL_TYPE_TINY. // The client protocol does not preserve the original declaration, // so length == 1 is treated as Bool by convention. - return length == 1 ? SqlType::Bool : SqlType::Integer; + return length == 1 ? SqlType::Bool : SqlType::Int; case MYSQL_TYPE_SHORT: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: - return SqlType::Integer; + return SqlType::Int; case MYSQL_TYPE_BIT: // BIT(1) is commonly used as a boolean value.