Skip to content

Commit

Permalink
Extend sql-db/hibernate-reactive and sql-db/vertx-sql with oracle
Browse files Browse the repository at this point in the history
Extend with reactive Oracle client.
Implementation of test plan for QUARKUS-1407:
https://github.com/quarkus-qe/quarkus-test-plans/blob/main/QUARKUS-1407.md
  • Loading branch information
jsmrcka committed Mar 23, 2022
1 parent de851af commit 5730b01
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ Covered DBs:
- MySQL
- DB2
- MSSQL
- Oracle

### `sql-db/vertx-sql`
Quarkus / Vertx SQL exploratory testing. A flight search engine in order to test Quarkus Reactive SQL extensions. A detailed description can be found in sql-db/vertx-sql/README.md
Expand Down
4 changes: 4 additions & 0 deletions sql-db/hibernate-reactive/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-mssql-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-oracle-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.quarkus.ts.reactive;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import io.quarkus.test.bootstrap.OracleService;
import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.QuarkusScenario;
import io.quarkus.test.services.Container;
import io.quarkus.test.services.QuarkusApplication;

@QuarkusScenario
public class OracleDatabaseIT extends AbstractReactiveDatabaseIT {

private static final String ORACLE_USER = "quarkus_test";
private static final String ORACLE_PASSWORD = "quarkus_test";
private static final String ORACLE_DATABASE = "quarkus_test";
static final int ORACLE_PORT = 1521;

@Container(image = "${oracle.image}", port = ORACLE_PORT, expectedLog = "DATABASE IS READY TO USE!")
static OracleService database = new ProvisionalOracleService()
.with(ORACLE_USER, ORACLE_PASSWORD, ORACLE_DATABASE);

@QuarkusApplication
static RestService app = new RestService().withProperties("oracle.properties")
.withProperty("quarkus.datasource.username", ORACLE_USER)
.withProperty("quarkus.datasource.password", ORACLE_PASSWORD)
.withProperty("quarkus.datasource.reactive.url", database::getReactiveUrl);

@Override
protected RestService getApp() {
return app;
}

@Test
@Override
@Disabled
public void ensureSessionIsPropagatedOnReactiveTransactions() {
// TODO: investigate / file an issue?
}

// TODO: Remove after https://github.com/quarkus-qe/quarkus-test-framework/pull/428 is available in TF
private static class ProvisionalOracleService extends OracleService {
@Override
public String getReactiveUrl() {
return getHost().replace("http://", getJdbcName() + ":thin:@") + ":" + getPort() + ":" + getDatabase();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.ts.reactive.openshift;

import org.junit.jupiter.api.Disabled;

import io.quarkus.test.scenarios.OpenShiftScenario;
import io.quarkus.ts.reactive.OracleDatabaseIT;

@Disabled("https://issues.redhat.com/browse/QUARKUS-1164?focusedCommentId=18977100&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-18977100")
@OpenShiftScenario
public class OpenShiftOracleIT extends OracleDatabaseIT {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
quarkus.datasource.db-kind=oracle
quarkus.hibernate-orm.sql-load-script=oracle_import.sql
quarkus.hibernate-orm.database.generation=create
11 changes: 11 additions & 0 deletions sql-db/hibernate-reactive/src/test/resources/oracle_import.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
DROP TABLE authors;
CREATE TABLE AUTHORS(ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, NAME VARCHAR(100) NOT NULL);
INSERT INTO authors(id,name) VALUES (1, 'Homer');
INSERT INTO authors(id,name) VALUES (2, 'Vern');
INSERT INTO authors(id,name) VALUES (3, 'Dlugi');
INSERT INTO authors(id,name) VALUES (4, 'Kahneman');
DROP TABLE books;
CREATE TABLE books(id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, author INT references authors(id) , title VARCHAR(500) NOT NULL, isbn VARCHAR(50));
INSERT INTO books(author, title) VALUES (3, 'Slovník');
INSERT INTO books(author, title, isbn) VALUES (4, 'Thinking fast and slow', '978-0374275631');
INSERT INTO books(author, title) VALUES (4, 'Attention and Effort');
3 changes: 2 additions & 1 deletion sql-db/vertx-sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ To compile and run this demo you will need:
- PostgreSQL
- MySQL
- DB2
- Oracle

## Scope of the testing

Quarkus / Vertx SQL exploratory testing

* Quarkus Vertx Reactive Routes (annotations)
* OpenAPI from annotations
* Reactive SQL / PostgreSQL / MySQL / DB2
* Reactive SQL / PostgreSQL / MySQL / DB2 / Oracle
* Select / insert statements
* Transaction statement between several tables
* Error mapping through routes
Expand Down
8 changes: 8 additions & 0 deletions sql-db/vertx-sql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mssql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-oracle-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-oracle</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.vertx.mutiny.db2client.DB2Pool;
import io.vertx.mutiny.mssqlclient.MSSQLPool;
import io.vertx.mutiny.mysqlclient.MySQLPool;
import io.vertx.mutiny.oracleclient.OraclePool;
import io.vertx.mutiny.pgclient.PgPool;

/**
Expand Down Expand Up @@ -63,6 +64,11 @@ public class Application {
@IfBuildProfile("mssql")
MSSQLPool mssql;

@Inject
@Named("oracle")
@IfBuildProfile("oracle")
OraclePool oracle;

void onStart(@Observes StartupEvent ev) {
LOGGER.info("The application is starting with profile " + ProfileManager.getActiveProfile());

Expand All @@ -81,6 +87,8 @@ synchronized DbPoolService pool() {
return new DbPoolService(db2, "\"" + db2DbName + "\"", selectedDB);
case "mssql":
return new DbPoolService(mssql, null, selectedDB);
case "oracle":
return new DbPoolService(oracle, null, selectedDB);
default:
return new DbPoolService(postgresql, postgresqlDbName, selectedDB);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void setNif(String nif) {

public Uni<Long> save(DbPoolService sqlClient) {
return SqlClientHelper.inTransactionUni(sqlClient, tx -> address.save(sqlClient).onItem().transformToUni(address_id -> {
List<String> fieldsNames = Arrays.asList("nif,name,last_name,contact_number,created_at, address_id".split(","));
List<String> fieldsNames = Arrays.asList("nif,name,last_name,contact_number,created_at,address_id".split(","));
List<Object> fieldsValues = Stream
.of(getNif(), getName(), getLastName(), getContactNumber(), getCreatedAt(), address_id)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import javax.validation.ConstraintViolationException;

import io.quarkus.vertx.web.Route;
import io.vertx.core.VertxException;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
Expand Down Expand Up @@ -33,10 +34,18 @@ void databaseDb2ConstraintFailure(DB2Exception e, HttpServerResponse response) {
}

@Route(path = "/*", type = Route.HandlerType.FAILURE, produces = "application/json")
void databaseDb2ConstraintFailure(MSSQLException e, HttpServerResponse response) {
void databaseMssqlConstraintFailure(MSSQLException e, HttpServerResponse response) {
response.setStatusCode(400).end(Json.encode(new JsonObject().put("msg", e.getMessage())));
}

// TODO: https://github.com/quarkusio/quarkus/issues/24264 - No Oracle-specific exception available
@Route(path = "/*", type = Route.HandlerType.FAILURE, produces = "application/json")
void databaseOracleConstraintFailure(VertxException e, HttpServerResponse response) {
if (e.getMessage().contains("Error Msg = ORA")) {
response.setStatusCode(400).end(Json.encode(new JsonObject().put("msg", e.getMessage())));
}
}

@Route(path = "/*", type = Route.HandlerType.FAILURE, produces = "application/json")
public void exceptions(ConstraintViolationException e, HttpServerResponse res) {
res.setStatusCode(400).end(handler -> e.getConstraintViolations().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
import java.util.stream.Collectors;

import io.smallrye.mutiny.Uni;
import io.vertx.core.json.JsonArray;
import io.vertx.mutiny.oracleclient.OracleClient;
import io.vertx.mutiny.sqlclient.Pool;
import io.vertx.mutiny.sqlclient.SqlClientHelper;
import io.vertx.oracleclient.OraclePrepareOptions;
import io.vertx.sqlclient.PropertyKind;

public class DbPoolService extends Pool {

private final static PropertyKind<Long> LAST_INSERTED_ID = PropertyKind.create("last-inserted-id", Long.class);
public static final String ORACLE_AUTO_GENERATED_KEY_NAME = "ID";
public static final OraclePrepareOptions ORACLE_AUTO_GENERATED_KEYS_OPTIONS = new OraclePrepareOptions()
.setAutoGeneratedKeysIndexes(new JsonArray().add(ORACLE_AUTO_GENERATED_KEY_NAME));
private final String databaseName;
private final String selectedDb;

Expand All @@ -34,6 +40,8 @@ public Uni<Long> save(String tableName, List<String> fieldsNames, List<Object> f
return saveDb2(tableName, fieldsNames, fieldsValues);
case "mssql":
return saveMS(tableName, fieldsNames, fieldsValues);
case "oracle":
return saveOracle(tableName, fieldsNames, fieldsValues);
default:
return savePg(tableName, fieldsNames, fieldsValues);
}
Expand Down Expand Up @@ -91,6 +99,20 @@ protected Uni<Long> saveDb2(String tableName, List<String> fieldsNames, List<Obj
});
}

protected Uni<Long> saveOracle(String tableName, List<String> fieldsNames, List<Object> fieldsValues) {
return SqlClientHelper.inTransactionUni(this, tx -> {
String fields = tableFieldsToString(fieldsNames);
String values = tableFieldsValuesToString(fieldsValues);

return tx
.preparedQuery("INSERT INTO " + getTableName(tableName) + " (" + fields + ") VALUES (" + values + ")",
ORACLE_AUTO_GENERATED_KEYS_OPTIONS)
.execute()
.onItem()
.transform(rows -> rows.property(OracleClient.GENERATED_KEYS).getLong(ORACLE_AUTO_GENERATED_KEY_NAME));
});
}

private String tableFieldsToString(List<String> fieldsNames) {
return String.join(",", fieldsNames);
}
Expand Down
13 changes: 13 additions & 0 deletions sql-db/vertx-sql/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,16 @@ quarkus.datasource.mssql.reactive.url=sqlserver://localhost:1433/msdb
quarkus.flyway.mssql.schemas=msdb
quarkus.datasource.mssql.jdbc.url=jdbc:sqlserver://localhost:1433;databaseName=msdb
quarkus.flyway.mssql.locations=db/migration/mssql,db/migration/common

## Oracle
## Database
%oracle.app.selected.db=oracle
quarkus.datasource.oracle.reactive=true
quarkus.datasource.oracle.db-kind=oracle
quarkus.datasource.oracle.username=test
quarkus.datasource.oracle.password=test
quarkus.datasource.oracle.reactive.url=oracle:thin:@localhost:1521:amadeus

## Flyway
quarkus.datasource.oracle.jdbc.url=jdbc:oracle:thin:@localhost:1521:amadeus
quarkus.flyway.oracle.locations=db/migration/oracle,db/migration/common
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

CREATE TABLE airports (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
iata_code VARCHAR(100) NOT NULL UNIQUE,
city VARCHAR(100) NOT NULL
);

CREATE TABLE airlines (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
iata_code VARCHAR(100) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
infant_price FLOAT(7)
);

CREATE TABLE flights (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
origin VARCHAR(100) NOT NULL,
destination VARCHAR(100) NOT NULL,
flight_code VARCHAR(100) NOT NULL,
base_price SMALLINT NOT NULL
);

CREATE TABLE pricingRules (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
days_to_departure SMALLINT NOT NULL,
until SMALLINT NOT NULL,
percentage SMALLINT NOT NULL
);

CREATE TABLE address (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
street VARCHAR(300) NOT NULL,
block_number VARCHAR(20) NOT NULL,
zip_code VARCHAR(20) NOT NULL,
city VARCHAR(150) NOT NULL,
country VARCHAR(200) NOT NULL,
created_at INT NOT NULL,
updated_at INT
);

CREATE TABLE passenger (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
nif VARCHAR(15) NOT NULL,
name VARCHAR(25) NOT NULL,
last_name VARCHAR(55) NOT NULL,
contact_number VARCHAR(20) NOT NULL,
created_at INT NOT NULL,
updated_at INT,
address_id NUMBER,
CONSTRAINT fk_address FOREIGN KEY(address_id) REFERENCES address(id) ON DELETE SET NULL
);

CREATE TABLE basket (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
flight VARCHAR(10) NOT NULL,
price NUMERIC NOT NULL,
created_at INT NOT NULL,
updated_at INT,
passenger_id NUMBER,
CONSTRAINT fk_passenger FOREIGN KEY(passenger_id) REFERENCES passenger(id) ON DELETE SET NULL
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.qe.vertx.sql.handlers;

import io.quarkus.test.bootstrap.OracleService;
import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.QuarkusScenario;
import io.quarkus.test.services.Container;
import io.quarkus.test.services.QuarkusApplication;

@QuarkusScenario
public class OracleHandlerIT extends CommonTestCases {
private static final int ORACLE_PORT = 1521;
private static final String DATABASE = "amadeus";

@Container(image = "${oracle.image}", port = ORACLE_PORT, expectedLog = "DATABASE IS READY TO USE!")
static OracleService oracle = new ProvisionalOracleService()
.with("test", "test", DATABASE);

@QuarkusApplication
static final RestService app = new RestService()
.withProperty("quarkus.datasource.oracle.username", oracle::getUser)
.withProperty("quarkus.datasource.oracle.password", oracle::getPassword)
.withProperty("quarkus.datasource.oracle.jdbc.url", oracle::getJdbcUrl)
.withProperty("quarkus.datasource.oracle.reactive.url", oracle::getReactiveUrl)
.withProperty("app.selected.db", "oracle")
// Enable Flyway for Oracle
.withProperty("quarkus.flyway.oracle.migrate-at-start", "true");

// TODO: Remove after https://github.com/quarkus-qe/quarkus-test-framework/pull/428 is available in TF
private static class ProvisionalOracleService extends OracleService {
@Override
public String getReactiveUrl() {
return getHost().replace("http://", getJdbcName() + ":thin:@") + ":" + getPort() + ":" + getDatabase();
}
}
}

0 comments on commit 5730b01

Please sign in to comment.