office-gobmx/dbaccess/qa/unit/embeddeddb_performancetest.cxx
Xisco Fauli fb8f4b57ce DBTestBase: Add createDBDocument
it can be used later on when porting the java tests
to CppUnittest

Change-Id: I17420fdf490d979392f5aeb90c3b3bfb319d7764
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169274
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
2024-06-20 15:44:26 +02:00

339 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "dbtest_base.cxx"
#include <memory>
#include <osl/process.h>
#include <osl/time.h>
#include <rtl/ustrbuf.hxx>
#include <tools/stream.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/sdbc/XParameters.hpp>
#include <com/sun/star/sdbc/XPreparedStatement.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/sdbc/XStatement.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
static void normaliseTimeValue(TimeValue* pVal)
{
pVal->Seconds += pVal->Nanosec / 1000000000;
pVal->Nanosec %= 1000000000;
}
static void getTimeDifference(const TimeValue* pTimeStart,
const TimeValue* pTimeEnd,
TimeValue* pTimeDifference)
{
// We add 1 second to the nanoseconds to ensure that we get a positive number
// We have to normalise anyway so this doesn't cause any harm.
// (Seconds/Nanosec are both unsigned)
pTimeDifference->Seconds = pTimeEnd->Seconds - pTimeStart->Seconds - 1;
pTimeDifference->Nanosec = 1000000000 + pTimeEnd->Nanosec - pTimeStart->Nanosec;
normaliseTimeValue(pTimeDifference);
}
static OUString getPrintableTimeValue(const TimeValue* pTimeValue)
{
return OUString::number(
(sal_uInt64(pTimeValue->Seconds) * SAL_CONST_UINT64(1000000000)
+ sal_uInt64(pTimeValue->Nanosec))/ 1000000
);
}
/*
* The recommended way to run this test is:
* 'SAL_LOG="" DBA_PERFTEST=YES make CppunitTest_dbaccess_embeddeddb_performancetest'
* This blocks the unnecessary exception output and show only the performance data.
*
* You also need to create the file dbaccess/qa/unit/data/wordlist, this list cannot
* contain any unescaped apostrophes (since the words are used directly to assemble
* sql statement), apostrophes are escaped using a double apostrophe, i.e. ''.
* one easy way of generating a list is using:
* 'for WORD in $(aspell dump master); do echo ${WORD//\'/\'\'}; done > dbaccess/qa/unit/data/wordlist'
*
* Note that wordlist cannot have more than 220580 lines, this is due to a hard
* limit in our hsqldb version.
*
* Also note that this unit test "fails" when doing performance testing, this is
* since by default unit test output is hidden, and thus there is no way of
* reading the results.
*/
class EmbeddedDBPerformanceTest
: public DBTestBase
{
private:
static constexpr OUString our_sEnableTestEnvVar = u"DBA_PERFTEST"_ustr;
// We store the results and print them at the end due to the amount of warning
// noise present which otherwise obscures the results.
OUStringBuffer m_aOutputBuffer;
void printTimes(const TimeValue* pTime1, const TimeValue* pTime2, const TimeValue* pTime3);
void doPerformanceTestOnODB(const OUString& rDriverURL,
std::u16string_view rDBName,
const bool bUsePreparedStatement);
void setupTestTable(uno::Reference< XConnection > const & xConnection);
SvFileStream *getWordListStream();
// Individual Tests
void performPreparedStatementInsertTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName);
void performStatementInsertTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName);
void performReadTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName);
// Perform all tests on a given DB.
void testFirebird();
void testHSQLDB();
public:
void testPerformance();
CPPUNIT_TEST_SUITE(EmbeddedDBPerformanceTest);
CPPUNIT_TEST(testPerformance);
CPPUNIT_TEST_SUITE_END();
};
SvFileStream* EmbeddedDBPerformanceTest::getWordListStream()
{
OUString wlPath = createFileURL(u"wordlist");
return new SvFileStream(wlPath, StreamMode::READ);
}
void EmbeddedDBPerformanceTest::printTimes(
const TimeValue* pTime1,
const TimeValue* pTime2,
const TimeValue* pTime3)
{
m_aOutputBuffer.append(
getPrintableTimeValue(pTime1) + "\t" +
getPrintableTimeValue(pTime2) + "\t" +
getPrintableTimeValue(pTime3) + "\t"
"\n");
}
// TODO: we probably should create a document from scratch instead?
void EmbeddedDBPerformanceTest::testPerformance()
{
OUString sEnabled;
osl_getEnvironment(our_sEnableTestEnvVar.pData, &sEnabled.pData);
if (sEnabled.isEmpty())
return;
m_aOutputBuffer.append("---------------------\n");
testFirebird();
m_aOutputBuffer.append("---------------------\n");
testHSQLDB();
m_aOutputBuffer.append("---------------------\n");
fprintf(stdout, "Performance Test Results:\n");
fprintf(stdout, "%s",
OUStringToOString(m_aOutputBuffer.makeStringAndClear(),
RTL_TEXTENCODING_UTF8)
.getStr()
);
// We want the results printed, but unit test output is only printed on failure
// Hence we deliberately fail the test.
CPPUNIT_ASSERT(false);
}
void EmbeddedDBPerformanceTest::testFirebird()
{
m_aOutputBuffer.append("Standard Insert\n");
doPerformanceTestOnODB(u"sdbc:embedded:firebird"_ustr, u"Firebird", false);
m_aOutputBuffer.append("PreparedStatement Insert\n");
doPerformanceTestOnODB(u"sdbc:embedded:firebird"_ustr, u"Firebird", true);
}
void EmbeddedDBPerformanceTest::testHSQLDB()
{
m_aOutputBuffer.append("Standard Insert\n");
doPerformanceTestOnODB(u"sdbc:embedded:hsqldb"_ustr, u"HSQLDB", false);
m_aOutputBuffer.append("PreparedStatement Insert\n");
doPerformanceTestOnODB(u"sdbc:embedded:hsqldb"_ustr, u"HSQLDB", true);
}
/**
* Use an existing .odb to do performance tests on. The database cannot have
* a table of the name PFTESTTABLE.
*/
void EmbeddedDBPerformanceTest::doPerformanceTestOnODB(
const OUString& rDriverURL,
std::u16string_view rDBName,
const bool bUsePreparedStatement)
{
createDBDocument(rDriverURL);
uno::Reference< XOfficeDatabaseDocument > xDocument(mxComponent, UNO_QUERY_THROW);
uno::Reference< XConnection > xConnection = getConnectionForDocument(xDocument);
setupTestTable(xConnection);
if (bUsePreparedStatement)
performPreparedStatementInsertTest(xConnection, rDBName);
else
performStatementInsertTest(xConnection, rDBName);
performReadTest(xConnection, rDBName);
}
void EmbeddedDBPerformanceTest::setupTestTable(
uno::Reference< XConnection > const & xConnection)
{
uno::Reference< XStatement > xStatement = xConnection->createStatement();
// Although not strictly necessary we use quoted identifiers to reflect
// the fact that Base always uses quoted identifiers.
xStatement->execute(
u"CREATE TABLE \"PFTESTTABLE\" ( \"ID\" INTEGER NOT NULL PRIMARY KEY "
", \"STRINGCOLUMNA\" VARCHAR (50) "
")"_ustr);
xConnection->commit();
}
void EmbeddedDBPerformanceTest::performPreparedStatementInsertTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName)
{
uno::Reference< XPreparedStatement > xPreparedStatement =
xConnection->prepareStatement(
u"INSERT INTO \"PFTESTTABLE\" ( \"ID\", "
"\"STRINGCOLUMNA\" "
") VALUES ( ?, ? )"_ustr
);
uno::Reference< XParameters > xParameters(xPreparedStatement, UNO_QUERY_THROW);
std::unique_ptr< SvFileStream > pFile(getWordListStream());
OUString aWord;
sal_Int32 aID = 0;
TimeValue aStart, aMiddle, aEnd;
osl_getSystemTime(&aStart);
while (pFile->ReadByteStringLine(aWord, RTL_TEXTENCODING_UTF8))
{
xParameters->setInt(1, aID++);
xParameters->setString(2, aWord);
xPreparedStatement->execute();
}
osl_getSystemTime(&aMiddle);
xConnection->commit();
osl_getSystemTime(&aEnd);
TimeValue aTimeInsert, aTimeCommit, aTimeTotal;
getTimeDifference(&aStart, &aMiddle, &aTimeInsert);
getTimeDifference(&aMiddle, &aEnd, &aTimeCommit);
getTimeDifference(&aStart, &aEnd, &aTimeTotal);
m_aOutputBuffer.append(OUString::Concat("Insert: ") + rDBName + "\n");
printTimes(&aTimeInsert, &aTimeCommit, &aTimeTotal);
pFile->Close();
}
void EmbeddedDBPerformanceTest::performStatementInsertTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName)
{
uno::Reference< XStatement > xStatement =
xConnection->createStatement();
std::unique_ptr< SvFileStream > pFile(getWordListStream());
OUString aWord;
sal_Int32 aID = 0;
TimeValue aStart, aMiddle, aEnd;
osl_getSystemTime(&aStart);
while (pFile->ReadByteStringLine(aWord, RTL_TEXTENCODING_UTF8))
{
xStatement->execute(
"INSERT INTO \"PFTESTTABLE\" ( \"ID\", "
"\"STRINGCOLUMNA\" "
") VALUES ( "
+ OUString::number(aID++) + ", '" + aWord + "' )"
);
}
osl_getSystemTime(&aMiddle);
xConnection->commit();
osl_getSystemTime(&aEnd);
TimeValue aTimeInsert, aTimeCommit, aTimeTotal;
getTimeDifference(&aStart, &aMiddle, &aTimeInsert);
getTimeDifference(&aMiddle, &aEnd, &aTimeCommit);
getTimeDifference(&aStart, &aEnd, &aTimeTotal);
m_aOutputBuffer.append(OUString::Concat("Insert: ") + rDBName + "\n");
printTimes(&aTimeInsert, &aTimeCommit, &aTimeTotal);
pFile->Close();
}
void EmbeddedDBPerformanceTest::performReadTest(
uno::Reference< XConnection > const & xConnection,
std::u16string_view rDBName)
{
uno::Reference< XStatement > xStatement = xConnection->createStatement();
TimeValue aStart, aMiddle, aEnd;
osl_getSystemTime(&aStart);
uno::Reference< XResultSet > xResults = xStatement->executeQuery(u"SELECT * FROM PFTESTTABLE"_ustr);
osl_getSystemTime(&aMiddle);
uno::Reference< XRow > xRow(xResults, UNO_QUERY_THROW);
while (xResults->next())
{
xRow->getString(2);
}
osl_getSystemTime(&aEnd);
TimeValue aTimeSelect, aTimeIterate, aTimeTotal;
getTimeDifference(&aStart, &aMiddle, &aTimeSelect);
getTimeDifference(&aMiddle, &aEnd, &aTimeIterate);
getTimeDifference(&aStart, &aEnd, &aTimeTotal);
m_aOutputBuffer.append(OUString::Concat("Read from: ") + rDBName + "\n");
printTimes(&aTimeSelect, &aTimeIterate, &aTimeTotal);
}
CPPUNIT_TEST_SUITE_REGISTRATION(EmbeddedDBPerformanceTest);
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */