1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "dbtest_base.cxx"
13 #include <osl/file.hxx>
14 #include <osl/process.h>
16 #include <rtl/ustrbuf.hxx>
17 #include <tools/stream.hxx>
18 #include <unotools/tempfile.hxx>
20 #include <com/sun/star/beans/XPropertySet.hpp>
21 #include <com/sun/star/frame/XStorable.hpp>
22 #include <com/sun/star/lang/XComponent.hpp>
23 #include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp>
24 #include <com/sun/star/sdbc/XColumnLocate.hpp>
25 #include <com/sun/star/sdbc/XConnection.hpp>
26 #include <com/sun/star/sdbc/XParameters.hpp>
27 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
28 #include <com/sun/star/sdbc/XResultSet.hpp>
29 #include <com/sun/star/sdbc/XRow.hpp>
30 #include <com/sun/star/sdbc/XStatement.hpp>
31 #include <com/sun/star/util/XCloseable.hpp>
33 using namespace ::com::sun::star
;
34 using namespace ::com::sun::star::beans
;
35 using namespace ::com::sun::star::frame
;
36 using namespace ::com::sun::star::lang
;
37 using namespace ::com::sun::star::sdb
;
38 using namespace ::com::sun::star::sdbc
;
39 using namespace ::com::sun::star::uno
;
41 static void normaliseTimeValue(TimeValue
* pVal
)
43 pVal
->Seconds
+= pVal
->Nanosec
/ 1000000000;
44 pVal
->Nanosec
%= 1000000000;
47 static void getTimeDifference(const TimeValue
* pTimeStart
,
48 const TimeValue
* pTimeEnd
,
49 TimeValue
* pTimeDifference
)
51 // We add 1 second to the nanoseconds to ensure that we get a positive number
52 // We have to normalise anyway so this doesn't cause any harm.
53 // (Seconds/Nanosec are both unsigned)
54 pTimeDifference
->Seconds
= pTimeEnd
->Seconds
- pTimeStart
->Seconds
- 1;
55 pTimeDifference
->Nanosec
= 1000000000 + pTimeEnd
->Nanosec
- pTimeStart
->Nanosec
;
56 normaliseTimeValue(pTimeDifference
);
59 static OUString
getPrintableTimeValue(const TimeValue
* pTimeValue
)
61 return OUString::number(
62 (sal_uInt64(pTimeValue
->Seconds
) * SAL_CONST_UINT64(1000000000)
63 + sal_uInt64(pTimeValue
->Nanosec
))/ 1000000
68 * The recommended way to run this test is:
69 * 'SAL_LOG="" DBA_PERFTEST=YES make CppunitTest_dbaccess_embeddeddb_performancetest'
70 * This blocks the unnecessary exception output and show only the performance data.
72 * You also need to create the file dbaccess/qa/unit/data/wordlist, this list cannot
73 * contain any unescaped apostrophes (since the words are used directly to assemble
74 * sql statement), apostrophes are escaped using a double apostrophe, i.e. ''.
75 * one easy way of generating a list is using:
76 * 'for WORD in $(aspell dump master); do echo ${WORD//\'/\'\'}; done > dbaccess/qa/unit/data/wordlist'
78 * Note that wordlist cannot have more than 220580 lines, this is due to a hard
79 * limit in our hsqldb version.
81 * Also note that this unit test "fails" when doing performance testing, this is
82 * since by default unit test output is hidden, and thus there is no way of
83 * reading the results.
85 class EmbeddedDBPerformanceTest
89 static const char our_sEnableTestEnvVar
[];
91 // We store the results and print them at the end due to the amount of warning
92 // noise present which otherwise obscures the results.
93 OUStringBuffer m_aOutputBuffer
;
95 void printTimes(const TimeValue
* pTime1
, const TimeValue
* pTime2
, const TimeValue
* pTime3
);
97 void doPerformanceTestOnODB(const OUString
& rDriverURL
,
98 const OUString
& rDBName
,
99 const bool bUsePreparedStatement
);
101 void setupTestTable(uno::Reference
< XConnection
> const & xConnection
);
103 SvFileStream
*getWordListStream();
106 void performPreparedStatementInsertTest(
107 uno::Reference
< XConnection
> const & xConnection
,
108 const OUString
& rDBName
);
109 void performStatementInsertTest(
110 uno::Reference
< XConnection
> const & xConnection
,
111 const OUString
& rDBName
);
112 void performReadTest(
113 uno::Reference
< XConnection
> const & xConnection
,
114 const OUString
& rDBName
);
116 // Perform all tests on a given DB.
121 void testPerformance();
123 CPPUNIT_TEST_SUITE(EmbeddedDBPerformanceTest
);
124 CPPUNIT_TEST(testPerformance
);
125 CPPUNIT_TEST_SUITE_END();
128 SvFileStream
* EmbeddedDBPerformanceTest::getWordListStream()
131 createFileURL("wordlist", wlPath
);
132 return new SvFileStream(wlPath
, StreamMode::READ
);
135 void EmbeddedDBPerformanceTest::printTimes(
136 const TimeValue
* pTime1
,
137 const TimeValue
* pTime2
,
138 const TimeValue
* pTime3
)
141 .append(getPrintableTimeValue(pTime1
)).append("\t")
142 .append(getPrintableTimeValue(pTime2
)).append("\t")
143 .append(getPrintableTimeValue(pTime3
)).append("\t")
147 const char EmbeddedDBPerformanceTest::our_sEnableTestEnvVar
[] = "DBA_PERFTEST";
149 // TODO: we probably should create a document from scratch instead?
151 void EmbeddedDBPerformanceTest::testPerformance()
154 osl_getEnvironment(OUString(our_sEnableTestEnvVar
).pData
, &sEnabled
.pData
);
156 if (sEnabled
.isEmpty())
159 m_aOutputBuffer
.append("---------------------\n");
161 m_aOutputBuffer
.append("---------------------\n");
163 m_aOutputBuffer
.append("---------------------\n");
165 fprintf(stdout
, "Performance Test Results:\n");
166 fprintf(stdout
, "%s",
167 OUStringToOString(m_aOutputBuffer
.makeStringAndClear(),
168 RTL_TEXTENCODING_UTF8
)
172 // We want the results printed, but unit test output is only printed on failure
173 // Hence we deliberately fail the test.
174 CPPUNIT_ASSERT(false);
177 void EmbeddedDBPerformanceTest::testFirebird()
180 m_aOutputBuffer
.append("Standard Insert\n");
181 doPerformanceTestOnODB("sdbc:embedded:firebird", "Firebird", false);
182 m_aOutputBuffer
.append("PreparedStatement Insert\n");
183 doPerformanceTestOnODB("sdbc:embedded:firebird", "Firebird", true);
186 void EmbeddedDBPerformanceTest::testHSQLDB()
188 m_aOutputBuffer
.append("Standard Insert\n");
189 doPerformanceTestOnODB("sdbc:embedded:hsqldb", "HSQLDB", false);
190 m_aOutputBuffer
.append("PreparedStatement Insert\n");
191 doPerformanceTestOnODB("sdbc:embedded:hsqldb", "HSQLDB", true);
195 * Use an existing .odb to do performance tests on. The database cannot have
196 * a table of the name PFTESTTABLE.
198 void EmbeddedDBPerformanceTest::doPerformanceTestOnODB(
199 const OUString
& rDriverURL
,
200 const OUString
& rDBName
,
201 const bool bUsePreparedStatement
)
203 ::utl::TempFile aFile
;
204 aFile
.EnableKillingFile();
207 uno::Reference
< XOfficeDatabaseDocument
> xDocument(
208 m_xSFactory
->createInstance("com.sun.star.sdb.OfficeDatabaseDocument"),
210 uno::Reference
< XStorable
> xStorable(xDocument
, UNO_QUERY_THROW
);
212 uno::Reference
< XDataSource
> xDataSource
= xDocument
->getDataSource();
213 uno::Reference
< XPropertySet
> xPropertySet(xDataSource
, UNO_QUERY_THROW
);
214 xPropertySet
->setPropertyValue("URL", Any(rDriverURL
));
216 xStorable
->storeAsURL(aFile
.GetURL(), uno::Sequence
< beans::PropertyValue
>());
219 uno::Reference
< XOfficeDatabaseDocument
> xDocument(
220 loadFromDesktop(aFile
.GetURL()), UNO_QUERY_THROW
);
222 uno::Reference
< XConnection
> xConnection
=
223 getConnectionForDocument(xDocument
);
225 setupTestTable(xConnection
);
227 if (bUsePreparedStatement
)
228 performPreparedStatementInsertTest(xConnection
, rDBName
);
230 performStatementInsertTest(xConnection
, rDBName
);
232 performReadTest(xConnection
, rDBName
);
235 void EmbeddedDBPerformanceTest::setupTestTable(
236 uno::Reference
< XConnection
> const & xConnection
)
238 uno::Reference
< XStatement
> xStatement
= xConnection
->createStatement();
240 // Although not strictly necessary we use quoted identifiers to reflect
241 // the fact that Base always uses quoted identifiers.
243 "CREATE TABLE \"PFTESTTABLE\" ( \"ID\" INTEGER NOT NULL PRIMARY KEY "
244 ", \"STRINGCOLUMNA\" VARCHAR (50) "
247 xConnection
->commit();
250 void EmbeddedDBPerformanceTest::performPreparedStatementInsertTest(
251 uno::Reference
< XConnection
> const & xConnection
,
252 const OUString
& rDBName
)
254 uno::Reference
< XPreparedStatement
> xPreparedStatement
=
255 xConnection
->prepareStatement(
256 "INSERT INTO \"PFTESTTABLE\" ( \"ID\", "
261 uno::Reference
< XParameters
> xParameters(xPreparedStatement
, UNO_QUERY_THROW
);
263 std::unique_ptr
< SvFileStream
> pFile(getWordListStream());
268 TimeValue aStart
, aMiddle
, aEnd
;
269 osl_getSystemTime(&aStart
);
271 while (pFile
->ReadByteStringLine(aWord
, RTL_TEXTENCODING_UTF8
))
273 xParameters
->setInt(1, aID
++);
274 xParameters
->setString(2, aWord
);
275 xPreparedStatement
->execute();
277 osl_getSystemTime(&aMiddle
);
278 xConnection
->commit();
279 osl_getSystemTime(&aEnd
);
282 TimeValue aTimeInsert
, aTimeCommit
, aTimeTotal
;
283 getTimeDifference(&aStart
, &aMiddle
, &aTimeInsert
);
284 getTimeDifference(&aMiddle
, &aEnd
, &aTimeCommit
);
285 getTimeDifference(&aStart
, &aEnd
, &aTimeTotal
);
286 m_aOutputBuffer
.append("Insert: ").append(rDBName
).append("\n");
287 printTimes(&aTimeInsert
, &aTimeCommit
, &aTimeTotal
);
292 void EmbeddedDBPerformanceTest::performStatementInsertTest(
293 uno::Reference
< XConnection
> const & xConnection
,
294 const OUString
& rDBName
)
296 uno::Reference
< XStatement
> xStatement
=
297 xConnection
->createStatement();
299 std::unique_ptr
< SvFileStream
> pFile(getWordListStream());
304 TimeValue aStart
, aMiddle
, aEnd
;
305 osl_getSystemTime(&aStart
);
307 while (pFile
->ReadByteStringLine(aWord
, RTL_TEXTENCODING_UTF8
))
310 "INSERT INTO \"PFTESTTABLE\" ( \"ID\", "
313 + OUString::number(aID
++) + ", '" + aWord
+ "' )"
316 osl_getSystemTime(&aMiddle
);
317 xConnection
->commit();
318 osl_getSystemTime(&aEnd
);
320 TimeValue aTimeInsert
, aTimeCommit
, aTimeTotal
;
321 getTimeDifference(&aStart
, &aMiddle
, &aTimeInsert
);
322 getTimeDifference(&aMiddle
, &aEnd
, &aTimeCommit
);
323 getTimeDifference(&aStart
, &aEnd
, &aTimeTotal
);
324 m_aOutputBuffer
.append("Insert: ").append(rDBName
).append("\n");
325 printTimes(&aTimeInsert
, &aTimeCommit
, &aTimeTotal
);
330 void EmbeddedDBPerformanceTest::performReadTest(
331 uno::Reference
< XConnection
> const & xConnection
,
332 const OUString
& rDBName
)
334 uno::Reference
< XStatement
> xStatement
= xConnection
->createStatement();
336 TimeValue aStart
, aMiddle
, aEnd
;
337 osl_getSystemTime(&aStart
);
339 uno::Reference
< XResultSet
> xResults
= xStatement
->executeQuery("SELECT * FROM PFTESTTABLE");
341 osl_getSystemTime(&aMiddle
);
343 uno::Reference
< XRow
> xRow(xResults
, UNO_QUERY_THROW
);
345 while (xResults
->next())
349 osl_getSystemTime(&aEnd
);
351 TimeValue aTimeSelect
, aTimeIterate
, aTimeTotal
;
352 getTimeDifference(&aStart
, &aMiddle
, &aTimeSelect
);
353 getTimeDifference(&aMiddle
, &aEnd
, &aTimeIterate
);
354 getTimeDifference(&aStart
, &aEnd
, &aTimeTotal
);
355 m_aOutputBuffer
.append("Read from: ").append(rDBName
).append("\n");
356 printTimes(&aTimeSelect
, &aTimeIterate
, &aTimeTotal
);
359 CPPUNIT_TEST_SUITE_REGISTRATION(EmbeddedDBPerformanceTest
);
361 CPPUNIT_PLUGIN_IMPLEMENT();
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */