From ef0108837e5e50e8f12bc3d0769e609d48ce91e0 Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Mon, 2 Mar 2026 13:36:56 +0530 Subject: [PATCH 01/18] added getSqlType method for mysql database --- lib/inc/drogon/HttpResponse.h | 15 +- lib/src/HttpResponseImpl.h | 26 ++- orm_lib/inc/drogon/orm/Field.h | 184 ++++++++-------------- orm_lib/inc/drogon/orm/Result.h | 37 ++++- orm_lib/src/Field.cc | 2 +- orm_lib/src/Result.cc | 28 +++- orm_lib/src/ResultImpl.h | 3 +- orm_lib/src/mysql_impl/MysqlResultImpl.cc | 5 + orm_lib/src/mysql_impl/MysqlResultImpl.h | 125 ++++++++++++++- 9 files changed, 273 insertions(+), 152 deletions(-) diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index 8b4e0409a6..cabf054c7b 100644 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -161,12 +161,6 @@ class DROGON_EXPORT HttpResponse setCustomStatusCode(code, message.data(), message.length()); } - /// Set whether the response should be compress. - virtual void setAllowCompression(bool allow) = 0; - - /// Get whether the response allow compression. - virtual bool allowCompression() const = 0; - /// Get the creation timestamp of the response. virtual const trantor::Date &creationDate() const = 0; @@ -309,6 +303,8 @@ class DROGON_EXPORT HttpResponse setBody(body, N - 1); } + virtual void setBody(const char *body, size_t len) = 0; + /// Get the response body. std::string_view body() const { @@ -566,9 +562,9 @@ class DROGON_EXPORT HttpResponse virtual const std::string &sendfileName() const = 0; /** - * @brief Returns the range of the file response as a pair of size_t + * @brief Returns the range of the file response as a pair ot size_t * (offset, length). Length of 0 means the entire file is sent. Behavior of - * this function is undefined if the response is not a file response + * this function is undefined if the response if not a file response */ using SendfileRange = std::pair; // { offset, length } virtual const SendfileRange &sendfileRange() const = 0; @@ -598,7 +594,6 @@ class DROGON_EXPORT HttpResponse } private: - virtual void setBody(const char *body, size_t len) = 0; virtual const char *getBodyData() const = 0; virtual size_t getBodyLength() const = 0; virtual void setContentTypeCodeAndCustomString(ContentType type, @@ -628,4 +623,4 @@ inline std::shared_ptr fromResponse(const HttpResponse &resp) { return resp.getJsonObject(); } -} // namespace drogon +} // namespace drogon \ No newline at end of file diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index aa8018cb73..e37fd15ac3 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -231,6 +231,15 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse } } + void setBody(const char *body, size_t len) override + { + bodyPtr_ = std::make_shared(body, len); + if (passThrough_) + { + addHeader("content-length", std::to_string(bodyPtr_->length())); + } + } + void redirect(const std::string &url) { headers_["location"] = url; @@ -463,21 +472,6 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse } private: - bool allowCompression_{true}; - - void setAllowCompression(bool allow) override; - - bool allowCompression() const override; - - void setBody(const char *body, size_t len) override - { - bodyPtr_ = std::make_shared(body, len); - if (passThrough_) - { - addHeader("content-length", std::to_string(bodyPtr_->length())); - } - } - void setContentTypeCodeAndCustomString(ContentType type, const char *typeString, size_t typeStringLength) override @@ -562,4 +556,4 @@ inline void swap(HttpResponseImpl &one, HttpResponseImpl &two) noexcept one.swap(two); } -} // namespace drogon +} // namespace drogon \ No newline at end of file diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index 53b2dbc73f..edd11af9f1 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -27,6 +27,7 @@ #include #include #include + #ifdef __linux__ #include #endif @@ -35,11 +36,8 @@ namespace drogon { namespace orm { + /// Reference to a field in a result set. -/** - * A field represents one entry in a row. It represents an actual value - * in the result set, and can be converted to various types. - */ class DROGON_EXPORT Field { public: @@ -51,46 +49,45 @@ class DROGON_EXPORT Field /// Is this field's value null? bool isNull() const; - /// Read as plain C string - /** - * Since the field's data is stored internally in the form of a - * zero-terminated C string, this is the fastest way to read it. Use the - * to() or as() functions to convert the string to other types such as - * @c int, or to C++ strings. - */ + /// Raw C-string value const char *c_str() const; - /// Get the length of the plain C string + /// Length of raw data size_t length() const { return result_.getLength(row_, column_); } - /// Convert to a type T value + // ========================================================= + // 🔥 NEW METADATA APIs + // ========================================================= + + 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_); } + + // ========================================================= + // Existing conversion API (UNCHANGED) + // ========================================================= + template T as() const { if (isNull()) return T(); + auto data_ = result_.getValue(row_, column_); - // auto dataLength_ = result_.getLength(row_, column_); - // For binary format! - // if (dataLength_ == 1) - // { - // return *data_; - // } - // else if (dataLength_ == 4) - // { - // const int32_t *n = (int32_t *)data_; - // return ntohl(*n); - // } - // else if (dataLength_ == 8) - // { - // const int64_t *n = (int64_t *)data_; - // return ntohll(*n); - // } - // return 0; - T value = T(); + T value{}; if (data_) { try @@ -100,20 +97,12 @@ class DROGON_EXPORT Field } catch (...) { - LOG_DEBUG << "Type error"; + LOG_DEBUG << "Field::as() conversion error"; } } return value; } - /// Parse the field as an SQL array. - /** - * Call the parser to retrieve values (and structure) from the array. - * - * Make sure the @c result object stays alive until parsing is finished. If - * you keep the @c row of @c field object alive, it will keep the @c result - * object alive as well. - */ ArrayParser getArrayParser() const { return ArrayParser(result_.getValue(row_, column_)); @@ -123,24 +112,24 @@ class DROGON_EXPORT Field std::vector> asArray() const { std::vector> ret; - auto arrParser = getArrayParser(); - while (1) + auto parser = getArrayParser(); + + while (true) { - auto arrVal = arrParser.getNext(); - if (arrVal.first == ArrayParser::juncture::done) - { + auto val = parser.getNext(); + if (val.first == ArrayParser::juncture::done) break; - } - if (arrVal.first == ArrayParser::juncture::string_value) + + if (val.first == ArrayParser::juncture::string_value) { - T val; - std::stringstream ss(std::move(arrVal.second)); - ss >> val; - ret.push_back(std::shared_ptr(new T(val))); + T v{}; + std::stringstream ss(std::move(val.second)); + ss >> v; + ret.emplace_back(std::make_shared(v)); } - else if (arrVal.first == ArrayParser::juncture::null_value) + else if (val.first == ArrayParser::juncture::null_value) { - ret.push_back(std::shared_ptr()); + ret.emplace_back(nullptr); } } return ret; @@ -148,25 +137,32 @@ class DROGON_EXPORT Field protected: Result::SizeType row_; - /** - * Column number - * You'd expect this to be a size_t, but due to the way reverse iterators - * are related to regular iterators, it must be allowed to underflow to -1. - */ long column_; + friend class Row; + friend class MysqlResultImpl; + friend class PgResultImpl; + friend class Sqlite3ResultImpl; + Field(const Row &row, Row::SizeType columnNum) noexcept; private: const Result result_; }; +// ============================================================= +// Explicit template specializations (UNCHANGED) +// ============================================================= + template <> DROGON_EXPORT std::string Field::as() const; + template <> DROGON_EXPORT const char *Field::as() const; + template <> DROGON_EXPORT char *Field::as() const; + template <> DROGON_EXPORT std::vector Field::as>() const; @@ -174,106 +170,54 @@ template <> inline std::string_view Field::as() const { auto first = result_.getValue(row_, column_); - auto length = result_.getLength(row_, column_); - return {first, length}; + auto len = result_.getLength(row_, column_); + return {first, len}; } template <> inline float Field::as() const { - if (isNull()) - return 0.0; - return std::stof(result_.getValue(row_, column_)); + return isNull() ? 0.0f : std::stof(result_.getValue(row_, column_)); } template <> inline double Field::as() const { - if (isNull()) - return 0.0; - return std::stod(result_.getValue(row_, column_)); + return isNull() ? 0.0 : std::stod(result_.getValue(row_, column_)); } template <> inline bool Field::as() const { if (result_.getLength(row_, column_) != 1) - { return false; - } - auto value = result_.getValue(row_, column_); - if (*value == 't' || *value == '1') - return true; - return false; + char v = *result_.getValue(row_, column_); + return (v == 't' || v == '1'); } template <> inline int Field::as() const { - if (isNull()) - return 0; - return std::stoi(result_.getValue(row_, column_)); + return isNull() ? 0 : std::stoi(result_.getValue(row_, column_)); } template <> inline long Field::as() const { - if (isNull()) - return 0; - return std::stol(result_.getValue(row_, column_)); -} - -template <> -inline int8_t Field::as() const -{ - if (isNull()) - return 0; - return static_cast(atoi(result_.getValue(row_, column_))); + return isNull() ? 0L : std::stol(result_.getValue(row_, column_)); } template <> inline long long Field::as() const { - if (isNull()) - return 0; - return atoll(result_.getValue(row_, column_)); -} - -template <> -inline unsigned int Field::as() const -{ - if (isNull()) - return 0; - return static_cast( - std::stoul(result_.getValue(row_, column_))); -} - -template <> -inline unsigned long Field::as() const -{ - if (isNull()) - return 0; - return std::stoul(result_.getValue(row_, column_)); -} - -template <> -inline uint8_t Field::as() const -{ - if (isNull()) - return 0; - return static_cast(atoi(result_.getValue(row_, column_))); + return isNull() ? 0LL : std::stoll(result_.getValue(row_, column_)); } template <> inline unsigned long long Field::as() const { - if (isNull()) - return 0; - return std::stoull(result_.getValue(row_, column_)); + return isNull() ? 0ULL : std::stoull(result_.getValue(row_, column_)); } -// std::vector Field::as>() const; -// template <> -// std::vector Field::as>() const; } // namespace orm } // namespace drogon diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index ab6389682d..4cf1a9953d 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -34,12 +34,41 @@ class Row; class ResultImpl; using ResultImplPtr = std::shared_ptr; +// 🔥 NEW: SQL field type abstraction +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 MysqlColumnMeta +{ + 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 +168,12 @@ class DROGON_EXPORT Result */ unsigned long long insertId() const noexcept; + SqlFieldType getSqlType(SizeType column) const; + const std::string &getTypeName(SizeType column) const; + int getColumnLength(SizeType column) const; + int getPrecision(SizeType column) const; + int getScale(SizeType column) const; + #ifdef _MSC_VER Result() noexcept = default; #endif @@ -181,4 +216,4 @@ inline void swap(drogon::orm::Result &one, drogon::orm::Result &two) noexcept one.swap(two); } } // namespace std -#endif +#endif \ No newline at end of file diff --git a/orm_lib/src/Field.cc b/orm_lib/src/Field.cc index 0f0089c86e..365f69515d 100644 --- a/orm_lib/src/Field.cc +++ b/orm_lib/src/Field.cc @@ -124,4 +124,4 @@ const char *Field::c_str() const // auto dataLength_ = result_.getLength(row_, column_); // return std::vector((int64_t *)data_,(int64_t *)(data_ + // dataLength_)); -// } +// } \ No newline at end of file diff --git a/orm_lib/src/Result.cc b/orm_lib/src/Result.cc index fe45ebb314..380a979bfa 100644 --- a/orm_lib/src/Result.cc +++ b/orm_lib/src/Result.cc @@ -170,6 +170,32 @@ 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); @@ -185,4 +211,4 @@ Result &Result::operator=(Result &&r) noexcept { resultPtr_ = std::move(r.resultPtr_); return *this; -} +} \ No newline at end of file diff --git a/orm_lib/src/ResultImpl.h b/orm_lib/src/ResultImpl.h index fb8e1d0379..199f617cee 100644 --- a/orm_lib/src/ResultImpl.h +++ b/orm_lib/src/ResultImpl.h @@ -36,6 +36,7 @@ class ResultImpl : public trantor::NonCopyable virtual const char *getValue(SizeType row, RowSizeType column) const = 0; virtual bool isNull(SizeType row, RowSizeType column) const = 0; virtual FieldSizeType getLength(SizeType row, RowSizeType column) const = 0; + virtual const MysqlColumnMeta &columnMeta(SizeType column) const = 0; virtual unsigned long long insertId() const noexcept { @@ -54,4 +55,4 @@ class ResultImpl : public trantor::NonCopyable }; } // namespace orm -} // namespace drogon +} // namespace drogon \ No newline at end of file diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.cc b/orm_lib/src/mysql_impl/MysqlResultImpl.cc index 20b4a42768..d3c1d8cd84 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 MysqlColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const +{ + return columnMeta_[column]; +} \ No newline at end of file diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 64013f0271..d99ba6f796 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -26,6 +26,93 @@ 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; // 🔥 THIS FIXES BINARY(16) + return SqlFieldType::Text; + + case MYSQL_TYPE_BLOB: + return SqlFieldType::Blob; + + 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,7 +126,38 @@ class MysqlResultImpl : public ResultImpl affectedRows_(affectedRows), insertId_(insertId) { - if (fieldsNumber_ > 0) + if (fieldArray_ && fieldsNumber_ > 0) + { + columnMeta_.resize(fieldsNumber_); + + for (unsigned int i = 0; i < fieldsNumber_; ++i) + { + const MYSQL_FIELD &f = fieldArray_[i]; + auto &meta = columnMeta_[i]; + + meta.sqlType = mysqlTypeToSql(f.type, f.flags); + + // mysql_field_type_to_name() may return nullptr + const char *typeName = mysqlFieldTypeToName(f.type, f.flags); + meta.typeName = typeName ? typeName : "UNKNOWN"; + + // VARCHAR / CHAR length + meta.length = static_cast(f.length); + + // DECIMAL(p,s): length == precision, decimals == scale + meta.precision = + (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.length) + : 0; + + meta.scale = + (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.decimals) + : 0; + } + } + + if (fieldArray_ && fieldsNumber_ > 0) { fieldsMapPtr_ = std::make_shared< std::unordered_map>(); @@ -53,6 +171,7 @@ class MysqlResultImpl : public ResultImpl (*fieldsMapPtr_)[fieldName] = i; } } + if (size() > 0) { rowsPtr_ = std::make_shared< @@ -80,11 +199,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 MysqlColumnMeta &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_; @@ -94,4 +215,4 @@ class MysqlResultImpl : public ResultImpl }; } // namespace orm -} // namespace drogon +} // namespace drogon \ No newline at end of file From 8b93dfc0dfc0a5025281a76ba7980e7eb9abbecd Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 14:48:07 +0530 Subject: [PATCH 02/18] Update HttpResponseImpl.h --- lib/src/HttpResponseImpl.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index e37fd15ac3..493ea14f41 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -472,6 +472,21 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse } private: + bool allowCompression_{true}; + + void setAllowCompression(bool allow) override; + + bool allowCompression() const override; + + void setBody(const char *body, size_t len) override + { + bodyPtr_ = std::make_shared(body, len); + if (passThrough_) + { + addHeader("content-length", std::to_string(bodyPtr_->length())); + } + } + void setContentTypeCodeAndCustomString(ContentType type, const char *typeString, size_t typeStringLength) override @@ -556,4 +571,4 @@ inline void swap(HttpResponseImpl &one, HttpResponseImpl &two) noexcept one.swap(two); } -} // namespace drogon \ No newline at end of file +} // namespace drogon From 811c29d86bac9bec1048577b47a5123f594260f3 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 14:50:59 +0530 Subject: [PATCH 03/18] Update HttpResponse.h --- lib/inc/drogon/HttpResponse.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index cabf054c7b..c3b955f023 100644 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -161,6 +161,12 @@ class DROGON_EXPORT HttpResponse setCustomStatusCode(code, message.data(), message.length()); } + /// Set whether the response should be compress. + virtual void setAllowCompression(bool allow) = 0; + + /// Get whether the response allow compression. + virtual bool allowCompression() const = 0; + /// Get the creation timestamp of the response. virtual const trantor::Date &creationDate() const = 0; @@ -623,4 +629,4 @@ inline std::shared_ptr fromResponse(const HttpResponse &resp) { return resp.getJsonObject(); } -} // namespace drogon \ No newline at end of file +} // namespace drogon From e5191aef5766da74f2cb2c86af8e79b9cecb3b1b Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 14:52:20 +0530 Subject: [PATCH 04/18] Update HttpResponseImpl.h --- lib/src/HttpResponseImpl.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 493ea14f41..1470bdbfd7 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -478,15 +478,6 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse bool allowCompression() const override; - void setBody(const char *body, size_t len) override - { - bodyPtr_ = std::make_shared(body, len); - if (passThrough_) - { - addHeader("content-length", std::to_string(bodyPtr_->length())); - } - } - void setContentTypeCodeAndCustomString(ContentType type, const char *typeString, size_t typeStringLength) override From 733f622dae38bbbc93c217aba198faaba5a34494 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 17:22:02 +0530 Subject: [PATCH 05/18] Update Field.h --- orm_lib/inc/drogon/orm/Field.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index edd11af9f1..d7322fda09 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -52,7 +52,7 @@ class DROGON_EXPORT Field /// Raw C-string value const char *c_str() const; - /// Length of raw data + /// Get the length of the plain C string size_t length() const { return result_.getLength(row_, column_); @@ -76,10 +76,7 @@ class DROGON_EXPORT Field /// Numeric scale (DECIMAL / NUMERIC) int scale() const noexcept { return result_.getScale(column_); } - // ========================================================= - // Existing conversion API (UNCHANGED) - // ========================================================= - + /// Convert to a type T value template T as() const { @@ -97,7 +94,7 @@ class DROGON_EXPORT Field } catch (...) { - LOG_DEBUG << "Field::as() conversion error"; + LOG_DEBUG << "Type error"; } } return value; From 7b155c27c8c4bc8eb6645de5326b6fe01ec80481 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 17:23:57 +0530 Subject: [PATCH 06/18] Update HttpResponse.h --- lib/inc/drogon/HttpResponse.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index c3b955f023..42e23de8c3 100644 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -568,9 +568,9 @@ class DROGON_EXPORT HttpResponse virtual const std::string &sendfileName() const = 0; /** - * @brief Returns the range of the file response as a pair ot size_t + * @brief Returns the range of the file response as a pair of size_t * (offset, length). Length of 0 means the entire file is sent. Behavior of - * this function is undefined if the response if not a file response + * this function is undefined if the response is not a file response */ using SendfileRange = std::pair; // { offset, length } virtual const SendfileRange &sendfileRange() const = 0; From ab53a63a2d1ff4bc8c066827ab54ed5cc674ee33 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 17:28:38 +0530 Subject: [PATCH 07/18] Update Field.h --- orm_lib/inc/drogon/orm/Field.h | 157 ++++++++++++++++++++++++--------- 1 file changed, 117 insertions(+), 40 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index d7322fda09..09c7934714 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -27,7 +27,6 @@ #include #include #include - #ifdef __linux__ #include #endif @@ -36,8 +35,11 @@ namespace drogon { namespace orm { - /// Reference to a field in a result set. +/** + * A field represents one entry in a row. It represents an actual value + * in the result set, and can be converted to various types. + */ class DROGON_EXPORT Field { public: @@ -49,7 +51,13 @@ class DROGON_EXPORT Field /// Is this field's value null? bool isNull() const; - /// Raw C-string value + /// Read as plain C string + /** + * Since the field's data is stored internally in the form of a + * zero-terminated C string, this is the fastest way to read it. Use the + * to() or as() functions to convert the string to other types such as + * @c int, or to C++ strings. + */ const char *c_str() const; /// Get the length of the plain C string @@ -82,9 +90,25 @@ class DROGON_EXPORT Field { if (isNull()) return T(); - auto data_ = result_.getValue(row_, column_); - T value{}; + // auto dataLength_ = result_.getLength(row_, column_); + // For binary format! + // if (dataLength_ == 1) + // { + // return *data_; + // } + // else if (dataLength_ == 4) + // { + // const int32_t *n = (int32_t *)data_; + // return ntohl(*n); + // } + // else if (dataLength_ == 8) + // { + // const int64_t *n = (int64_t *)data_; + // return ntohll(*n); + // } + // return 0; + T value = T(); if (data_) { try @@ -100,6 +124,14 @@ class DROGON_EXPORT Field return value; } + /// Parse the field as an SQL array. + /** + * Call the parser to retrieve values (and structure) from the array. + * + * Make sure the @c result object stays alive until parsing is finished. If + * you keep the @c row of @c field object alive, it will keep the @c result + * object alive as well. + */ ArrayParser getArrayParser() const { return ArrayParser(result_.getValue(row_, column_)); @@ -109,24 +141,24 @@ class DROGON_EXPORT Field std::vector> asArray() const { std::vector> ret; - auto parser = getArrayParser(); - - while (true) + auto arrParser = getArrayParser(); + while (1) { - auto val = parser.getNext(); - if (val.first == ArrayParser::juncture::done) + auto arrVal = arrParser.getNext(); + if (arrVal.first == ArrayParser::juncture::done) + { break; - - if (val.first == ArrayParser::juncture::string_value) + } + if (arrVal.first == ArrayParser::juncture::string_value) { - T v{}; - std::stringstream ss(std::move(val.second)); - ss >> v; - ret.emplace_back(std::make_shared(v)); + T val; + std::stringstream ss(std::move(arrVal.second)); + ss >> val; + ret.push_back(std::shared_ptr(new T(val))); } - else if (val.first == ArrayParser::juncture::null_value) + else if (arrVal.first == ArrayParser::juncture::null_value) { - ret.emplace_back(nullptr); + ret.push_back(std::shared_ptr()); } } return ret; @@ -134,32 +166,25 @@ class DROGON_EXPORT Field protected: Result::SizeType row_; + /** + * Column number + * You'd expect this to be a size_t, but due to the way reverse iterators + * are related to regular iterators, it must be allowed to underflow to -1. + */ long column_; - friend class Row; - friend class MysqlResultImpl; - friend class PgResultImpl; - friend class Sqlite3ResultImpl; - Field(const Row &row, Row::SizeType columnNum) noexcept; private: const Result result_; }; -// ============================================================= -// Explicit template specializations (UNCHANGED) -// ============================================================= - template <> DROGON_EXPORT std::string Field::as() const; - template <> DROGON_EXPORT const char *Field::as() const; - template <> DROGON_EXPORT char *Field::as() const; - template <> DROGON_EXPORT std::vector Field::as>() const; @@ -167,54 +192,106 @@ template <> inline std::string_view Field::as() const { auto first = result_.getValue(row_, column_); - auto len = result_.getLength(row_, column_); - return {first, len}; + auto length = result_.getLength(row_, column_); + return {first, length}; } template <> inline float Field::as() const { - return isNull() ? 0.0f : std::stof(result_.getValue(row_, column_)); + if (isNull()) + return 0.0; + return std::stof(result_.getValue(row_, column_)); } template <> inline double Field::as() const { - return isNull() ? 0.0 : std::stod(result_.getValue(row_, column_)); + if (isNull()) + return 0.0; + return std::stod(result_.getValue(row_, column_)); } template <> inline bool Field::as() const { if (result_.getLength(row_, column_) != 1) + { return false; - char v = *result_.getValue(row_, column_); - return (v == 't' || v == '1'); + } + auto value = result_.getValue(row_, column_); + if (*value == 't' || *value == '1') + return true; + return false; } template <> inline int Field::as() const { - return isNull() ? 0 : std::stoi(result_.getValue(row_, column_)); + if (isNull()) + return 0; + return std::stoi(result_.getValue(row_, column_)); } template <> inline long Field::as() const { - return isNull() ? 0L : std::stol(result_.getValue(row_, column_)); + if (isNull()) + return 0; + return std::stol(result_.getValue(row_, column_)); +} + +template <> +inline int8_t Field::as() const +{ + if (isNull()) + return 0; + return static_cast(atoi(result_.getValue(row_, column_))); } template <> inline long long Field::as() const { - return isNull() ? 0LL : std::stoll(result_.getValue(row_, column_)); + if (isNull()) + return 0; + return atoll(result_.getValue(row_, column_)); +} + +template <> +inline unsigned int Field::as() const +{ + if (isNull()) + return 0; + return static_cast( + std::stoul(result_.getValue(row_, column_))); +} + +template <> +inline unsigned long Field::as() const +{ + if (isNull()) + return 0; + return std::stoul(result_.getValue(row_, column_)); +} + +template <> +inline uint8_t Field::as() const +{ + if (isNull()) + return 0; + return static_cast(atoi(result_.getValue(row_, column_))); } template <> inline unsigned long long Field::as() const { - return isNull() ? 0ULL : std::stoull(result_.getValue(row_, column_)); + if (isNull()) + return 0; + return std::stoull(result_.getValue(row_, column_)); } +// std::vector Field::as>() const; +// template <> +// std::vector Field::as>() const; } // namespace orm } // namespace drogon From ea6fbd815d035ea108f8310f1c0fcdc4f4dab370 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 2 Mar 2026 17:37:01 +0530 Subject: [PATCH 08/18] Update Field.cc --- orm_lib/src/Field.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm_lib/src/Field.cc b/orm_lib/src/Field.cc index 365f69515d..0f0089c86e 100644 --- a/orm_lib/src/Field.cc +++ b/orm_lib/src/Field.cc @@ -124,4 +124,4 @@ const char *Field::c_str() const // auto dataLength_ = result_.getLength(row_, column_); // return std::vector((int64_t *)data_,(int64_t *)(data_ + // dataLength_)); -// } \ No newline at end of file +// } From c2ad4c83a2095424873edd38a0462149eb472af4 Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Fri, 6 Mar 2026 13:42:04 +0530 Subject: [PATCH 09/18] Run format.sh and apply clang-format fixes --- orm_lib/inc/drogon/orm/Field.h | 25 ++++++-- orm_lib/inc/drogon/orm/Result.h | 2 +- orm_lib/src/Result.cc | 3 +- orm_lib/src/ResultImpl.h | 2 +- orm_lib/src/mysql_impl/MysqlResultImpl.cc | 2 +- orm_lib/src/mysql_impl/MysqlResultImpl.h | 69 +++++++++++++---------- 6 files changed, 64 insertions(+), 39 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index 09c7934714..5f6ce528cd 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -70,19 +70,34 @@ class DROGON_EXPORT Field // 🔥 NEW METADATA APIs // ========================================================= - SqlFieldType sqlType() const noexcept { return result_.getSqlType(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_); } + const std::string &typeName() const noexcept + { + return result_.getTypeName(column_); + } /// Character length (VARCHAR) - int columnLength() const noexcept { return result_.getColumnLength(column_); } + int columnLength() const noexcept + { + return result_.getColumnLength(column_); + } /// Numeric precision (DECIMAL / NUMERIC) - int precision() const noexcept { return result_.getPrecision(column_); } + int precision() const noexcept + { + return result_.getPrecision(column_); + } /// Numeric scale (DECIMAL / NUMERIC) - int scale() const noexcept { return result_.getScale(column_); } + int scale() const noexcept + { + return result_.getScale(column_); + } /// Convert to a type T value template diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 4cf1a9953d..d3c5d214e6 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -216,4 +216,4 @@ inline void swap(drogon::orm::Result &one, drogon::orm::Result &two) noexcept one.swap(two); } } // namespace std -#endif \ No newline at end of file +#endif diff --git a/orm_lib/src/Result.cc b/orm_lib/src/Result.cc index 380a979bfa..309884f90e 100644 --- a/orm_lib/src/Result.cc +++ b/orm_lib/src/Result.cc @@ -195,7 +195,6 @@ int Result::getScale(SizeType column) const return resultPtr_->columnMeta(column).scale; } - int Result::oid(RowSizeType column) const noexcept { return resultPtr_->oid(column); @@ -211,4 +210,4 @@ Result &Result::operator=(Result &&r) noexcept { resultPtr_ = std::move(r.resultPtr_); return *this; -} \ No newline at end of file +} diff --git a/orm_lib/src/ResultImpl.h b/orm_lib/src/ResultImpl.h index 199f617cee..01d058758a 100644 --- a/orm_lib/src/ResultImpl.h +++ b/orm_lib/src/ResultImpl.h @@ -55,4 +55,4 @@ class ResultImpl : public trantor::NonCopyable }; } // namespace orm -} // namespace drogon \ No newline at end of file +} // namespace drogon diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.cc b/orm_lib/src/mysql_impl/MysqlResultImpl.cc index d3c1d8cd84..1a9cbdf141 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.cc +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.cc @@ -87,4 +87,4 @@ unsigned long long MysqlResultImpl::insertId() const noexcept const MysqlColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const { return columnMeta_[column]; -} \ No newline at end of file +} diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index d99ba6f796..ea900eb895 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -27,8 +27,7 @@ namespace drogon namespace orm { -inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, - unsigned int flags) +inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) { switch (t) { @@ -57,7 +56,7 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, case MYSQL_TYPE_STRING: if (flags & BINARY_FLAG) - return SqlFieldType::Binary; // 🔥 THIS FIXES BINARY(16) + return SqlFieldType::Binary; // 🔥 THIS FIXES BINARY(16) return SqlFieldType::Text; case MYSQL_TYPE_BLOB: @@ -81,18 +80,25 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, } } -inline const char* mysqlFieldTypeToName(enum enum_field_types t, +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_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: @@ -104,15 +110,22 @@ inline const char* mysqlFieldTypeToName(enum enum_field_types t, 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"; + 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"; + default: + return "UNKNOWN"; } } + class MysqlResultImpl : public ResultImpl { public: @@ -126,7 +139,7 @@ class MysqlResultImpl : public ResultImpl affectedRows_(affectedRows), insertId_(insertId) { - if (fieldArray_ && fieldsNumber_ > 0) + if (fieldArray_ && fieldsNumber_ > 0) { columnMeta_.resize(fieldsNumber_); @@ -145,18 +158,16 @@ class MysqlResultImpl : public ResultImpl meta.length = static_cast(f.length); // DECIMAL(p,s): length == precision, decimals == scale - meta.precision = - (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.length) - : 0; - - meta.scale = - (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.decimals) - : 0; + meta.precision = (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.length) + : 0; + + meta.scale = (meta.sqlType == SqlFieldType::Decimal) + ? static_cast(f.decimals) + : 0; } } - + if (fieldArray_ && fieldsNumber_ > 0) { fieldsMapPtr_ = std::make_shared< @@ -215,4 +226,4 @@ class MysqlResultImpl : public ResultImpl }; } // namespace orm -} // namespace drogon \ No newline at end of file +} // namespace drogon From 650569c41c75dedc93f0ea6aae40587ba68ef1ec Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Fri, 6 Mar 2026 18:06:56 +0530 Subject: [PATCH 10/18] orm: add fallback columnMeta() implementation to fix SQLite/PostgreSQL builds --- orm_lib/inc/drogon/orm/Result.h | 2 +- orm_lib/src/ResultImpl.h | 8 +++++++- orm_lib/src/mysql_impl/MysqlResultImpl.cc | 2 +- orm_lib/src/mysql_impl/MysqlResultImpl.h | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index d3c5d214e6..994a1b24f5 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -60,7 +60,7 @@ enum class SqlStatus End }; -struct MysqlColumnMeta +struct ColumnMeta { SqlFieldType sqlType{SqlFieldType::Unknown}; std::string typeName; diff --git a/orm_lib/src/ResultImpl.h b/orm_lib/src/ResultImpl.h index 01d058758a..18c945b2f2 100644 --- a/orm_lib/src/ResultImpl.h +++ b/orm_lib/src/ResultImpl.h @@ -36,7 +36,13 @@ class ResultImpl : public trantor::NonCopyable virtual const char *getValue(SizeType row, RowSizeType column) const = 0; virtual bool isNull(SizeType row, RowSizeType column) const = 0; virtual FieldSizeType getLength(SizeType row, RowSizeType column) const = 0; - virtual const MysqlColumnMeta &columnMeta(SizeType column) const = 0; + + virtual const ColumnMeta &columnMeta(SizeType column) const + { + (void)column; + static ColumnMeta dummy{}; + return dummy; + } virtual unsigned long long insertId() const noexcept { diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.cc b/orm_lib/src/mysql_impl/MysqlResultImpl.cc index 1a9cbdf141..238d305f6c 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.cc +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.cc @@ -84,7 +84,7 @@ unsigned long long MysqlResultImpl::insertId() const noexcept return insertId_; } -const MysqlColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const +const ColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const { return columnMeta_[column]; } diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index ea900eb895..37e7f70ca4 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -210,13 +210,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 MysqlColumnMeta &columnMeta(SizeType column) const 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_; + std::vector columnMeta_; const Result::RowSizeType fieldsNumber_; const SizeType affectedRows_; const unsigned long long insertId_; From d96d56e93ffafed29babe503a97017dad2061c43 Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Fri, 6 Mar 2026 18:40:03 +0530 Subject: [PATCH 11/18] trantor Synced with master --- trantor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trantor b/trantor index 5000e2a726..720db22f1a 160000 --- a/trantor +++ b/trantor @@ -1 +1 @@ -Subproject commit 5000e2a72687232c8675b28ce86a29ed7d44309e +Subproject commit 720db22f1a367d36c41c22a16ccfc71f1cdad595 From 2d0c0547b31bcb460bde7e8eeea36f6b0cf33d1b Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 9 Mar 2026 16:32:25 +0530 Subject: [PATCH 12/18] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- orm_lib/src/ResultImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm_lib/src/ResultImpl.h b/orm_lib/src/ResultImpl.h index 18c945b2f2..5322b9819b 100644 --- a/orm_lib/src/ResultImpl.h +++ b/orm_lib/src/ResultImpl.h @@ -40,7 +40,7 @@ class ResultImpl : public trantor::NonCopyable virtual const ColumnMeta &columnMeta(SizeType column) const { (void)column; - static ColumnMeta dummy{}; + static const ColumnMeta dummy{}; return dummy; } From 69bc6dd90074e8ce0e4c041f8455420b5f4cac11 Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 9 Mar 2026 16:32:52 +0530 Subject: [PATCH 13/18] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- orm_lib/src/mysql_impl/MysqlResultImpl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.cc b/orm_lib/src/mysql_impl/MysqlResultImpl.cc index 238d305f6c..ff664b0dd8 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.cc +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.cc @@ -86,5 +86,5 @@ unsigned long long MysqlResultImpl::insertId() const noexcept const ColumnMeta &MysqlResultImpl::columnMeta(SizeType column) const { - return columnMeta_[column]; + return columnMeta_.at(column); } From 0494d52201bc3ccd093e8ca099ae2dbe101c401e Mon Sep 17 00:00:00 2001 From: karthikeyan-netizen Date: Mon, 9 Mar 2026 16:33:59 +0530 Subject: [PATCH 14/18] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- orm_lib/inc/drogon/orm/Result.h | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 994a1b24f5..57a6fc1da0 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -168,10 +168,75 @@ 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 From a1a0d6f6548dd399e92a1748c13efef51dfcb5f7 Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Mon, 9 Mar 2026 17:17:30 +0530 Subject: [PATCH 15/18] orm(mysql): merge duplicate field metadata loops in MysqlResultImpl constructor --- orm_lib/src/mysql_impl/MysqlResultImpl.h | 32 ++++++++++-------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 37e7f70ca4..281816249c 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -143,6 +143,10 @@ class MysqlResultImpl : public ResultImpl { columnMeta_.resize(fieldsNumber_); + fieldsMapPtr_ = std::make_shared< + std::unordered_map>(); + fieldsMapPtr_->reserve(fieldsNumber_); + for (unsigned int i = 0; i < fieldsNumber_; ++i) { const MYSQL_FIELD &f = fieldArray_[i]; @@ -150,35 +154,25 @@ class MysqlResultImpl : public ResultImpl meta.sqlType = mysqlTypeToSql(f.type, f.flags); - // mysql_field_type_to_name() may return nullptr const char *typeName = mysqlFieldTypeToName(f.type, f.flags); meta.typeName = typeName ? typeName : "UNKNOWN"; - // VARCHAR / CHAR length meta.length = static_cast(f.length); - // DECIMAL(p,s): length == precision, decimals == scale meta.precision = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.length) - : 0; + ? static_cast(f.length) + : 0; meta.scale = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.decimals) - : 0; - } - } + ? static_cast(f.decimals) + : 0; - if (fieldArray_ && fieldsNumber_ > 0) - { - fieldsMapPtr_ = std::make_shared< - std::unordered_map>(); - for (RowSizeType i = 0; i < fieldsNumber_; ++i) - { - std::string fieldName = fieldArray_[i].name; + std::string fieldName = f.name; std::transform(fieldName.begin(), - fieldName.end(), - fieldName.begin(), - [](unsigned char c) { return tolower(c); }); + fieldName.end(), + fieldName.begin(), + [](unsigned char c) { return std::tolower(c); }); + (*fieldsMapPtr_)[fieldName] = i; } } From 9ca946d43056f39b9156ba69b6bba7252bbafbed Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Mon, 9 Mar 2026 17:29:25 +0530 Subject: [PATCH 16/18] Did some changes Copilot suggested --- orm_lib/inc/drogon/orm/Field.h | 4 ---- orm_lib/inc/drogon/orm/Result.h | 1 - orm_lib/src/mysql_impl/MysqlResultImpl.h | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Field.h b/orm_lib/inc/drogon/orm/Field.h index 5f6ce528cd..e682636fb9 100644 --- a/orm_lib/inc/drogon/orm/Field.h +++ b/orm_lib/inc/drogon/orm/Field.h @@ -66,10 +66,6 @@ class DROGON_EXPORT Field return result_.getLength(row_, column_); } - // ========================================================= - // 🔥 NEW METADATA APIs - // ========================================================= - SqlFieldType sqlType() const noexcept { return result_.getSqlType(column_); diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 57a6fc1da0..4093d9c01c 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -34,7 +34,6 @@ class Row; class ResultImpl; using ResultImplPtr = std::shared_ptr; -// 🔥 NEW: SQL field type abstraction enum class SqlFieldType : uint8_t { Unknown = 0, diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 281816249c..57f46bd185 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -56,7 +56,7 @@ inline SqlFieldType mysqlTypeToSql(enum enum_field_types t, unsigned int flags) case MYSQL_TYPE_STRING: if (flags & BINARY_FLAG) - return SqlFieldType::Binary; // 🔥 THIS FIXES BINARY(16) + return SqlFieldType::Binary; return SqlFieldType::Text; case MYSQL_TYPE_BLOB: From 9be60458e930b17ce3dfdad4ab08e30b4a67ab8f Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Mon, 9 Mar 2026 17:46:17 +0530 Subject: [PATCH 17/18] Ran ./format.sh --- orm_lib/inc/drogon/orm/Result.h | 15 ++++++++++----- orm_lib/src/mysql_impl/MysqlResultImpl.h | 14 +++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/orm_lib/inc/drogon/orm/Result.h b/orm_lib/inc/drogon/orm/Result.h index 4093d9c01c..3ac6620f32 100644 --- a/orm_lib/inc/drogon/orm/Result.h +++ b/orm_lib/inc/drogon/orm/Result.h @@ -170,7 +170,8 @@ class DROGON_EXPORT Result /** * @brief Get the logical SQL type of the specified column. * - * @param column Zero-based index of the column (must be less than columns()). + * @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. * @@ -183,7 +184,8 @@ class DROGON_EXPORT Result /** * @brief Get the database-specific type name of the specified column. * - * @param column Zero-based index of the column (must be less than columns()). + * @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.). @@ -197,7 +199,8 @@ class DROGON_EXPORT Result /** * @brief Get the defined maximum length of the specified column. * - * @param column Zero-based index of the column (must be less than columns()). + * @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. @@ -211,7 +214,8 @@ class DROGON_EXPORT Result /** * @brief Get the numeric precision for the specified column. * - * @param column Zero-based index of the column (must be less than columns()). + * @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. @@ -226,7 +230,8 @@ class DROGON_EXPORT Result /** * @brief Get the numeric scale for the specified column. * - * @param column Zero-based index of the column (must be less than columns()). + * @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. diff --git a/orm_lib/src/mysql_impl/MysqlResultImpl.h b/orm_lib/src/mysql_impl/MysqlResultImpl.h index 57f46bd185..4b86b8dedb 100644 --- a/orm_lib/src/mysql_impl/MysqlResultImpl.h +++ b/orm_lib/src/mysql_impl/MysqlResultImpl.h @@ -160,18 +160,18 @@ class MysqlResultImpl : public ResultImpl meta.length = static_cast(f.length); meta.precision = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.length) - : 0; + ? static_cast(f.length) + : 0; meta.scale = (meta.sqlType == SqlFieldType::Decimal) - ? static_cast(f.decimals) - : 0; + ? static_cast(f.decimals) + : 0; std::string fieldName = f.name; std::transform(fieldName.begin(), - fieldName.end(), - fieldName.begin(), - [](unsigned char c) { return std::tolower(c); }); + fieldName.end(), + fieldName.begin(), + [](unsigned char c) { return std::tolower(c); }); (*fieldsMapPtr_)[fieldName] = i; } From 3c68c539a7b25ddea21ea642d34ab6da8062fb73 Mon Sep 17 00:00:00 2001 From: karthikeyan M Date: Sat, 14 Mar 2026 19:59:12 +0530 Subject: [PATCH 18/18] http: revert setBody() access modifier change --- lib/inc/drogon/HttpResponse.h | 3 +-- lib/src/HttpResponseImpl.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index 42e23de8c3..8b4e0409a6 100644 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -309,8 +309,6 @@ class DROGON_EXPORT HttpResponse setBody(body, N - 1); } - virtual void setBody(const char *body, size_t len) = 0; - /// Get the response body. std::string_view body() const { @@ -600,6 +598,7 @@ class DROGON_EXPORT HttpResponse } private: + virtual void setBody(const char *body, size_t len) = 0; virtual const char *getBodyData() const = 0; virtual size_t getBodyLength() const = 0; virtual void setContentTypeCodeAndCustomString(ContentType type, diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 1470bdbfd7..0a4fa4c745 100644 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -231,15 +231,6 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse } } - void setBody(const char *body, size_t len) override - { - bodyPtr_ = std::make_shared(body, len); - if (passThrough_) - { - addHeader("content-length", std::to_string(bodyPtr_->length())); - } - } - void redirect(const std::string &url) { headers_["location"] = url; @@ -474,6 +465,15 @@ class DROGON_EXPORT HttpResponseImpl : public HttpResponse private: bool allowCompression_{true}; + void setBody(const char *body, size_t len) override + { + bodyPtr_ = std::make_shared(body, len); + if (passThrough_) + { + addHeader("content-length", std::to_string(bodyPtr_->length())); + } + } + void setAllowCompression(bool allow) override; bool allowCompression() const override;