From 013e8708453488247b0da30e3e0662bc2d351b5a Mon Sep 17 00:00:00 2001 From: JohanCorn Date: Sat, 19 Sep 2026 21:01:56 +0200 Subject: [PATCH] Fix SQL_IsNull() returning 0 for NULL columns in threaded queries Threaded query results are copied into an AtomicResult, which stored NULL cells as empty strings, so IsNull() could never report NULL. Store NULL cells as empty slots and make GetString() return NULL for them. Fixes #1165 --- modules/mysqlx/threading.cpp | 16 +++++++++++----- modules/sqlite/threading.cpp | 15 +++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/modules/mysqlx/threading.cpp b/modules/mysqlx/threading.cpp index 4f2cbfbade..f14a9be37d 100644 --- a/modules/mysqlx/threading.cpp +++ b/modules/mysqlx/threading.cpp @@ -502,7 +502,9 @@ const char *AtomicResult::GetString(unsigned int columnId) size_t idx = (m_CurRow * m_FieldCount) + columnId; - assert(m_Table[idx] != NULL); + // NULL cells are stored as empty slots (see CopyFrom()). + if (!m_Table[idx]) + return NULL; return m_Table[idx]->chars(); } @@ -583,12 +585,16 @@ void AtomicResult::CopyFrom(IResultSet *rs) row = rs->GetRow(); for (unsigned int i=0; iGetString(i); + if (!string) { - *m_Table[idx] = row->GetString(i); + // Keep NULL distinguishable from an empty string, ke::AString can't hold NULL. + delete m_Table[idx]; + m_Table[idx] = NULL; + } else if (m_Table[idx]) { + *m_Table[idx] = string; } else { - const char* string = row->GetString(i); - m_Table[idx] = new ke::AString(string ? string : ""); + m_Table[idx] = new ke::AString(string); } } rs->NextRow(); diff --git a/modules/sqlite/threading.cpp b/modules/sqlite/threading.cpp index 2512cbfd11..74a414608e 100644 --- a/modules/sqlite/threading.cpp +++ b/modules/sqlite/threading.cpp @@ -479,7 +479,9 @@ const char *AtomicResult::GetString(unsigned int columnId) size_t idx = (m_CurRow * m_FieldCount) + columnId; - assert(m_Table[idx] != NULL); + // NULL cells are stored as empty slots (see CopyFrom()). + if (!m_Table[idx]) + return NULL; return m_Table[idx]->chars(); } @@ -559,11 +561,16 @@ void AtomicResult::CopyFrom(IResultSet *rs) row = rs->GetRow(); for (unsigned int i=0; iGetString(i); + if (!string) { - *m_Table[idx] = row->GetString(i); + // Keep NULL distinguishable from an empty string, ke::AString can't hold NULL. + delete m_Table[idx]; + m_Table[idx] = NULL; + } else if (m_Table[idx]) { + *m_Table[idx] = string; } else { - m_Table[idx] = new ke::AString(row->GetString(i)); + m_Table[idx] = new ke::AString(string); } } rs->NextRow();