Skip to content

Commit 4bd6cb9

Browse files
committed
Executor: Add migration functionality. Tests: add IntTest + FloatTest.
1 parent 27960e9 commit 4bd6cb9

File tree

12 files changed

+750
-242
lines changed

12 files changed

+750
-242
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ add_library(${OATPP_THIS_MODULE_NAME}
2424
oatpp-postgresql/QueryResult.cpp
2525
oatpp-postgresql/QueryResult.hpp
2626
oatpp-postgresql/Types.hpp
27-
)
27+
oatpp-postgresql/orm.hpp)
2828

2929
set_target_properties(${OATPP_THIS_MODULE_NAME} PROPERTIES
3030
CXX_STANDARD 11

src/oatpp-postgresql/Executor.cpp

Lines changed: 179 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,31 @@
2929

3030
#include "QueryResult.hpp"
3131

32+
#include "oatpp/orm/Transaction.hpp"
33+
3234
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
35+
#include "oatpp/core/macro/codegen.hpp"
3336

3437
#include <vector>
3538

3639
namespace oatpp { namespace postgresql {
3740

41+
namespace {
42+
43+
#include OATPP_CODEGEN_BEGIN(DTO)
44+
45+
class VersionRow : public oatpp::DTO {
46+
47+
DTO_INIT(VersionRow, DTO);
48+
49+
DTO_FIELD(Int64, version);
50+
51+
};
52+
53+
#include OATPP_CODEGEN_END(DTO)
54+
55+
}
56+
3857
Executor::QueryParams::QueryParams(const StringTemplate& queryTemplate,
3958
const std::unordered_map<oatpp::String, oatpp::Void>& params,
4059
const mapping::TypeMapper& typeMapper,
@@ -44,7 +63,11 @@ Executor::QueryParams::QueryParams(const StringTemplate& queryTemplate,
4463
auto extra = std::static_pointer_cast<ql_template::Parser::TemplateExtra>(queryTemplate.getExtraData());
4564

4665
query = extra->preparedTemplate->c_str();
47-
queryName = extra->templateName->c_str();
66+
if(extra->templateName) {
67+
queryName = extra->templateName->c_str();
68+
} else {
69+
queryName = nullptr;
70+
}
4871

4972
count = queryTemplate.getTemplateVariables().size();
5073

@@ -214,43 +237,188 @@ std::shared_ptr<orm::QueryResult> Executor::execute(const StringTemplate& queryT
214237

215238
}
216239

217-
std::shared_ptr<orm::QueryResult> Executor::begin(const std::shared_ptr<orm::Connection>& connection) {
240+
std::shared_ptr<orm::QueryResult> Executor::exec(const oatpp::String& statement,
241+
const std::shared_ptr<orm::Connection>& connection,
242+
bool useExecParams)
243+
{
218244

219245
std::shared_ptr<orm::Connection> conn = connection;
220246
if(!conn) {
221247
conn = getConnection();
222248
}
223249

224250
auto pgConnection = std::static_pointer_cast<postgresql::Connection>(conn);
225-
PGresult *qres = PQexec(pgConnection->getHandle(), "BEGIN");
251+
252+
PGresult *qres;
253+
if(useExecParams) {
254+
qres = PQexecParams(pgConnection->getHandle(),
255+
statement->c_str(),
256+
0 /* nParams */,
257+
nullptr /* paramTypes */,
258+
nullptr /* paramValues */,
259+
nullptr /* paramLengths */,
260+
nullptr /* paramFormats */,
261+
1 /* resultFormat */);
262+
} else {
263+
qres = PQexec(pgConnection->getHandle(), statement->c_str());
264+
}
265+
226266
return std::make_shared<QueryResult>(qres, pgConnection, m_connectionProvider, m_resultMapper);
227267

228268
}
229269

270+
std::shared_ptr<orm::QueryResult> Executor::begin(const std::shared_ptr<orm::Connection>& connection) {
271+
return exec("BEGIN", connection);
272+
}
273+
230274
std::shared_ptr<orm::QueryResult> Executor::commit(const std::shared_ptr<orm::Connection>& connection) {
231-
auto pgConnection = std::static_pointer_cast<postgresql::Connection>(connection);
232-
PGresult *qres = PQexec(pgConnection->getHandle(), "END");
233-
return std::make_shared<QueryResult>(qres, pgConnection, m_connectionProvider, m_resultMapper);
275+
if(!connection) {
276+
throw std::runtime_error("[oatpp::postgresql::Executor::commit()]: "
277+
"Error. Can't COMMIT - NULL connection.");
278+
}
279+
return exec("COMMIT", connection);
234280
}
235281

236282
std::shared_ptr<orm::QueryResult> Executor::rollback(const std::shared_ptr<orm::Connection>& connection) {
237-
auto pgConnection = std::static_pointer_cast<postgresql::Connection>(connection);
238-
PGresult *qres = PQexec(pgConnection->getHandle(), "ROLLBACK");
239-
return std::make_shared<QueryResult>(qres, pgConnection, m_connectionProvider, m_resultMapper);
283+
if(!connection) {
284+
throw std::runtime_error("[oatpp::postgresql::Executor::commit()]: "
285+
"Error. Can't ROLLBACK - NULL connection.");
286+
}
287+
return exec("ROLLBACK", connection);
288+
}
289+
290+
oatpp::String Executor::getSchemaVersionTableName(const oatpp::String& suffix) {
291+
data::stream::BufferOutputStream stream;
292+
stream << "oatpp_schema_version";
293+
if (suffix && suffix->getSize() > 0) {
294+
stream << "_" << suffix;
295+
}
296+
return stream.toString();
297+
}
298+
299+
std::shared_ptr<orm::QueryResult> Executor::updateSchemaVersion(v_int64 newVersion,
300+
const oatpp::String& suffix,
301+
const std::shared_ptr<orm::Connection>& connection)
302+
{
303+
data::stream::BufferOutputStream stream;
304+
stream
305+
<< "UPDATE "
306+
<< getSchemaVersionTableName(suffix) << " "
307+
<< "SET version=" << newVersion << ";";
308+
return exec(stream.toString(), connection, true);
240309
}
241310

242311
v_int64 Executor::getSchemaVersion(const oatpp::String& suffix,
243312
const std::shared_ptr<orm::Connection>& connection)
244313
{
245-
// TODO implement me!
314+
315+
std::shared_ptr<orm::QueryResult> result;
316+
317+
{
318+
data::stream::BufferOutputStream stream;
319+
stream << "CREATE TABLE IF NOT EXISTS " << getSchemaVersionTableName(suffix) << " (version BIGINT)";
320+
result = exec(stream.toString(), connection);
321+
if(!result->isSuccess()) {
322+
throw std::runtime_error("[oatpp::postgresql::Executor::getSchemaVersion()]: "
323+
"Error. Can't create schema version table. " + result->getErrorMessage()->std_str());
324+
}
325+
}
326+
327+
data::stream::BufferOutputStream stream;
328+
stream << "SELECT * FROM " << getSchemaVersionTableName(suffix);
329+
result = exec(stream.toString(), result->getConnection(), true);
330+
if(!result->isSuccess()) {
331+
throw std::runtime_error("[oatpp::postgresql::Executor::getSchemaVersion()]: "
332+
"Error. Can't get schema version. " + result->getErrorMessage()->std_str());
333+
}
334+
335+
auto rows = result->fetch<oatpp::Vector<oatpp::Object<VersionRow>>>();
336+
337+
if(rows->size() == 0) {
338+
339+
stream.setCurrentPosition(0);
340+
stream << "INSERT INTO " << getSchemaVersionTableName(suffix) << " (version) VALUES (0)";
341+
result = exec(stream.toString(), result->getConnection(), true);
342+
343+
if(result->isSuccess()) {
344+
return 0;
345+
}
346+
347+
throw std::runtime_error("[oatpp::postgresql::Executor::getSchemaVersion()]: "
348+
"Error. Can't init schema version. " + result->getErrorMessage()->std_str());
349+
350+
} else if(rows->size() == 1) {
351+
352+
auto row = rows[0];
353+
if(!row->version) {
354+
throw std::runtime_error("[oatpp::postgresql::Executor::getSchemaVersion()]: "
355+
"Error. The schema version table is corrupted - version is null.");
356+
}
357+
358+
return row->version;
359+
360+
}
361+
362+
throw std::runtime_error("[oatpp::postgresql::Executor::getSchemaVersion()]: "
363+
"Error. The schema version table is corrupted - multiple version rows.");
364+
246365
}
247366

248367
void Executor::migrateSchema(const oatpp::String& script,
249368
v_int64 newVersion,
250369
const oatpp::String& suffix,
251370
const std::shared_ptr<orm::Connection>& connection)
252371
{
253-
// TODO implement me!
372+
373+
if(!script) {
374+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: Error. Script is null.");
375+
}
376+
377+
if(!connection) {
378+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: Error. Connection is null.");
379+
}
380+
381+
auto currVersion = getSchemaVersion(suffix, connection);
382+
if(newVersion <= currVersion) {
383+
return;
384+
}
385+
386+
if(newVersion > currVersion + 1) {
387+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: Error. +1 version increment is allowed only.");
388+
}
389+
390+
if(script->getSize() == 0) {
391+
OATPP_LOGW("[oatpp::postgresql::Executor::migrateSchema()]", "Warning. Executing empty script for version %d", newVersion);
392+
}
393+
394+
{
395+
396+
orm::Transaction transaction(this, connection);
397+
398+
std::shared_ptr<orm::QueryResult> result;
399+
400+
result = exec(script, connection);
401+
if(!result->isSuccess()) {
402+
OATPP_LOGE("[oatpp::postgresql::Executor::migrateSchema()]",
403+
"Error. Migration failed for version %d. %s", newVersion, result->getErrorMessage()->c_str());
404+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: "
405+
"Error. Migration failed. " + result->getErrorMessage()->std_str());
406+
407+
}
408+
409+
result = updateSchemaVersion(newVersion, suffix, connection);
410+
411+
if(!result->isSuccess() || result->hasMoreToFetch() > 0) {
412+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: Error. Migration failed. Can't set new version.");
413+
}
414+
415+
result = transaction.commit();
416+
if(!result->isSuccess()) {
417+
throw std::runtime_error("[oatpp::postgresql::Executor::migrateSchema()]: Error. Migration failed. Can't commit.");
418+
}
419+
420+
}
421+
254422
}
255423

256424
}}

src/oatpp-postgresql/Executor.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ class Executor : public orm::Executor {
6565
};
6666

6767
private:
68+
69+
std::shared_ptr<orm::QueryResult> exec(const oatpp::String& statement,
70+
const std::shared_ptr<orm::Connection>& connection,
71+
bool useExecParams = false);
72+
73+
oatpp::String getSchemaVersionTableName(const oatpp::String& suffix);
74+
75+
std::shared_ptr<orm::QueryResult> updateSchemaVersion(v_int64 newVersion,
76+
const oatpp::String& suffix,
77+
const std::shared_ptr<orm::Connection>& connection);
78+
79+
private:
80+
6881
std::unique_ptr<Oid[]> getParamTypes(const StringTemplate& queryTemplate, const ParamsTypeMap& paramsTypeMap);
6982

7083
std::shared_ptr<QueryResult> prepareQuery(const StringTemplate& queryTemplate,

src/oatpp-postgresql/orm.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/***************************************************************************
2+
*
3+
* Project _____ __ ____ _ _
4+
* ( _ ) /__\ (_ _)_| |_ _| |_
5+
* )(_)( /(__)\ )( (_ _)(_ _)
6+
* (_____)(__)(__)(__) |_| |_|
7+
*
8+
*
9+
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
10+
*
11+
* Licensed under the Apache License, Version 2.0 (the "License");
12+
* you may not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing, software
18+
* distributed under the License is distributed on an "AS IS" BASIS,
19+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
* See the License for the specific language governing permissions and
21+
* limitations under the License.
22+
*
23+
***************************************************************************/
24+
25+
#ifndef oatpp_postgresql_orm_hpp
26+
#define oatpp_postgresql_orm_hpp
27+
28+
#include "Executor.hpp"
29+
#include "Types.hpp"
30+
31+
#include "oatpp/orm/SchemaMigration.hpp"
32+
#include "oatpp/orm/DbClient.hpp"
33+
#include "oatpp/core/macro/codegen.hpp"
34+
35+
#endif // oatpp_postgresql_orm_hpp

test/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
add_definitions (
2+
-DTEST_DB_URL="postgresql://postgres:db-pass@localhost:5432/postgres"
3+
-DTEST_DB_MIGRATION="${CMAKE_CURRENT_SOURCE_DIR}/oatpp-postgresql/migration/"
4+
)
5+
16
add_executable(module-tests
7+
oatpp-postgresql/types/IntTest.cpp
8+
oatpp-postgresql/types/IntTest.hpp
9+
oatpp-postgresql/types/FloatTest.cpp
10+
oatpp-postgresql/types/FloatTest.hpp
211
oatpp-postgresql/tests.cpp
3-
)
12+
)
413

514
set_target_properties(module-tests PROPERTIES
615
CXX_STANDARD 11
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
DROP TABLE IF EXISTS test_floats;
2+
3+
CREATE TABLE test_floats (
4+
f_decimal decimal,
5+
f_number numeric(1000),
6+
f_real real,
7+
f_double double precision
8+
);
9+
10+
INSERT INTO test_floats
11+
(f_decimal, f_number, f_real, f_double) VALUES (null, null, null, null);
12+
13+
INSERT INTO test_floats
14+
(f_decimal, f_number, f_real, f_double) VALUES (0, 0, 0, 0);
15+
16+
INSERT INTO test_floats
17+
(f_decimal, f_number, f_real, f_double) VALUES (0.1, 0.1, 0.1, 0.1);
18+
19+
INSERT INTO test_floats
20+
(f_decimal, f_number, f_real, f_double) VALUES (-0.1, -0.1, -0.1, -0.1);
21+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
DROP TABLE IF EXISTS test_ints;
2+
3+
CREATE TABLE test_ints (
4+
f_int16 smallint,
5+
f_int32 integer,
6+
f_int64 bigint
7+
);

0 commit comments

Comments
 (0)