[Libreoffice-commits] core.git: Branch 'libreoffice-6-3' - connectivity/qa connectivity/source

Tamas Bunth (via logerrit) logerrit at kemper.freedesktop.org
Fri Aug 2 15:13:47 UTC 2019


 connectivity/qa/connectivity/mysql/mysql.cxx                     |   41 ++
 connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx |  164 +++++-----
 connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx |    7 
 3 files changed, 130 insertions(+), 82 deletions(-)

New commits:
commit 8c32cc30dbba3ee466b75628436ab790d6561e36
Author:     Tamas Bunth <tamas.bunth at collabora.co.uk>
AuthorDate: Mon Jul 29 13:27:38 2019 +0200
Commit:     Xisco Faulí <xiscofauli at libreoffice.org>
CommitDate: Fri Aug 2 17:12:57 2019 +0200

    mysqlc: Fix query of cursor position in result set
    
    Fix queries like "IsAfterLast" in result sets of prepared statements in
    the mysql driver.
    
    Cursor position is stored in the driver, since the mysql C driver does
    not support the query of the cursor position.
    
    The cursor position works the following way:
    - 0 means the cursor is on "BeforeFirst". In that state calling of
      getXXX() methods is user error.
    - 1 means the first row is already fetched.
    - n means the last fow is fetched, where n is the total number of rows
      in the result set.
    - Everything bigger than n is "AfterLast"
    
    Change-Id: I131f2042606897019cc0f868dbc4151faf4850ac
    Reviewed-on: https://gerrit.libreoffice.org/76549
    Tested-by: Jenkins
    Reviewed-by: Tamás Bunth <btomi96 at gmail.com>
    (cherry picked from commit 41d3be4a48ea2abe019cd4f9b51bef703a2dc454)
    Reviewed-on: https://gerrit.libreoffice.org/76585
    Reviewed-by: Xisco Faulí <xiscofauli at libreoffice.org>

diff --git a/connectivity/qa/connectivity/mysql/mysql.cxx b/connectivity/qa/connectivity/mysql/mysql.cxx
index 3e48ce5b3750..24af725b0110 100644
--- a/connectivity/qa/connectivity/mysql/mysql.cxx
+++ b/connectivity/qa/connectivity/mysql/mysql.cxx
@@ -54,6 +54,7 @@ public:
     void testDBMetaData();
     void testTimestampField();
     void testNumericConversionPrepared();
+    void testPreparedStmtIsAfterLast();
 
     CPPUNIT_TEST_SUITE(MysqlTestDriver);
     CPPUNIT_TEST(testDBConnection);
@@ -63,6 +64,7 @@ public:
     CPPUNIT_TEST(testDBMetaData);
     CPPUNIT_TEST(testTimestampField);
     CPPUNIT_TEST(testNumericConversionPrepared);
+    CPPUNIT_TEST(testPreparedStmtIsAfterLast);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -405,6 +407,45 @@ void MysqlTestDriver::testNumericConversionPrepared()
     xStatement->executeUpdate("DROP TABLE myTestTable");
 }
 
+/**
+ * Test cursor positioning method isAfterLast in case of using prepared
+ * statement.
+ */
+void MysqlTestDriver::testPreparedStmtIsAfterLast()
+{
+    Reference<XConnection> xConnection = m_xDriver->connect(m_sUrl, m_infos);
+    if (!xConnection.is())
+        CPPUNIT_ASSERT_MESSAGE("cannot connect to data source!", xConnection.is());
+    uno::Reference<XStatement> xStatement = xConnection->createStatement();
+    CPPUNIT_ASSERT(xStatement.is());
+    xStatement->executeUpdate("DROP TABLE IF EXISTS myTestTable");
+
+    // create test table
+    xStatement->executeUpdate("CREATE TABLE myTestTable (id INTEGER PRIMARY KEY)");
+    Reference<XPreparedStatement> xPrepared
+        = xConnection->prepareStatement(OUString{ "INSERT INTO myTestTable VALUES (?)" });
+    Reference<XParameters> xParams(xPrepared, UNO_QUERY);
+    constexpr int ROW_COUNT = 6;
+    for (int i = 0; i < ROW_COUNT; ++i)
+    {
+        xParams->setShort(1, i);
+        xPrepared->executeUpdate();
+    }
+
+    // query test table
+    xPrepared = xConnection->prepareStatement("SELECT id from myTestTable where id = 3");
+    Reference<XResultSet> xResultSet = xPrepared->executeQuery();
+
+    // There should be exactly one row, therefore IsAfterLast is false at first.
+    xResultSet->next();
+    CPPUNIT_ASSERT(!xResultSet->isAfterLast());
+
+    // attempt to fetch more data
+    bool hasData = xResultSet->next();
+    CPPUNIT_ASSERT(!hasData); // now we are on "AfterLast"
+    CPPUNIT_ASSERT(xResultSet->isAfterLast());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(MysqlTestDriver);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx
index 3b9e4a2aeb57..6c4711f65c71 100644
--- a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx
+++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.cxx
@@ -100,6 +100,62 @@ const std::type_index getTypeFromMysqlType(enum_field_types type)
 }
 }
 
+bool OPreparedResultSet::fetchResult()
+{
+    // allocate array if it does not exist
+    if (m_aData == nullptr)
+    {
+        m_aData.reset(new MYSQL_BIND[m_nColumnCount]);
+        memset(m_aData.get(), 0, m_nColumnCount * sizeof(MYSQL_BIND));
+        m_aMetaData.reset(new BindMetaData[m_nColumnCount]);
+    }
+    for (sal_Int32 i = 0; i < m_nColumnCount; ++i)
+    {
+        m_aMetaData[i].is_null = 0;
+        m_aMetaData[i].length = 0l;
+        m_aMetaData[i].error = 0;
+
+        m_aData[i].is_null = &m_aMetaData[i].is_null;
+        m_aData[i].buffer_length = m_aFields[i].type == MYSQL_TYPE_BLOB ? 0 : m_aFields[i].length;
+        m_aData[i].length = &m_aMetaData[i].length;
+        m_aData[i].error = &m_aMetaData[i].error;
+        m_aData[i].buffer = nullptr;
+        m_aData[i].buffer_type = m_aFields[i].type;
+
+        // allocates memory, if it is a fixed size type. If not then nullptr
+        mysqlc_sdbc_driver::allocateSqlVar(&m_aData[i].buffer, m_aData[i].buffer_type,
+                                           m_aFields[i].length);
+    }
+    mysql_stmt_bind_result(m_pStmt, m_aData.get());
+    int failure = mysql_stmt_fetch(m_pStmt);
+
+    for (sal_Int32 i = 0; i < m_nColumnCount; ++i)
+    {
+        if (*m_aData[i].error)
+        {
+            // expected if we have a BLOB, as buffer_length is set to 0. We want to
+            // fetch it piece by piece
+            // see https://bugs.mysql.com/file.php?id=12361&bug_id=33086
+            if (m_aData[i].buffer == nullptr)
+            {
+                m_aData[i].buffer_length = *m_aData[i].length;
+                m_aData[i].buffer = malloc(*m_aData[i].length);
+                mysql_stmt_fetch_column(m_pStmt, &m_aData[i], i, 0);
+            }
+        }
+    }
+
+    if (failure == 1)
+    {
+        MYSQL* pMysql = m_rConnection.getMysqlConnection();
+        mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(pMysql), mysql_errno(pMysql),
+                                                     *this, m_encoding);
+    }
+    else if (failure == MYSQL_NO_DATA)
+        return false;
+    return true;
+}
+
 OUString SAL_CALL OPreparedResultSet::getImplementationName()
 {
     return OUString("com.sun.star.sdbcx.mysqlc.ResultSet");
@@ -126,9 +182,12 @@ OPreparedResultSet::OPreparedResultSet(OConnection& rConn, OPreparedStatement* p
     , m_pStmt(pStmt)
     , m_encoding(rConn.getConnectionEncoding())
 {
-    m_nFieldCount = mysql_stmt_field_count(pStmt);
+    m_nColumnCount = mysql_stmt_field_count(pStmt);
     m_pResult = mysql_stmt_result_metadata(m_pStmt);
+    if (m_pResult != nullptr)
+        mysql_stmt_store_result(m_pStmt);
     m_aFields = mysql_fetch_fields(m_pResult);
+    m_nRowCount = mysql_stmt_num_rows(pStmt);
 }
 
 void OPreparedResultSet::disposing()
@@ -166,7 +225,7 @@ sal_Int32 SAL_CALL OPreparedResultSet::findColumn(const OUString& columnName)
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
     MYSQL_FIELD* pFields = mysql_fetch_fields(m_pResult);
-    for (sal_Int32 i = 0; i < m_nFieldCount; ++i)
+    for (sal_Int32 i = 0; i < m_nColumnCount; ++i)
     {
         if (columnName.equalsIgnoreAsciiCaseAscii(pFields[i].name))
             return i + 1; // sdbc indexes from 1
@@ -445,7 +504,7 @@ sal_Bool SAL_CALL OPreparedResultSet::isBeforeFirst()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    return m_nCurrentField == 0;
+    return m_nCurrentRow == 0;
 }
 
 sal_Bool SAL_CALL OPreparedResultSet::isAfterLast()
@@ -453,7 +512,7 @@ sal_Bool SAL_CALL OPreparedResultSet::isAfterLast()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    return m_nCurrentField >= m_nFieldCount;
+    return m_nCurrentRow > m_nRowCount;
 }
 
 sal_Bool SAL_CALL OPreparedResultSet::isFirst()
@@ -461,7 +520,7 @@ sal_Bool SAL_CALL OPreparedResultSet::isFirst()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    return m_nCurrentField == 1 && !isAfterLast();
+    return m_nCurrentRow == 1 && !isAfterLast();
 }
 
 sal_Bool SAL_CALL OPreparedResultSet::isLast()
@@ -469,16 +528,17 @@ sal_Bool SAL_CALL OPreparedResultSet::isLast()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    return mysql_field_tell(m_pResult) == static_cast<unsigned>(m_nFieldCount);
+    return m_nCurrentRow == m_nRowCount;
 }
 
 void SAL_CALL OPreparedResultSet::beforeFirst()
 {
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
+    mysql_stmt_data_seek(m_pStmt, 0);
 
-    mysqlc_sdbc_driver::throwFeatureNotImplementedException("OPreparedResultSet::beforeFirst",
-                                                            *this);
+    m_nCurrentRow = 0;
+    m_aData.reset();
 }
 
 void SAL_CALL OPreparedResultSet::afterLast()
@@ -509,6 +569,7 @@ sal_Bool SAL_CALL OPreparedResultSet::first()
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
     mysql_stmt_data_seek(m_pStmt, 0);
+    m_nCurrentRow = 0;
     next();
 
     return true;
@@ -519,7 +580,7 @@ sal_Bool SAL_CALL OPreparedResultSet::last()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    mysql_stmt_data_seek(m_pStmt, m_nFieldCount - 1);
+    mysql_stmt_data_seek(m_pStmt, m_nRowCount - 1);
     next();
 
     return true;
@@ -530,11 +591,10 @@ sal_Bool SAL_CALL OPreparedResultSet::absolute(sal_Int32 row)
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    sal_Int32 nFields = m_nFieldCount;
-    sal_Int32 nToGo = row < 0 ? nFields - row : row - 1;
+    sal_Int32 nToGo = row < 0 ? m_nRowCount - row : row - 1;
 
-    if (nToGo >= nFields)
-        nToGo = nFields - 1;
+    if (nToGo >= m_nRowCount)
+        nToGo = m_nRowCount - 1;
     if (nToGo < 0)
         nToGo = 0;
 
@@ -549,19 +609,18 @@ sal_Bool SAL_CALL OPreparedResultSet::relative(sal_Int32 row)
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    sal_Int32 nFields = m_nFieldCount;
     if (row == 0)
         return true;
 
-    sal_Int32 nToGo = m_nCurrentField + row;
-    if (nToGo >= nFields)
-        nToGo = nFields - 1;
+    sal_Int32 nToGo = m_nCurrentRow + row;
+    if (nToGo >= m_nRowCount)
+        nToGo = m_nRowCount - 1;
     if (nToGo < 0)
         nToGo = 0;
 
     mysql_stmt_data_seek(m_pStmt, nToGo);
     next();
-    m_nCurrentField += row;
+    m_nCurrentRow += row;
 
     return true;
 }
@@ -571,12 +630,12 @@ sal_Bool SAL_CALL OPreparedResultSet::previous()
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
 
-    if (m_nCurrentField <= 1)
+    if (m_nCurrentRow <= 1)
         return false;
 
-    mysql_stmt_data_seek(m_pStmt, m_nCurrentField - 2);
+    mysql_stmt_data_seek(m_pStmt, m_nCurrentRow - 2);
     next();
-    --m_nFieldCount;
+    --m_nCurrentRow;
     return true;
 }
 
@@ -616,68 +675,13 @@ sal_Bool SAL_CALL OPreparedResultSet::next()
 {
     MutexGuard aGuard(m_aMutex);
     checkDisposed(OPreparedResultSet_BASE::rBHelper.bDisposed);
-
-    bool bFirstRun = false;
-    // allocate array if it does not exist
-    if (m_aData == nullptr)
-    {
-        bFirstRun = true;
-        m_aData.reset(new MYSQL_BIND[m_nFieldCount]);
-        memset(m_aData.get(), 0, m_nFieldCount * sizeof(MYSQL_BIND));
-        m_aMetaData.reset(new BindMetaData[m_nFieldCount]);
-    }
-    for (sal_Int32 i = 0; i < m_nFieldCount; ++i)
-    {
-        m_aMetaData[i].is_null = 0;
-        m_aMetaData[i].length = 0l;
-        m_aMetaData[i].error = 0;
-
-        m_aData[i].is_null = &m_aMetaData[i].is_null;
-        m_aData[i].buffer_length = m_aFields[i].type == MYSQL_TYPE_BLOB ? 0 : m_aFields[i].length;
-        m_aData[i].length = &m_aMetaData[i].length;
-        m_aData[i].error = &m_aMetaData[i].error;
-        m_aData[i].buffer = nullptr;
-        m_aData[i].buffer_type = m_aFields[i].type;
-
-        // allocates memory, if it is a fixed size type. If not then nullptr
-        mysqlc_sdbc_driver::allocateSqlVar(&m_aData[i].buffer, m_aData[i].buffer_type,
-                                           m_aFields[i].length);
-    }
-    mysql_stmt_bind_result(m_pStmt, m_aData.get());
-    if (bFirstRun)
-        mysql_stmt_store_result(m_pStmt);
-    int failure = mysql_stmt_fetch(m_pStmt);
-
-    for (sal_Int32 i = 0; i < m_nFieldCount; ++i)
-    {
-        if (*m_aData[i].error)
-        {
-            // expected if we have a BLOB, as buffer_length is set to 0. We want to
-            // fetch it piece by piece
-            // see https://bugs.mysql.com/file.php?id=12361&bug_id=33086
-            if (m_aData[i].buffer == nullptr)
-            {
-                m_aData[i].buffer_length = *m_aData[i].length;
-                m_aData[i].buffer = malloc(*m_aData[i].length);
-                mysql_stmt_fetch_column(m_pStmt, &m_aData[i], i, 0);
-            }
-        }
-    }
-
-    if (failure == 1)
-    {
-        MYSQL* pMysql = m_rConnection.getMysqlConnection();
-        mysqlc_sdbc_driver::throwSQLExceptionWithMsg(mysql_error(pMysql), mysql_errno(pMysql),
-                                                     *this, m_encoding);
-    }
-    else if (failure == MYSQL_NO_DATA)
-        return false;
+    bool hasData = fetchResult();
 
     // current field cannot be asked as a number. We have to keep track it
     // manually.
-    m_nCurrentField += 1;
+    m_nCurrentRow += 1;
 
-    return true;
+    return hasData;
 }
 
 sal_Bool SAL_CALL OPreparedResultSet::wasNull()
@@ -1078,7 +1082,7 @@ void OPreparedResultSet::checkColumnIndex(sal_Int32 index)
 {
     if (!m_aData)
         throw SQLException("Cursor out of range", *this, OUString(), 1, Any());
-    if (index < 1 || index > static_cast<int>(m_nFieldCount))
+    if (index < 1 || index > static_cast<int>(m_nColumnCount))
     {
         /* static object for efficiency or thread safety is a problem ? */
         throw SQLException("index out of range", *this, OUString(), 1, Any());
diff --git a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx
index 5c8c231c44d8..4ff64d5b4320 100644
--- a/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx
+++ b/connectivity/source/drivers/mysqlc/mysqlc_prepared_resultset.hxx
@@ -70,8 +70,9 @@ class OPreparedResultSet final : public OBase_Mutex,
     MYSQL_FIELD* m_aFields;
 
     rtl_TextEncoding m_encoding;
-    sal_Int32 m_nCurrentField = 0;
-    sal_Int32 m_nFieldCount;
+    sal_Int32 m_nCurrentRow = 0;
+    sal_Int32 m_nColumnCount;
+    sal_Int32 m_nRowCount;
 
     // Use c style arrays, because we have to work with pointers
     // on these.
@@ -96,6 +97,8 @@ class OPreparedResultSet final : public OBase_Mutex,
     template <typename T> T retrieveValue(const sal_Int32 nColumnIndex);
     connectivity::ORowSetValue getRowSetValue(sal_Int32 nColumnIndex);
 
+    bool fetchResult();
+
     // you can't delete objects of this type
     virtual ~OPreparedResultSet() override = default;
 


More information about the Libreoffice-commits mailing list