diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8cdc7..97998ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,20 @@ -## 2.5.1 (November 26, 2021) -* Updated the sailor version to 3.3.6 -* Reduced the size of component icon file +# JDBC Component -## 2.5.0 (October 1, 2021) +## 2.4.1 (May 15, 2020) -* Add New Select action -* Deprecate old Select action +* Upgrade sailor to 3.1.0 -## 2.4.5 (September 1, 2021) +## 2.4.0 (March 12, 2020) -Open only one connection pool per one execution and reuse it +* Add Firebird RDBMS support -## 2.4.4 (August 12, 2021) +## 2.3.2 (October 21, 2019) -* Remove dependencyCheckAnalyze task - -## 2.4.3 (February 12, 2021) - -* Update sailor version to 3.3.2 - -## 2.4.2 (November 20, 2020) - -* Update sailor version to 3.3.1 -* Annual audit of the component code to check if it exposes a sensitive data in the logs -* Annual dependencies vulnerabilities audit +* Add rebound mechanism in case of deadlocks for actions: Insert, UpsertByPK, DeleteByPK -## 2.4.0 (October 17, 2019) +## 2.4.0 (october 17, 2019) * Add `Custom Query` action -* Add rebound mechanism in case of deadlocks for actions: Insert, UpsertByPK, DeleteByPK ## 2.3.1 (September 30, 2019) diff --git a/README.md b/README.md index 0dc4fff..62f6ee7 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,6 @@ -[![CircleCI](https://circleci.com/gh/elasticio/jdbc-component.svg?style=svg)](https://circleci.com/gh/elasticio/jdbc-component) # JDBC-component -## Table of Contents - -* [General information](#general-information) - * [Description](#description) - * [Completeness Matrix](#completeness-matrix) -* [Credentials](#credentials) -* [Triggers](#triggers) - * [Select trigger](#select-trigger) - * [Get Rows Polling trigger](#get-rows-polling-trigger) -* [Actions](#actions) - * [Execute custom query](#execute-custom-query) - * [Select action](#select-action) - * [Lookup Row By Primary Key](#lookup-row-by-primary-key) - * [Insert action](#insert-action) - * [Delete Row By Primary Key](#delete-row-by-primary-key) - * [Execute stored procedure](#execute-stored-procedure) - * [Upsert Row By Primary Key)](#upsert-row-by-primary-key) -* [Known Limitations](#known-limitations) - -Execute stored procedure - -## General information -### Description + +## Description This is an open source component for working with object-relational database management systems on [elastic.io platform](http://www.elastic.io "elastic.io platform"). ### Completeness Matrix @@ -30,27 +8,88 @@ This is an open source component for working with object-relational database man [JDBC Component Completeness Matrix](https://docs.google.com/spreadsheets/d/1sZr9ydJbMK8v-TguctmFDiqgjRKcrpbdj4CeFuZEkQU/edit?usp=sharing) +### Purpose +With this component you will have following triggers: + +``SELECT`` - this trigger will execute an [SQL](https://en.wikipedia.org/wiki/SQL "SQL") query that returns multiple results, it has limitations on the query and suited only for SELECT type of queries. The trigger will remember last execution timestamp and let you build queries on it. + +``GET ROWS POLLING`` - this trigger will execute select query from specified table with simple criteria of selected datetime or timestamp table. The trigger will remember last execution timestamp and let you build queries on it. + +Following actions are inside: + +``SELECT`` - this action will execute an [SQL](https://en.wikipedia.org/wiki/SQL "SQL") query that returns multiple results, it has limitations on the query and suited only for SELECT type of queries. + +``LOOKUP BY PRIMARY KEY`` - this action will execute select query from specified table, as criteria can be used only [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY"). The action returns only one result (a primary key is unique). + +``UPSERT BY PRIMARY KEY`` - this action will execute select command from specified table, as search criteria can be used only [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY"), and execute insert command by PRIMARY KEY with specified field, if result does not found, else - action will execute update command by PRIMARY KEY with specified field. The action returns only one result row (a primary key is unique). + +``DELETE BY PRIMARY KEY`` - this action will execute delete query from specified table, as criteria can be used only [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY"). The action returns an integer value that indicates the number of rows affected, the returned value can be 0 or 1 (a primary key is unique). + +``INSERT`` - this action will execute insert query into the specified table. The action returns boolean value is execution insert successful or not. + +### How works + +### Requirements +Before you can deploy any code into elastic.io **you must be a registered elastic.io platform user**. Please see our home page at [http://www.elastic.io](http://www.elastic.io) to learn how. + +#### Environment variables +For integration-testing is needed to specify following environment variables: +1. Connection to MSSQL: + - ``CONN_USER_MSSQL`` - user login + - ``CONN_PASSWORD_MSSQL`` - user password + - ``CONN_DBNAME_MSSQL`` - DataBase name + - ``CONN_HOST_MSSQL`` - DataBase host + - ``CONN_PORT_MSSQL`` - DataBase port +2. Connection to MySQL: + - ``CONN_USER_MYSQL`` - user login + - ``CONN_PASSWORD_MYSQL`` - user password + - ``CONN_DBNAME_MYSQL`` - DataBase name + - ``CONN_HOST_MYSQL`` - DataBase host + - ``CONN_PORT_MYSQL`` - DataBase port +3. Connection to Oracle: + - ``CONN_USER_ORACLE`` - user login + - ``CONN_PASSWORD_ORACLE`` - user password + - ``CONN_DBNAME_ORACLE`` - DataBase name + - ``CONN_HOST_ORACLE`` - DataBase host + - ``CONN_PORT_ORACLE`` - DataBase port +4. Connection to PostgreSQL: + - ``CONN_USER_POSTGRESQL`` - user login + - ``CONN_PASSWORD_POSTGRESQL`` - user password + - ``CONN_DBNAME_POSTGRESQL`` - DataBase name + - ``CONN_HOST_POSTGRESQL`` - DataBase host + - ``CONN_PORT_POSTGRESQL`` - DataBase port + 5. Connection to Firebird: + - ``CONN_USER_FIREBIRD`` - user login + - ``CONN_PASSWORD_FIREBIRD`` - user password + - ``CONN_DBNAME_FIREBIRD`` - DataBase name + - ``CONN_HOST_FIREBIRD`` - DataBase host + - ``CONN_PORT_FIREBIRD`` - DataBase port + - ``CONN_PORT_FIREBIRD`` - DataBase port +#### Others ## Credentials You need to use following properties to configure credentials: -```DB Engine``` - Choose one of existing database types: +### DB Engine +Choose one of existing database types: ![image](https://user-images.githubusercontent.com/40201204/43577772-6f85bdea-9655-11e8-96e1-368493a36c9d.png) -```Connection URI``` - Provide hostname of the server, e.g. ``acme.com`` - -```Connection port``` - Optional field. Provide port of the server instance, as by default: +### Connection URI +Provide hostname of the server, e.g. ``acme.com`` +### Connection port +Optional field. Provide port of the server instance, as by default: - ``3306`` - MySQL - ``5432`` - PostgreSQL - ``1521`` - Oracle - ``1433`` - MSSQL - -```Database Name``` - Provide name of database at the instance that you want to interact with. - -```User``` - Provide a username that has permissions to interact with the Database. - -```Password``` - Provide a password of the user that has permissions to interact with the Database. - -```Configuration properties``` - Optional field. Provide a configuration properties for connections to the Database, e.g. ``useUnicode=true&serverTimezone=UTC`` +- ``3050`` - Firebird +### Database Name +Provide name of database at the instance that you want to interact with. +### User +Provide a username that has permissions to interact with the Database. +### Password +Provide a password of the user that has permissions to interact with the Database. +### Configuration properties +Optional field. Provide a configuration properties for connections to the Database, e.g. ``useUnicode=true&serverTimezone=UTC`` **Limitation:** `Configuration properties` value may not be checked during Credentials Verifacation, so in case of using this field make sure that it contains correct input. @@ -69,7 +108,6 @@ The format of ``Start Polling From (optional)`` field should be like ``yyyy-mm-d - ``mi`` - minute - ``ss`` - second - ``sss`` - millisecond (optional) -- ### Get Rows Polling trigger This trigger can polling data from provided table. As WHERE clause you can use column, which has datatype like DATE or TIMESTAMP. ![image](https://user-images.githubusercontent.com/40201204/43591332-c99f6b3e-967b-11e8-8a77-bf8386e83d51.png) @@ -107,7 +145,7 @@ Optional field, indicates the beginning time to start polling from (defaults to This action exists in JDBC component only for backward compatibility. New [**Select trigger**](#select-trigger) is recommended to use. ## Actions -### Execute custom query +### Execute custom query action Action to execute custom SQL query from provided request string. **Note:** SQL request will be executed according to chosen database JDBC specification. @@ -138,8 +176,7 @@ UPDATE stars SET radius = 5 WHERE id = 2; ``` ### Select action -![image](https://user-images.githubusercontent.com/16806832/134408205-04b84670-c976-41e7-b805-faabff4ae1e5.png) - +![image](https://user-images.githubusercontent.com/40201204/43592439-39ec5738-967e-11e8-8632-3655b08982d3.png) The action will execute an [SQL](https://en.wikipedia.org/wiki/SQL "SQL") query that can return multiple results, it has limitations on the query and suited only for SELECT type of queries. In SQL query you can use clause variables with specific data types. Internally we use prepared statements, so all incoming data is @@ -172,14 +209,9 @@ Following types are supported: * ``float`` * ``date`` -![image](https://user-images.githubusercontent.com/16806832/134408591-b9faa51c-3b35-4cf2-992d-51dcd07c5cb5.png) - -Dropdown **Emit Behaviour** contains following possible options: - * Fetch all - a single message with an array `results` containing all the objects (rows) will be emitted - * Emit Individually - multiple messages (one message per one row) will be emitted - * Expect Single - a single message with one result row will be emitted. If more than one row is returned the error will be thrown. A boolean input "Allow Zero Results" (defaults to `false`) appears at input metadata. If `false` - error will be thrown, else - the empty object will be emitted. +![image](https://user-images.githubusercontent.com/40201204/43644974-332f2aa4-9739-11e8-8483-f7395e5d195d.png) -![image](https://user-images.githubusercontent.com/16806832/134408977-d4692d3f-e9fb-48be-9104-c4cb121accaa.png) +Checkbox ``Don't throw Error on an Empty Result`` allows to emit an empty response, otherwise you will get an error on empty response. #### Input fields description Component supports dynamic incoming metadata - as soon as your query is in place it will be parsed and incoming metadata will be generated accordingly. @@ -217,7 +249,7 @@ As output metadata, you will get execution insert result like: } ``` -### Delete Row By Primary Key +### Delete Row By Primary Key action ![image](https://user-images.githubusercontent.com/40201204/43592505-5b6bbfe8-967e-11e8-845e-2ce8ac707357.png) The action will execute delete query from a ``Table`` dropdown field, as criteria can be used only [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY"). The action returns count of affected rows. Checkbox ``Don't throw Error on an Empty Result`` allows to emit an empty response, otherwise you will get an error on empty response. @@ -266,7 +298,7 @@ Component generates next metadata: ![image](https://user-images.githubusercontent.com/22715422/62056735-edd26200-b226-11e9-871e-0efc305d70b2.png) -### Upsert Row By Primary Key +### Upsert Row By Primary Key action The action will execute ``SELECT`` command from a ``Tables`` dropdown field, as search criteria can be used only [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY"), and execute ``INSERT`` command by PRIMARY KEY with specified field, if result does not found, else - action will execute ``UPDATE`` command by PRIMARY KEY with specified field. The action returns only one result row (a primary key is unique). 1. Find and select jdbc-component in the component repository ![image](https://user-images.githubusercontent.com/16806832/44981615-c70a9d80-af7b-11e8-8055-3b553abe8212.png) @@ -304,15 +336,11 @@ As an input metadata you will get all fields of selected table. [PRIMARY KEY](ht ![image](https://user-images.githubusercontent.com/16806832/44397461-1a76f780-a549-11e8-8247-9a6f9aa3f3b4.png) -### Create or update record (Deprecated) +### Create or update record action (Deprecated) This action exists in JDBC component only for backward compatibility. -Please use [**Upsert row by primary key**](#upsert-row-by-primary-key) instead. +Please use [**Upsert row by primary key**](#upsert-row-by-primary-key-action) instead. -### Select (Deprecated) -This action exists in JDBC component only for backward compatibility. -Please use [**Select action**](#select-action) instead. - -## Known limitations +## Current limitations 1. Only tables with one [PRIMARY KEY](https://en.wikipedia.org/wiki/Primary_key "PRIMARY KEY") is supported. You will see the message ``Table has not Primary Key. Should be one Primary Key ``, if the selected table doesn't have a primary key. Also, you will see the message ``Composite Primary Key is not supported ``, if the selected table has composite primary key. @@ -321,15 +349,20 @@ Please use [**Select action**](#select-action) instead. - ``PostgreSQL`` - compatible with PostgreSQL 8.2 and higher - ``Oracle`` - compatible with Oracle Database 8.1.7 - 12.1.0.2 - ``MSSQL`` - compatible with Microsoft SQL Server 2008 R2 and higher +- ``Firebird`` - compatible with Firebird 2.0 and higher 3. The current implementation of the action ``Upsert By Primary Key`` doesn't mark non-nullable fields as required fields at a dynamic metadata. In case of updating such fields with an empty value you will get SQL Exception ``Cannot insert the value NULL into...``. You should manually fill in all non-nullable fields with previous data, if you want to update part of columns in a row, even if data in that fields doesn't change. 4. The current implementation of the action ``Execute stored procedure`` doesn't support ResultSet MSSQL output. 5. The current implementation of the action ``Execute stored procedure`` doesn't support any array types parameters. (MySQL does not have schemas by definition) 6. Rebound mechanism only works for this SQL State: - - ``MySQL``: 40001, XA102 - - ``Oracle``: 61000 - - ``MSSQL``: 40001 - - ``PostgreSQL``: 40P01 +- ``MySQL``: 40001, XA102 +- ``Oracle``: 61000 +- ``MSSQL``: 40001 +- ``PostgreSQL``: 40P01 +- ``Firebird``: 10054, 10038 ## License Apache-2.0 © [elastic.io GmbH](https://www.elastic.io "elastic.io GmbH") + +## API and Documentation links +[elastic.io iPaaS Documentation](https://support.elastic.io/support/home "elastic.io iPaaS Documentation") diff --git a/build.gradle b/build.gradle index da3b88f..1faa58f 100755 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ group = 'io.elastic' +version = '2.4.1' apply plugin: 'java' apply plugin: 'idea' apply plugin: 'eclipse' @@ -27,11 +28,12 @@ test { } task integrationTest(type: Test) { + maxParallelForks = 1 testLogging { showStandardStreams = true } filter() { - includeTestsMatching "io.elastic.jdbc.integration.*" + includeTestsMatching 'io.elastic.jdbc.integration.*' } } @@ -47,30 +49,35 @@ targetCompatibility = 1.8 repositories { maven { - url "https://oss.sonatype.org/content/repositories/snapshots" + url 'https://oss.sonatype.org/content/repositories/snapshots' + } + maven { + url 'https://oss.sonatype.org/content/repositories/ioelastic-1034' } mavenCentral() mavenLocal() } dependencies { + compile 'io.elastic:sailor-jvm:3.3.7' + compile 'mysql:mysql-connector-java:8.0.11' + compile group: 'org.postgresql', name: 'postgresql', version: '42.2.4' + compile group: 'org.firebirdsql.jdbc', name: 'jaybird-jdk18', version: '3.0.8' compile files("./lib/ojdbc6.jar") compile files("./lib/sqljdbc4.jar") - // The following 3 dependencies are to workaround this: https://github.com/elasticio/sailor-jvm/issues/59 - compile 'com.fasterxml.jackson.core:jackson-core:2.10.1' - compile 'com.fasterxml.jackson.core:jackson-annotations:2.10.1' - compile 'com.fasterxml.jackson.core:jackson-databind:2.10.1' - compile 'com.google.code.gson:gson:2.8.6' - compile 'com.microsoft.sqlserver:mssql-jdbc:6.4.0.jre8' - compile 'io.elastic:sailor-jvm:3.3.6' - compile 'mysql:mysql-connector-java:8.0.20' - compile 'org.postgresql:postgresql:42.2.18' + compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + compile group: 'com.microsoft.sqlserver', name: 'mssql-jdbc', version: '6.4.0.jre8' + compile 'com.fasterxml.jackson.core:jackson-core:2.7.3' + compile 'com.fasterxml.jackson.core:jackson-annotations:2.7.3' + compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3' + compile group: 'uk.org.lidalia', name: 'sysout-over-slf4j', version: '1.0.2' - testCompile 'io.github.cdimascio:java-dotenv:5.1.0' - testCompile 'org.hsqldb:hsqldb:2.0.0' + testCompile group: 'io.github.cdimascio', name: 'java-dotenv', version: '5.1.0' testCompile 'org.spockframework:spock-core:1.1-groovy-2.4' + testCompile 'org.hsqldb:hsqldb:2.0.0' } + wrapper { gradleVersion = '5.4.1' } diff --git a/component.json b/component.json index 8d6b3c9..8ecf4ff 100755 --- a/component.json +++ b/component.json @@ -14,7 +14,8 @@ "mysql": "MySQL", "postgresql": "PostgreSQL", "oracle": "Oracle", - "mssql": "MSSQL" + "mssql": "MSSQL", + "firebirdsql": "Firebird" }, "note": "Please Select Database Type" }, @@ -108,6 +109,11 @@ "label": "Start Polling From (optional)", "required": false, "placeholder": "1970-01-01 00:00:00.000" + }, + "emitBulk": { + "viewClass": "CheckBoxView", + "label": "Emit bulk?", + "required": false } }, "dynamicMetadata": "io.elastic.jdbc.providers.ColumnNamesProvider" @@ -156,7 +162,7 @@ } } }, - "out":{ + "out": { "type": "object", "required": false, "properties": { @@ -203,14 +209,14 @@ "required": true, "model": "io.elastic.jdbc.providers.TableNameProvider" }, - "reboundEnabled" : { + "reboundEnabled": { "viewClass": "SelectView", "note": "Default is No", "label": "Enable Rebound", "required": false, - "model" : { - "Yes" : "Yes", - "No" : "No" + "model": { + "Yes": "Yes", + "No": "No" } } }, @@ -228,14 +234,14 @@ "required": true, "model": "io.elastic.jdbc.providers.TableNameProvider" }, - "reboundEnabled" : { + "reboundEnabled": { "viewClass": "SelectView", "note": "Default is No", "label": "Enable Rebound", "required": false, - "model" : { - "Yes" : "Yes", - "No" : "No" + "model": { + "Yes": "Yes", + "No": "No" } } }, @@ -257,14 +263,14 @@ "label": "Don`t throw Error on an Empty Result", "viewClass": "CheckBoxView" }, - "reboundEnabled" : { + "reboundEnabled": { "viewClass": "SelectView", "note": "Default is No", "label": "Enable Rebound", "required": false, - "model" : { - "Yes" : "Yes", - "No" : "No" + "model": { + "Yes": "Yes", + "No": "No" } } }, @@ -367,5 +373,7 @@ }, "dynamicMetadata": "io.elastic.jdbc.providers.ProcedureFieldsNameProvider" } - } -} + }, + "language": "java", + "sailor_version": "2.1.3" +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf..758de96 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 508b2d4..fab963f 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Sep 20 22:42:00 EEST 2021 +#Thu Apr 02 13:29:34 EEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index b0d6d0a..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,21 +1,5 @@ #!/usr/bin/env sh -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - ############################################################################## ## ## Gradle start up script for UN*X @@ -44,7 +28,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index 9991c50..f955316 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,19 +1,3 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem http://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -30,7 +14,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" +set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/src/main/java/io/elastic/jdbc/actions/DeleteRowByPrimaryKey.java b/src/main/java/io/elastic/jdbc/actions/DeleteRowByPrimaryKey.java index 8e37089..86ac967 100644 --- a/src/main/java/io/elastic/jdbc/actions/DeleteRowByPrimaryKey.java +++ b/src/main/java/io/elastic/jdbc/actions/DeleteRowByPrimaryKey.java @@ -35,27 +35,11 @@ public void execute(ExecutionParameters parameters) { StringBuilder primaryKey = new StringBuilder(); StringBuilder primaryValue = new StringBuilder(); Integer primaryKeysCount = 0; - String tableName = ""; - String dbEngine = ""; Boolean nullableResult = false; - - if (configuration.containsKey(PROPERTY_TABLE_NAME) - && Utils.getNonNullString(configuration, PROPERTY_TABLE_NAME).length() != 0) { - tableName = configuration.getString(PROPERTY_TABLE_NAME); - } else if (snapshot.containsKey(PROPERTY_TABLE_NAME) - && Utils.getNonNullString(snapshot, PROPERTY_TABLE_NAME).length() != 0) { - tableName = snapshot.getString(PROPERTY_TABLE_NAME); - } else { - throw new RuntimeException("Table name is required field"); - } - - if (Utils.getNonNullString(configuration, PROPERTY_DB_ENGINE).length() != 0) { - dbEngine = configuration.getString(PROPERTY_DB_ENGINE); - } else if (Utils.getNonNullString(snapshot, PROPERTY_DB_ENGINE).length() != 0) { - dbEngine = snapshot.getString(PROPERTY_DB_ENGINE); - } else { - throw new RuntimeException("DB Engine is required field"); - } + final String dbEngine = Utils.getDbEngine(configuration); + final boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); + final boolean isFirebird = dbEngine.equals(Engines.FIREBIRDSQL.name().toLowerCase()); + final String tableName = Utils.getTableName(configuration, (isOracle || isFirebird)); if (Utils.getNonNullString(configuration, PROPERTY_NULLABLE_RESULT).equals("true")) { nullableResult = true; @@ -64,39 +48,43 @@ public void execute(ExecutionParameters parameters) { } for (Map.Entry entry : body.entrySet()) { + LOGGER.info("{} = {}", entry.getKey(), entry.getValue()); primaryKey.append(entry.getKey()); primaryValue.append(entry.getValue()); primaryKeysCount++; } if (primaryKeysCount == 1) { - try { - Connection connection = Utils.getConnection(configuration); + try (Connection connection = Utils.getConnection(configuration)) { LOGGER.info("Executing delete row by primary key action"); - boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); - Utils.columnTypes = Utils.getColumnTypes(connection, isOracle, tableName); - LOGGER.debug("Detected column types"); + Utils.columnTypes = Utils.getColumnTypes(connection, tableName); + LOGGER.info("Detected column types: " + Utils.columnTypes); try { QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(dbEngine); + LOGGER.info("Lookup parameters: {} = {}", primaryKey.toString(), primaryValue.toString()); query.from(tableName).lookup(primaryKey.toString(), primaryValue.toString()); checkConfig(configuration); JsonObject row = query.executeLookup(connection, body); + for (Map.Entry entry : configuration.entrySet()) { + LOGGER.info("Key = " + entry.getKey() + " Value = " + entry.getValue()); + } + if (row.size() != 0) { int result = query.executeDelete(connection, body); if (result == 1) { - LOGGER.info("Emitting data..."); + LOGGER.info("Emitting data {}", row); parameters.getEventEmitter().emitData(new Message.Builder().body(row).build()); } else { - LOGGER.error("Unexpected result"); + LOGGER.info("Unexpected result"); throw new RuntimeException("Unexpected result"); } } else if (row.size() == 0 && nullableResult) { JsonObject emptyRow = Json.createObjectBuilder() .add("empty dataset", "nothing to delete") .build(); - LOGGER.info("Emitting data..."); + LOGGER.info("Emitting data {}", emptyRow); parameters.getEventEmitter().emitData(new Message.Builder().body(emptyRow).build()); } else if (row.size() == 0 && !nullableResult) { LOGGER.info("Empty response. Error message will be returned"); @@ -107,24 +95,23 @@ public void execute(ExecutionParameters parameters) { .add(PROPERTY_ID_COLUMN, primaryKey.toString()) .add(PROPERTY_LOOKUP_VALUE, primaryValue.toString()) .add(PROPERTY_NULLABLE_RESULT, nullableResult).build(); - LOGGER.info("Emitting new snapshot"); + LOGGER.info("Emitting new snapshot {}", snapshot.toString()); parameters.getEventEmitter().emitSnapshot(snapshot); } catch (SQLException e) { if (Utils.reboundIsEnabled(configuration)) { List states = Utils.reboundDbState.get(dbEngine); if (states.contains(e.getSQLState())) { - LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {} because of a SQL Exception", - System.getenv("ELASTICIO_REBOUND_LIMIT"), - System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION")); + LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {}. Reason: {}", System.getenv("ELASTICIO_REBOUND_LIMIT"), + System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION"), e.getMessage()); parameters.getEventEmitter().emitRebound(e); return; } } - LOGGER.error("Failed to make request"); + LOGGER.error("Failed to make request", e.toString()); throw new RuntimeException(e); } } catch (SQLException e) { - LOGGER.error("Failed to close connection"); + LOGGER.error("Failed to close connection", e.toString()); } } else { LOGGER.error("Error: Should be one Primary Key"); diff --git a/src/main/java/io/elastic/jdbc/actions/ExecuteStoredProcedure.java b/src/main/java/io/elastic/jdbc/actions/ExecuteStoredProcedure.java index 4545d15..c25d467 100644 --- a/src/main/java/io/elastic/jdbc/actions/ExecuteStoredProcedure.java +++ b/src/main/java/io/elastic/jdbc/actions/ExecuteStoredProcedure.java @@ -1,11 +1,13 @@ package io.elastic.jdbc.actions; import io.elastic.api.ExecutionParameters; -import io.elastic.api.Message; import io.elastic.api.Function; +import io.elastic.api.Message; import io.elastic.jdbc.query_builders.Query; import io.elastic.jdbc.utils.QueryFactory; import io.elastic.jdbc.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.json.JsonObject; import java.sql.Connection; @@ -13,16 +15,19 @@ public class ExecuteStoredProcedure implements Function { + private static final Logger LOGGER = LoggerFactory.getLogger(ExecuteStoredProcedure.class); + @Override public void execute(ExecutionParameters parameters) { final JsonObject body = parameters.getMessage().getBody(); final JsonObject configuration = parameters.getConfiguration(); - try { - Connection connection = Utils.getConnection(configuration); + + try (Connection connection = Utils.getConnection(configuration)) { QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(configuration.getString("dbEngine")); JsonObject result = query.callProcedure(connection, body, configuration); + parameters.getEventEmitter() .emitData(new Message.Builder().body(result).build()); } catch (SQLException e) { diff --git a/src/main/java/io/elastic/jdbc/actions/InsertAction.java b/src/main/java/io/elastic/jdbc/actions/InsertAction.java index c034be2..f193e8a 100644 --- a/src/main/java/io/elastic/jdbc/actions/InsertAction.java +++ b/src/main/java/io/elastic/jdbc/actions/InsertAction.java @@ -1,20 +1,23 @@ package io.elastic.jdbc.actions; import io.elastic.api.ExecutionParameters; -import io.elastic.api.Function; import io.elastic.api.Message; -import io.elastic.jdbc.query_builders.Query; +import io.elastic.api.Function; import io.elastic.jdbc.utils.Engines; +import io.elastic.jdbc.query_builders.Query; import io.elastic.jdbc.utils.QueryFactory; import io.elastic.jdbc.utils.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.json.Json; -import javax.json.JsonObject; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import javax.json.Json; +import javax.json.JsonObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class InsertAction implements Function { @@ -27,13 +30,13 @@ public void execute(ExecutionParameters parameters) { final JsonObject body = parameters.getMessage().getBody(); final String dbEngine = Utils.getDbEngine(configuration); final boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); - final String tableName = Utils.getTableName(configuration, isOracle); - LOGGER.info("Found dbEngine: '{}'", dbEngine); - try { - Connection connection = Utils.getConnection(configuration); - Utils.columnTypes = Utils.getColumnTypes(connection, isOracle, tableName); - LOGGER.debug("Detected column types"); - LOGGER.info("Inserting values in the table"); + final boolean isFirebird = dbEngine.equals(Engines.FIREBIRDSQL.name().toLowerCase()); + final String tableName = Utils.getTableName(configuration, (isOracle || isFirebird)); + LOGGER.info("Found dbEngine: '{}' and tableName: '{}'", dbEngine, tableName); + try (Connection connection = Utils.getConnection(configuration)) { + Utils.columnTypes = Utils.getColumnTypes(connection, tableName); + LOGGER.info("Detected column types: " + Utils.columnTypes); + LOGGER.info("Inserting in table '{}' values '{}'", tableName, body); QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(dbEngine); query.from(tableName); @@ -42,9 +45,7 @@ public void execute(ExecutionParameters parameters) { if (Utils.reboundIsEnabled(configuration)) { List states = Utils.reboundDbState.get(dbEngine); if (states.contains(e.getSQLState())) { - LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {} because of a SQL Exception", - System.getenv("ELASTICIO_REBOUND_LIMIT"), - System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION")); + LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {}. Reason: {}", System.getenv("ELASTICIO_REBOUND_LIMIT"), System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION"), e.getMessage()); parameters.getEventEmitter().emitRebound(e); return; } @@ -54,7 +55,7 @@ public void execute(ExecutionParameters parameters) { JsonObject result = Json.createObjectBuilder() .add("result", true) .build(); - LOGGER.info("Emitting data..."); + LOGGER.info("Emit data= {}", result); parameters.getEventEmitter().emitData(new Message.Builder().body(result).build()); LOGGER.info("Insert action is successfully executed"); } diff --git a/src/main/java/io/elastic/jdbc/actions/LookupRowByPrimaryKey.java b/src/main/java/io/elastic/jdbc/actions/LookupRowByPrimaryKey.java index 448b6ec..fa29601 100644 --- a/src/main/java/io/elastic/jdbc/actions/LookupRowByPrimaryKey.java +++ b/src/main/java/io/elastic/jdbc/actions/LookupRowByPrimaryKey.java @@ -35,10 +35,10 @@ public void execute(ExecutionParameters parameters) { JsonObject snapshot = parameters.getSnapshot(); StringBuilder primaryKey = new StringBuilder(); StringBuilder primaryValue = new StringBuilder(); - Integer primaryKeysCount = 0; + int primaryKeysCount = 0; String tableName = ""; String dbEngine = ""; - Boolean nullableResult = false; + boolean nullableResult = false; if (configuration.containsKey(PROPERTY_TABLE_NAME) && Utils.getNonNullString(configuration, PROPERTY_TABLE_NAME).length() != 0) { @@ -64,9 +64,8 @@ public void execute(ExecutionParameters parameters) { nullableResult = true; } - boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); - for (Map.Entry entry : body.entrySet()) { + LOGGER.info("{} = {}", entry.getKey(), entry.getValue()); primaryKey.append(entry.getKey()); primaryValue.append(entry.getValue()); primaryKeysCount++; @@ -74,27 +73,28 @@ public void execute(ExecutionParameters parameters) { if (primaryKeysCount == 1) { - try { - Connection connection = Utils.getConnection(configuration); + try (Connection connection = Utils.getConnection(configuration)) { LOGGER.info("Executing lookup row by primary key action"); - Utils.columnTypes = Utils.getColumnTypes(connection, isOracle, tableName); - LOGGER.debug("Detected column types"); + Utils.columnTypes = Utils.getColumnTypes(connection, tableName); + LOGGER.info("Detected column types: " + Utils.columnTypes); try { QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(dbEngine); - LOGGER.debug("Got Lookup parameters"); + LOGGER.info("Lookup parameters: {} = {}", primaryKey.toString(), primaryValue.toString()); query.from(tableName).lookup(primaryKey.toString(), primaryValue.toString()); checkConfig(configuration); JsonObject row = query.executeLookup(connection, body); if (row.size() != 0) { LOGGER.info("Emitting data"); + LOGGER.info(row.toString()); parameters.getEventEmitter().emitData(new Message.Builder().body(row).build()); } if (row.size() == 0 && nullableResult) { JsonObjectBuilder emptyResBuilder = Json.createObjectBuilder(); emptyResBuilder.add("empty dataset", JsonValue.NULL); LOGGER.info("Emitting data"); + LOGGER.info(JSON.stringify(emptyResBuilder.build())); parameters.getEventEmitter().emitData(new Message.Builder().body(emptyResBuilder.build()).build()); } else if (row.size() == 0 && !nullableResult) { LOGGER.info("Empty response. Error message will be returned"); @@ -105,14 +105,14 @@ public void execute(ExecutionParameters parameters) { .add(PROPERTY_ID_COLUMN, primaryKey.toString()) .add(PROPERTY_LOOKUP_VALUE, primaryValue.toString()) .add(PROPERTY_NULLABLE_RESULT, nullableResult).build(); - LOGGER.info("Emitting new snapshot"); + LOGGER.info("Emitting new snapshot {}", snapshot.toString()); parameters.getEventEmitter().emitSnapshot(snapshot); } catch (SQLException e) { - LOGGER.error("Failed to make request"); + LOGGER.error("Failed to make request", e.toString()); throw new RuntimeException(e); } } catch (SQLException e) { - LOGGER.error("Failed to close connection"); + LOGGER.error("Failed to close connection", e.toString()); } } else { LOGGER.error("Error: Should be one Primary Key"); diff --git a/src/main/java/io/elastic/jdbc/actions/SelectAction.java b/src/main/java/io/elastic/jdbc/actions/SelectAction.java index a8a65c8..9cc6b16 100644 --- a/src/main/java/io/elastic/jdbc/actions/SelectAction.java +++ b/src/main/java/io/elastic/jdbc/actions/SelectAction.java @@ -3,13 +3,11 @@ import io.elastic.api.ExecutionParameters; import io.elastic.api.Message; import io.elastic.api.Function; -import io.elastic.api.ShutdownParameters; import io.elastic.jdbc.query_builders.Query; import io.elastic.jdbc.utils.QueryFactory; import io.elastic.jdbc.utils.Utils; import java.sql.Connection; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; import javax.json.Json; import javax.json.JsonObject; @@ -17,8 +15,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Deprecated public class SelectAction implements Function { + private static final Logger LOGGER = LoggerFactory.getLogger(SelectAction.class); private static final String SQL_QUERY_VALUE = "sqlQuery"; private static final String PROPERTY_NULLABLE_RESULT = "nullableResult"; @@ -46,19 +44,20 @@ public void execute(ExecutionParameters parameters) { } Utils.columnTypes = Utils.getVariableTypes(sqlQuery); + LOGGER.info("Detected column types: " + Utils.columnTypes); LOGGER.info("Executing select action"); - LOGGER.debug("Detected column types"); try { QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(dbEngine); sqlQuery = Query.preProcessSelect(sqlQuery); - LOGGER.debug("Got SQL Query"); + LOGGER.info("SQL Query: {}", sqlQuery); ArrayList resultList; - Connection connection = Utils.getConnection(configuration); - resultList = query.executeSelectQuery(connection, sqlQuery, body); + try(Connection connection = Utils.getConnection(configuration)){ + resultList = query.executeSelectQuery(connection, sqlQuery, body); + } for (int i = 0; i < resultList.size(); i++) { - LOGGER.debug("Columns count: {} from {}", i + 1, resultList.size()); - LOGGER.info("Emitting data..."); + LOGGER.info("Columns count: {} from {}", i + 1, resultList.size()); + LOGGER.info("Emitting data {}", resultList.get(i).toString()); parameters.getEventEmitter() .emitData(new Message.Builder().body(resultList.get(i)).build()); } @@ -67,7 +66,7 @@ public void execute(ExecutionParameters parameters) { resultList.add(Json.createObjectBuilder() .add("empty dataset", "no data") .build()); - LOGGER.info("Emitting data..."); + LOGGER.info("Emitting data {}", resultList.get(0)); parameters.getEventEmitter() .emitData(new Message.Builder().body(resultList.get(0)).build()); } else if (resultList.size() == 0 && !nullableResult) { @@ -79,7 +78,7 @@ public void execute(ExecutionParameters parameters) { .add(PROPERTY_SKIP_NUMBER, skipNumber + resultList.size()) .add(SQL_QUERY_VALUE, sqlQuery) .add(PROPERTY_NULLABLE_RESULT, nullableResult).build(); - LOGGER.info("Emitting new snapshot"); + LOGGER.info("Emitting new snapshot {}", snapshot.toString()); parameters.getEventEmitter().emitSnapshot(snapshot); } catch (SQLException e) { throw new RuntimeException(e); diff --git a/src/main/java/io/elastic/jdbc/actions/UpsertRowByPrimaryKey.java b/src/main/java/io/elastic/jdbc/actions/UpsertRowByPrimaryKey.java index 48d7400..538c6a7 100644 --- a/src/main/java/io/elastic/jdbc/actions/UpsertRowByPrimaryKey.java +++ b/src/main/java/io/elastic/jdbc/actions/UpsertRowByPrimaryKey.java @@ -31,7 +31,7 @@ public void execute(ExecutionParameters parameters) { JsonObject resultRow; String tableName; String dbEngine; - String catalog = null; + String catalog = ""; String schemaName = ""; String primaryKey = ""; int primaryKeysCount = 0; @@ -55,15 +55,18 @@ public void execute(ExecutionParameters parameters) { } LOGGER.info("Executing lookup primary key"); - boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); - Boolean isMysql = configuration.getString("dbEngine").equals("mysql"); + final boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); + final boolean isMysql = dbEngine.equals(Engines.MYSQL.name().toLowerCase()); + final boolean isFirebird = dbEngine.equals(Engines.FIREBIRDSQL.name().toLowerCase()); - try { - Connection connection = Utils.getConnection(configuration); + try (Connection connection = Utils.getConnection(configuration)) { DatabaseMetaData dbMetaData = connection.getMetaData(); if (isMysql) { catalog = configuration.getString("databaseName"); } + if (isFirebird) { + tableName = tableName.toUpperCase(); + } if (tableName.contains(".")) { schemaName = (isOracle) ? tableName.split("\\.")[0].toUpperCase() : tableName.split("\\.")[0]; @@ -79,17 +82,18 @@ public void execute(ExecutionParameters parameters) { } if (primaryKeysCount == 1) { LOGGER.info("Executing upsert row by primary key action"); - Utils.columnTypes = Utils.getColumnTypes(connection, isOracle, tableName); - LOGGER.debug("Detected column types"); + Utils.columnTypes = Utils.getColumnTypes(connection, tableName); + LOGGER.info("Detected column types: " + Utils.columnTypes); QueryFactory queryFactory = new QueryFactory(); Query query = queryFactory.getQuery(dbEngine); - LOGGER.debug("Execute upsert parameters"); + LOGGER + .info("Execute upsert parameters by PK: '{}' = {}", primaryKey, body.get(primaryKey)); query.from(tableName); resultRow = query.executeUpsert(connection, primaryKey, body); - LOGGER.info("Emitting data"); + LOGGER.info("Emit data= {}", resultRow); parameters.getEventEmitter().emitData(new Message.Builder().body(resultRow).build()); snapshot = Json.createObjectBuilder().add(PROPERTY_TABLE_NAME, tableName).build(); - LOGGER.info("Emitting new snapshot"); + LOGGER.info("Emitting new snapshot {}", snapshot.toString()); parameters.getEventEmitter().emitSnapshot(snapshot); } else if (primaryKeysCount == 0) { LOGGER.error("Error: Table has not Primary Key. Should be one Primary Key"); @@ -103,9 +107,7 @@ public void execute(ExecutionParameters parameters) { if (Utils.reboundIsEnabled(configuration)) { List states = Utils.reboundDbState.get(dbEngine); if (states.contains(e.getSQLState())) { - LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {} because of a SQL Exception", - System.getenv("ELASTICIO_REBOUND_LIMIT"), - System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION")); + LOGGER.warn("Starting rebound max iter: {}, rebound ttl: {}. Reason: {}", System.getenv("ELASTICIO_REBOUND_LIMIT"), System.getenv("ELASTICIO_REBOUND_INITIAL_EXPIRATION"), e.getMessage()); parameters.getEventEmitter().emitRebound(e); return; } diff --git a/src/main/java/io/elastic/jdbc/providers/ColumnNamesForInsertProvider.java b/src/main/java/io/elastic/jdbc/providers/ColumnNamesForInsertProvider.java index aa67649..9f1c341 100644 --- a/src/main/java/io/elastic/jdbc/providers/ColumnNamesForInsertProvider.java +++ b/src/main/java/io/elastic/jdbc/providers/ColumnNamesForInsertProvider.java @@ -54,7 +54,8 @@ private JsonObject getInputMetaData(JsonObject configuration) { LOGGER.info("Getting input metadata..."); final String dbEngine = Utils.getDbEngine(configuration); final boolean isOracle = dbEngine.equals(Engines.ORACLE.name().toLowerCase()); - final String tableName = Utils.getTableName(configuration, isOracle); + final boolean isFirebird = dbEngine.equals(Engines.FIREBIRDSQL.name().toLowerCase()); + final String tableName = Utils.getTableName(configuration, (isOracle || isFirebird)); JsonObjectBuilder propertiesIn = Json.createObjectBuilder(); boolean isEmpty = true; @@ -89,7 +90,7 @@ private JsonObject getInputMetaData(JsonObject configuration) { final boolean isPrimaryKey = Utils.isPrimaryKey(primaryKeysNames, fieldName); final boolean isNotNull = Utils.isNotNull(resultSet); - final boolean isAutoincrement = Utils.isAutoincrement(resultSet, isOracle); + final boolean isAutoincrement = Utils.isAutoincrement(resultSet, (isOracle || isFirebird)); final boolean isCalculated = Utils.isCalculated(resultSet, dbEngine); LOGGER .trace( diff --git a/src/main/java/io/elastic/jdbc/query_builders/Firebird.java b/src/main/java/io/elastic/jdbc/query_builders/Firebird.java new file mode 100644 index 0000000..f5b7602 --- /dev/null +++ b/src/main/java/io/elastic/jdbc/query_builders/Firebird.java @@ -0,0 +1,190 @@ +package io.elastic.jdbc.query_builders; + +import io.elastic.jdbc.providers.ProcedureFieldsNameProvider; +import io.elastic.jdbc.utils.ProcedureParameter; +import io.elastic.jdbc.utils.ProcedureParameter.Direction; +import io.elastic.jdbc.utils.Utils; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; + +public class Firebird extends Query { + + public ArrayList executePolling(Connection connection) throws SQLException { + validateQuery(); + + /* workaround to set FIRST operation on the 1st position in the statement */ + StringBuilder sql = new StringBuilder("WITH d AS (SELECT * FROM "); + sql.append(tableName); + sql.append(" WHERE "); + sql.append(pollingField); + sql.append(" > ?) "); + sql.append("SELECT FIRST ? * FROM d"); + if (orderField != null) { + sql.append(" ORDER BY ").append(orderField).append(" ASC"); + } + + return getRowsExecutePolling(connection, sql.toString()); + } + + public JsonObject executeLookup(Connection connection, JsonObject body) throws SQLException { + validateQuery(); + + /* workaround to set FIRST operation on the 1st position in the statement */ + StringBuilder sql = new StringBuilder("SELECT * FROM "); + sql.append(tableName); + sql.append(" WHERE "); + sql.append(lookupField); + sql.append(" = ?"); + sql.append(" ORDER BY ").append(lookupField); + sql.append(" ASC ROWS ? TO ?"); + + return getLookupRow(connection, body, sql.toString(), 1, skipNumber += countNumber); + } + + public int executeDelete(Connection connection, JsonObject body) throws SQLException { + String sql = "DELETE" + + " FROM " + tableName + + " WHERE " + lookupField + " = ?"; + try (PreparedStatement stmt = connection.prepareStatement(sql)) { + stmt.setString(1, lookupValue); + return stmt.executeUpdate(); + } + } + + public void executeInsert(Connection connection, String tableName, JsonObject body) + throws SQLException { + validateQuery(); + StringBuilder keys = new StringBuilder(); + StringBuilder values = new StringBuilder(); + for (Map.Entry entry : body.entrySet()) { + if (keys.length() > 0) { + keys.append(","); + } + keys.append(entry.getKey()); + if (values.length() > 0) { + values.append(","); + } + values.append("?"); + } + String sql = "INSERT INTO " + tableName + + " (" + keys.toString() + ")" + + " VALUES (" + values.toString() + ")"; + try (PreparedStatement stmt = connection.prepareStatement(sql)) { + int i = 1; + for (String key : body.keySet()) { + Utils.setStatementParam(stmt, i, key, body); + i++; + } + stmt.execute(); + } + } + + public void executeUpdate(Connection connection, String tableName, String idColumn, + String idValue, JsonObject body) throws SQLException { + validateQuery(); + StringBuilder setString = new StringBuilder(); + for (Map.Entry entry : body.entrySet()) { + if (setString.length() > 0) { + setString.append(","); + } + setString.append(entry.getKey()).append(" = ?"); + } + String sql = "UPDATE " + tableName + + " SET " + setString.toString() + + " WHERE " + idColumn + " = ?"; + try (PreparedStatement stmt = connection.prepareStatement(sql)) { + int i = 1; + for (String key : body.keySet()) { + Utils.setStatementParam(stmt, i, key, body); + i++; + } + Utils.setStatementParam(stmt, i, idColumn, body); + stmt.execute(); + } + } + + @Override + protected CallableStatement prepareCallableStatement(Connection connection, String procedureName, + Map procedureParams, JsonObject messageBody) + throws SQLException { + CallableStatement stmt = connection.prepareCall( + String.format("{call %s%s}", procedureName, + generateStatementWildcardMask(procedureParams))); + + for (int inc = 1; inc <= procedureParams.size(); inc++) { + final int order = inc; + ProcedureParameter parameter = procedureParams.values() + .stream() + .filter(p -> p.getOrder() == order) + .findFirst().orElseThrow(() -> new IllegalStateException("Can't find parameter by order")); + + if (parameter.getDirection() == Direction.IN || parameter.getDirection() == Direction.INOUT) { + if (parameter.getDirection() == Direction.INOUT) { + stmt.registerOutParameter(inc, parameter.getType()); + } + + String type = Utils.cleanJsonType(Utils.detectColumnType(parameter.getType(), "")); + switch (type) { + case ("number"): + stmt.setObject(inc, + messageBody.getJsonNumber(parameter.getName()).toString(), + parameter.getType()); + break; + case ("boolean"): + stmt.setObject(inc, messageBody.getBoolean(parameter.getName()), + parameter.getType()); + break; + default: + stmt.setObject(inc, messageBody.getString(parameter.getName()), + parameter.getType()); + } + } else if (parameter.getDirection() == Direction.OUT) { + stmt.registerOutParameter(inc, parameter.getType()); + } + } + + return stmt; + } + + @Override + public JsonObject callProcedure(Connection connection, JsonObject body, JsonObject configuration) + throws SQLException { + + Map procedureParams = ProcedureFieldsNameProvider + .getProcedureMetadata(configuration).stream() + .collect(Collectors.toMap(ProcedureParameter::getName, Function.identity())); + + CallableStatement stmt = prepareCallableStatement(connection, + configuration.getString("procedureName"), procedureParams, body); + + stmt.execute(); + + JsonObjectBuilder resultBuilder = Json.createObjectBuilder(); + + procedureParams.values().stream() + .filter(param -> param.getDirection() == Direction.OUT + || param.getDirection() == Direction.INOUT) + .forEach(param -> { + try { + addValueToResultJson(resultBuilder, stmt, procedureParams, param.getName()); + } catch (SQLException e) { + e.printStackTrace(); + } + }); + + stmt.close(); + + return resultBuilder.build(); + } + +} diff --git a/src/main/java/io/elastic/jdbc/query_builders/MySQL.java b/src/main/java/io/elastic/jdbc/query_builders/MySQL.java index 9583ede..accb368 100644 --- a/src/main/java/io/elastic/jdbc/query_builders/MySQL.java +++ b/src/main/java/io/elastic/jdbc/query_builders/MySQL.java @@ -51,7 +51,9 @@ public int executeDelete(Connection connection, JsonObject body) throws SQLExcep " FROM " + tableName + " WHERE " + lookupField + " = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { - stmt.setString(1, lookupValue); + for (Map.Entry entry : body.entrySet()) { + Utils.setStatementParam(stmt, 1, entry.getKey(), body); + } return stmt.executeUpdate(); } } diff --git a/src/main/java/io/elastic/jdbc/triggers/SelectTrigger.java b/src/main/java/io/elastic/jdbc/triggers/SelectTrigger.java index b146644..96c1072 100644 --- a/src/main/java/io/elastic/jdbc/triggers/SelectTrigger.java +++ b/src/main/java/io/elastic/jdbc/triggers/SelectTrigger.java @@ -15,6 +15,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +27,7 @@ public class SelectTrigger implements Function { private static final String PROPERTY_DB_ENGINE = "dbEngine"; private static final String LAST_POLL_PLACEHOLDER = "%%EIO_LAST_POLL%%"; private static final String SQL_QUERY_VALUE = "sqlQuery"; + private static final String EMIT_BULK = "emitBulk"; private static final String PROPERTY_POLLING_VALUE = "pollingValue"; private static final String PROPERTY_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss.sss"; private static final String PROPERTY_SKIP_NUMBER = "skipNumber"; @@ -36,9 +39,15 @@ public final void execute(ExecutionParameters parameters) { final JsonObject configuration = parameters.getConfiguration(); checkConfig(configuration); String sqlQuery = configuration.getString(SQL_QUERY_VALUE); + Boolean emitBulk = false; JsonObject snapshot = parameters.getSnapshot(); + JsonArrayBuilder bulk = Json.createArrayBuilder(); Integer skipNumber = 0; + if (Utils.getNonNullString(configuration, EMIT_BULK).equals("true")) { + emitBulk = true; + } + Calendar cDate = Calendar.getInstance(); cDate.set(cDate.get(Calendar.YEAR), cDate.get(Calendar.MONTH), cDate.get(Calendar.DATE), 0, 0, 0); @@ -59,11 +68,12 @@ public final void execute(ExecutionParameters parameters) { + formattedDate); pollingValue = cts; } - LOGGER.debug("EIO_LAST_POLL = {}", pollingValue); + LOGGER.info("EIO_LAST_POLL = {}", pollingValue); if (snapshot.get(PROPERTY_SKIP_NUMBER) != null) { skipNumber = snapshot.getInt(PROPERTY_SKIP_NUMBER); } + LOGGER.info("SQL QUERY {} : ", sqlQuery); LOGGER.info("Executing select trigger"); try { QueryFactory queryFactory = new QueryFactory(); @@ -73,23 +83,36 @@ public final void execute(ExecutionParameters parameters) { sqlQuery = sqlQuery.replace(LAST_POLL_PLACEHOLDER, "?"); query.selectPolling(sqlQuery, pollingValue); } + LOGGER.info("SQL Query: {}", sqlQuery); Connection connection = Utils.getConnection(configuration); ArrayList resultList = query.executeSelectTrigger(connection, sqlQuery); - for (int i = 0; i < resultList.size(); i++) { - LOGGER.info("Columns count: {} from {}", i + 1, resultList.size()); - LOGGER.info("Emitting data"); + + if (emitBulk) { + LOGGER.info("Building result"); + for(JsonObject row : resultList) { + bulk.add(row); + } + LOGGER.debug("Emitting data {}", resultList.toString()); parameters.getEventEmitter() - .emitData(new Message.Builder().body(resultList.get(i)).build()); + .emitData(new Message.Builder().body(Json.createObjectBuilder() + .add("result", bulk).build()).build()); + } else { + for (int i = 0; i < resultList.size(); i++) { + LOGGER.info("Columns count: {} from {}", i + 1, resultList.size()); + LOGGER.debug("Emitting data {}", resultList.get(i).toString()); + parameters.getEventEmitter() + .emitData(new Message.Builder().body(resultList.get(i)).build()); + } } snapshot = Json.createObjectBuilder() .add(PROPERTY_SKIP_NUMBER, skipNumber + resultList.size()) .add(LAST_POLL_PLACEHOLDER, pollingValue.toString()) .add(SQL_QUERY_VALUE, sqlQuery).build(); - LOGGER.info("Emitting new snapshot"); + LOGGER.info("Emitting new snapshot {}", snapshot.toString()); parameters.getEventEmitter().emitSnapshot(snapshot); } catch (SQLException e) { - LOGGER.error("Failed to make request"); + LOGGER.error("Failed to make request", e.toString()); throw new RuntimeException(e); } } @@ -109,4 +132,5 @@ private void checkConfig(JsonObject config) { + "'"); } } + } diff --git a/src/main/java/io/elastic/jdbc/utils/Engines.java b/src/main/java/io/elastic/jdbc/utils/Engines.java index e511753..7e9d2f3 100644 --- a/src/main/java/io/elastic/jdbc/utils/Engines.java +++ b/src/main/java/io/elastic/jdbc/utils/Engines.java @@ -1,6 +1,18 @@ package io.elastic.jdbc.utils; public enum Engines { + FIREBIRDSQL("org.firebirdsql.jdbc.FBDriver", 3050) { + @Override + protected String getSubprotocol(String host, Integer port, String db) { + return "firebirdsql"; + } + + @Override + protected String getSubname(String host, Integer port, String db) { + return String.format("//%s:%s/%s", host, port, db); + } + }, + MYSQL("com.mysql.cj.jdbc.Driver", 3306) { @Override protected String getSubprotocol(String host, Integer port, String db) { diff --git a/src/main/java/io/elastic/jdbc/utils/QueryFactory.java b/src/main/java/io/elastic/jdbc/utils/QueryFactory.java index 67695dc..08bd2a0 100644 --- a/src/main/java/io/elastic/jdbc/utils/QueryFactory.java +++ b/src/main/java/io/elastic/jdbc/utils/QueryFactory.java @@ -2,6 +2,7 @@ import io.elastic.jdbc.query_builders.MSSQL; import io.elastic.jdbc.query_builders.MySQL; +import io.elastic.jdbc.query_builders.Firebird; import io.elastic.jdbc.query_builders.Oracle; import io.elastic.jdbc.query_builders.PostgreSQL; import io.elastic.jdbc.query_builders.Query; @@ -21,6 +22,9 @@ public Query getQuery(String dbEngine) { if (dbEngine.toLowerCase().equals("mysql")) { return new MySQL(); } + if (dbEngine.toLowerCase().equals("firebirdsql")) { + return new Firebird(); + } return null; } } diff --git a/src/main/java/io/elastic/jdbc/utils/Utils.java b/src/main/java/io/elastic/jdbc/utils/Utils.java index 9a434cc..d9b09ee 100644 --- a/src/main/java/io/elastic/jdbc/utils/Utils.java +++ b/src/main/java/io/elastic/jdbc/utils/Utils.java @@ -1,8 +1,17 @@ package io.elastic.jdbc.utils; -import java.io.IOException; -import java.io.StringReader; -import java.sql.*; +import java.io.*; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -19,6 +28,7 @@ import javax.json.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J; public class Utils { @@ -38,23 +48,32 @@ public class Utils { private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); public static Map columnTypes = null; public static final Map> reboundDbState; - private static Connection con; - private static JsonObject connectionConfig; - static { reboundDbState = new HashMap<>(); reboundDbState.put(Engines.POSTGRESQL.name(), Collections.singletonList("40P01")); reboundDbState.put(Engines.MSSQL.name(), Collections.singletonList("40001")); reboundDbState.put(Engines.MYSQL.name(), Arrays.asList("40001", "XA102")); reboundDbState.put(Engines.ORACLE.name(), Collections.singletonList("61000")); + reboundDbState.put(Engines.FIREBIRDSQL.name(), Arrays.asList("10054", "10038")); } - public static Connection getConnection(final JsonObject config) throws SQLException { - if (con != null && !con.isClosed() && connectionConfig == config) { - LOGGER.info("Use connection defined before"); - return con; + public static class MyWriter extends java.io.PrintWriter { + + public MyWriter(OutputStream out) { + super(out); + } + + @Override + public void println(String x) { + LOGGER.info(x); } - connectionConfig = config; + } + + public static Connection getConnection(final JsonObject config) throws SQLException { + SysOutOverSLF4J.sendSystemOutAndErrToSLF4J(); + DriverManager.setLogStream(System.out); + PrintWriter myWriter = new MyWriter(System.out); + DriverManager.setLogWriter(myWriter); final String engine = getRequiredNonEmptyString(config, CFG_DB_ENGINE, "Engine is required") .toLowerCase(); final String host = getRequiredNonEmptyString(config, CFG_HOST, "Host is required"); @@ -65,19 +84,10 @@ public static Connection getConnection(final JsonObject config) throws SQLExcept engineType.loadDriverClass(); final String connectionString = engineType.getConnectionString(host, port, databaseName); Properties properties = getConfigurationProperties(config, engineType); - LOGGER.info("Connecting to connection string"); - con = DriverManager.getConnection(connectionString, properties); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - if(!con.isClosed()) con.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - })); - return con; + LOGGER.info("Connecting to {}", connectionString); + return DriverManager.getConnection(connectionString, properties); } - private static String getPassword(final JsonObject config, final Engines engineType) { final String password = getNonNullString(config, CFG_PASSWORD); if (password.isEmpty() && engineType != Engines.HSQLDB) { @@ -97,10 +107,11 @@ private static Properties getConfigurationProperties(final JsonObject config, try { properties.load(new StringReader(configurationProperties.replaceAll("&", "\n"))); } catch (IOException e) { - LOGGER.error("Failed while parsing configuration properties"); + LOGGER.error("Failed while parsing configuration properties. Error: " + e.getMessage()); throw new RuntimeException(e); } } + LOGGER.info("Got properties: {}", properties); properties.setProperty("user", user); properties.setProperty("password", password); return properties; @@ -125,7 +136,7 @@ public static String getNonNullString(final JsonObject config, final String key) } } } catch (NullPointerException | ClassCastException e) { - LOGGER.error("Key doesn't have any mapping"); + LOGGER.info("key {} doesn't have any mapping: {}", key, e); } return value.toString().replaceAll("\"", ""); } @@ -142,11 +153,16 @@ public static void setStatementParam(PreparedStatement statement, int paramNumbe JsonObject body) throws SQLException { try { if (isNumeric(colName)) { - if ((body.get(colName) != null) && (body.get(colName) != JsonValue.NULL)) { - statement.setBigDecimal(paramNumber, body.getJsonNumber(colName).bigDecimalValue()); - } else { - statement.setBigDecimal(paramNumber, null); - } + if ((body.get(colName) != null) && (body.get(colName) != JsonValue.NULL)) { + // workaround for Firebase -> isNumeric=true, but value = "true" or "false" + if (body.get(colName).toString().equals("true") || body.get(colName).toString().equals("false")) { + statement.setBoolean(paramNumber, body.getBoolean(colName)); + } else { + statement.setBigDecimal(paramNumber, body.getJsonNumber(colName).bigDecimalValue()); + } + } else { + statement.setBigDecimal(paramNumber, null); + } } else if (isTimestamp(colName)) { if ((body.get(colName) != null) && (body.get(colName) != JsonValue.NULL)) { statement.setTimestamp(paramNumber, Timestamp.valueOf(body.getString(colName))); @@ -172,7 +188,7 @@ public static void setStatementParam(PreparedStatement statement, int paramNumbe statement.setNull(paramNumber, Types.VARCHAR); } } - } catch (java.lang.NumberFormatException e) { + } catch (java.lang.NumberFormatException | java.lang.ClassCastException e) { String message = String .format("Provided data: %s can't be cast to the column %s datatype", body.get(colName), colName); @@ -230,8 +246,7 @@ private static boolean isBoolean(String columnName) { return type != null && type.equals("boolean"); } - public static Map getColumnTypes(Connection connection, Boolean isOracle, - String tableName) { + public static Map getColumnTypes(Connection connection, String tableName) { DatabaseMetaData md; ResultSet rs = null; Map columnTypes = new HashMap<>(); @@ -243,6 +258,10 @@ public static Map getColumnTypes(Connection connection, Boolean tableName = tableName.split("\\.")[1]; } rs = md.getColumns(null, schemaName, tableName, "%"); + if (!rs.isBeforeFirst()){ + // ResultSet is empty, maybe we need to use null as Catalog? + rs = md.getColumns(null, schemaName, tableName, "%"); + } while (rs.next()) { String name = rs.getString("COLUMN_NAME").toLowerCase(); String type = detectColumnType(rs.getInt("DATA_TYPE"), rs.getString("TYPE_NAME")); @@ -255,7 +274,7 @@ public static Map getColumnTypes(Connection connection, Boolean try { rs.close(); } catch (Exception e) { - LOGGER.error("Failed to close result set!"); + LOGGER.error(e.toString()); } } } @@ -348,7 +367,7 @@ public static JsonObjectBuilder getColumnDataByType(ResultSet rs, ResultSetMetaD break; } } catch (SQLException | java.lang.NullPointerException e) { - LOGGER.error("Failed to get data by type"); + LOGGER.error("Failed to get data by type", e); throw new RuntimeException(e); } return row; @@ -373,14 +392,16 @@ public static String cleanJsonType(String rawType) { * Converts table name according Engine Type * * @param configuration should contains tableName - * @param isOracle flag is Engine type equals `oracle` + * @param isOracleOrFirebird flag is Engine type equals `oracle` */ - public static String getTableName(JsonObject configuration, boolean isOracle) { + public static String getTableName(JsonObject configuration, boolean isOracleOrFirebird) { if (configuration.containsKey(PROPERTY_TABLE_NAME) && getNonNullString(configuration, PROPERTY_TABLE_NAME).length() != 0) { String tableName = configuration.getString(PROPERTY_TABLE_NAME); if (tableName.contains(".")) { - tableName = isOracle ? tableName.split("\\.")[1].toUpperCase() : tableName.split("\\.")[1]; + tableName = isOracleOrFirebird ? tableName.split("\\.")[1].toUpperCase() : tableName.split("\\.")[1]; + } else { + tableName = isOracleOrFirebird ? tableName.toUpperCase() : tableName; } return tableName; } else { @@ -426,10 +447,10 @@ public static ArrayList getPrimaryKeyNames(String catalog, String schema while (resultSet.next()) { primaryKeysNames.add(resultSet.getString("COLUMN_NAME")); } - LOGGER.debug("Found primary key(s)"); + LOGGER.debug("Found primary key(s): '{}'", primaryKeysNames); } catch (SQLException e) { String errorMessage = "Cannot get Primary Keys Names"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } return primaryKeysNames; @@ -473,7 +494,7 @@ public static Boolean isAutoincrement(ResultSet resultSet, final boolean isOracl isAutoincrement = resultSet.getString("IS_AUTOINCREMENT").equals("YES"); } catch (SQLException e) { String errorMessage = "Cannot get property 'IS_AUTOINCREMENT'"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } } @@ -490,7 +511,7 @@ public static Boolean isNotNull(ResultSet resultSet) { return resultSet.getString("IS_NULLABLE").equals("NO"); } catch (SQLException e) { String errorMessage = "Cannot get property 'IS_NULLABLE'"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } } @@ -508,7 +529,7 @@ public static Boolean isCalculated(ResultSet resultSet, final String dbEngine) { return resultSet.getString("IS_GENERATEDCOLUMN").equals("YES"); } catch (SQLException e) { String errorMessage = "Cannot get property 'IS_GENERATEDCOLUMN'"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } case "mssql": @@ -516,7 +537,7 @@ public static Boolean isCalculated(ResultSet resultSet, final String dbEngine) { return resultSet.getString("SS_IS_COMPUTED").equals("1"); } catch (SQLException e) { String errorMessage = "Cannot get property 'SS_IS_COMPUTED'"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } case "postgresql": @@ -525,7 +546,7 @@ public static Boolean isCalculated(ResultSet resultSet, final String dbEngine) { columnDef = resultSet.getString("COLUMN_DEF"); } catch (SQLException e) { String errorMessage = "Cannot get property 'COLUMN_DEF'"; - LOGGER.error(errorMessage); + LOGGER.error(errorMessage, e); throw new RuntimeException(errorMessage, e); } return (columnDef != null) && columnDef.contains("nextval("); diff --git a/src/test/groovy/io/elastic/jdbc/TestUtils.java b/src/test/groovy/io/elastic/jdbc/TestUtils.java index 73979aa..7e151ba 100644 --- a/src/test/groovy/io/elastic/jdbc/TestUtils.java +++ b/src/test/groovy/io/elastic/jdbc/TestUtils.java @@ -12,6 +12,8 @@ public class TestUtils { public static final String TEST_TABLE_NAME = "stars"; private static final String SQL_DELETE_TABLE = " DROP TABLE IF EXISTS " + TEST_TABLE_NAME; + private static final String FIREBIRD_DELETE_TABLE = + " DROP TABLE " + TEST_TABLE_NAME; private static final String ORACLE_DELETE_TABLE = "BEGIN" + " EXECUTE IMMEDIATE 'DROP TABLE " + TEST_TABLE_NAME + "';" @@ -55,6 +57,14 @@ public class TestUtils { + "visible bit, " + "createdat DATETIME, " + "diameter INT GENERATED ALWAYS AS (radius * 2));"; + private static final String FIREBIRD_CREATE_TABLE = "RECREATE TABLE " + + TEST_TABLE_NAME + + " (ID int, " + + "NAME varchar(255) NOT NULL, " + + "RADIUS int NOT NULL," + + "DESTINATION float," + + "VISIBLE smallint, " + + "createdat TIMESTAMP)"; private static Dotenv dotenv = Dotenv.configure().ignoreIfMissing().load(); public static JsonObjectBuilder getMssqlConfigurationBuilder() { @@ -73,12 +83,37 @@ public static JsonObjectBuilder getMssqlConfigurationBuilder() { .add("connectionString", connectionString); } + public static JsonObjectBuilder getFirebirdConfigurationBuilder() { + final String host = dotenv.get("CONN_HOST_FIREBIRD"); + final String port = dotenv.get("CONN_PORT_FIREBIRD"); + final String databaseName = dotenv.get("CONN_DBNAME_FIREBIRD"); + String configProperties = ""; + if ((!dotenv.get("CONN_CONFIG_PROP_FIREBIRD", "").equals(""))) { + configProperties = dotenv.get("CONN_CONFIG_PROP_FIREBIRD"); + } + final String connectionString = + "jdbc:firebirdsql://" + host + ":" + port + "/" + databaseName + "?" + configProperties; + return Json.createObjectBuilder() + .add("dbEngine", "firebirdsql") + .add("host", host) + .add("port", port) + .add("databaseName", databaseName) + .add("user", dotenv.get("CONN_USER_FIREBIRD")) + .add("password", dotenv.get("CONN_PASSWORD_FIREBIRD")) + .add("configurationProperties", configProperties) + .add("connectionString", connectionString); + } + public static JsonObjectBuilder getMysqlConfigurationBuilder() { final String host = dotenv.get("CONN_HOST_MYSQL"); final String port = dotenv.get("CONN_PORT_MYSQL"); final String databaseName = dotenv.get("CONN_DBNAME_MYSQL"); + String configProperties = ""; + if (!dotenv.get("CONN_CONFIG_PROP_MYSQL", "").equals("")) { + configProperties = dotenv.get("CONN_CONFIG_PROP_MYSQL"); + } final String connectionString = - "jdbc:mysql://" + host + ":" + port + "/" + databaseName; + "jdbc:mysql://" + host + ":" + port + "/" + databaseName + "?" + configProperties; return Json.createObjectBuilder() .add("dbEngine", "mysql") .add("host", host) @@ -86,6 +121,7 @@ public static JsonObjectBuilder getMysqlConfigurationBuilder() { .add("databaseName", databaseName) .add("user", dotenv.get("CONN_USER_MYSQL")) .add("password", dotenv.get("CONN_PASSWORD_MYSQL")) + .add("configurationProperties", configProperties) .add("connectionString", connectionString); } @@ -146,6 +182,9 @@ public static void createTestTable(Connection connection, String dbEngine) case "postgresql": connection.createStatement().execute(POSTGRESQL_CREATE_TABLE); break; + case "firebirdsql": + connection.createStatement().execute(FIREBIRD_CREATE_TABLE); + break; default: throw new RuntimeException("Unsupported dbEngine" + dbEngine); } @@ -155,6 +194,8 @@ public static void deleteTestTable(Connection connection, String dbEngine) throws SQLException { if (dbEngine.toLowerCase().equals("oracle")){ connection.createStatement().execute(ORACLE_DELETE_TABLE); + } else if (dbEngine.toLowerCase().equals("firebirdsql")){ + connection.createStatement().execute(FIREBIRD_DELETE_TABLE); } else { connection.createStatement().execute(SQL_DELETE_TABLE); } diff --git a/src/test/groovy/io/elastic/jdbc/integration/JdbcCredentialsVerifierSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/JdbcCredentialsVerifierSpec.groovy index 663b0b1..3c2803e 100644 --- a/src/test/groovy/io/elastic/jdbc/integration/JdbcCredentialsVerifierSpec.groovy +++ b/src/test/groovy/io/elastic/jdbc/integration/JdbcCredentialsVerifierSpec.groovy @@ -22,6 +22,16 @@ class JdbcCredentialsVerifierSpec extends Specification { notThrown(Throwable.class) } + def "should verify successfully Firebird"() { + setup: + JsonObject firebirdConfig = TestUtils.getFirebirdConfigurationBuilder().build() + when: + new JdbcCredentialsVerifier().verify(firebirdConfig) + + then: + notThrown(Throwable.class) + } + def "should verify successfully MySql"() { setup: JsonObject mysqlConfig = TestUtils.getMysqlConfigurationBuilder() diff --git a/src/test/groovy/io/elastic/jdbc/integration/actions/custom_query_action/CustomQueryFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/actions/custom_query_action/CustomQueryFirebirdSpec.groovy new file mode 100644 index 0000000..fa1a579 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/actions/custom_query_action/CustomQueryFirebirdSpec.groovy @@ -0,0 +1,151 @@ +package io.elastic.jdbc.integration.actions.custom_query_action + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.actions.CustomQuery +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet + +class CustomQueryFirebirdSpec extends Specification { + @Shared + EventEmitter.Callback errorCallback + @Shared + EventEmitter.Callback snapshotCallback + @Shared + EventEmitter.Callback dataCallback + @Shared + EventEmitter.Callback reboundCallback + @Shared + EventEmitter.Callback httpReplyCallback + @Shared + EventEmitter emitter + @Shared + CustomQuery action + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, NAME varchar(255) NOT NULL, DATET timestamp, RADIUS int, DESTINATION int, VISIBLE smallint, VISIBLEDATE date)" + @Shared + String sqlDropTable = "DROP TABLE STARS" + + + def cleanupSpec() { + JsonObject config = getConfig() + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def setup() { + errorCallback = Mock(EventEmitter.Callback) + snapshotCallback = Mock(EventEmitter.Callback) + dataCallback = Mock(EventEmitter.Callback) + reboundCallback = Mock(EventEmitter.Callback) + httpReplyCallback = Mock(EventEmitter.Callback) + emitter = new EventEmitter.Builder().onData(dataCallback).onSnapshot(snapshotCallback).onError(errorCallback) + .onRebound(reboundCallback).onHttpReplyCallback(httpReplyCallback).build() + action = new CustomQuery() + prepareStarsTable() + } + + def runAction(JsonObject config, JsonObject body, JsonObject snapshot) { + Message msg = new Message.Builder().body(body).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, config, snapshot) + action.execute(params); + + } + + def prepareStarsTable() { + JsonObject config = getConfig() + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateTable) + connection.createStatement().execute("INSERT INTO stars values (1,'Taurus', '2015-02-19 10:10:10.0', 123, 5, 0, '2015-02-19')") + connection.createStatement().execute("INSERT INTO stars values (2,'Eridanus', '2017-02-19 10:10:10.0', 852, 5, 0, '2015-07-19')") + connection.close() + } + + + def getRecords(tableName) { + ArrayList records = new ArrayList(); + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + ResultSet rs = connection.createStatement().executeQuery("SELECT * FROM " + tableName + ";"); + while (rs.next()) { + records.add(rs.toRowResult().toString()); + } + rs.close(); + connection.close() + return records; + } + + def "make insert"() { + JsonObject snapshot = Json.createObjectBuilder().build() + JsonObject body = Json.createObjectBuilder() + .add("query", "INSERT INTO stars values (3, 'Rastaban', '2015-02-19 10:10:10.0', 123, 5, 1, '2018-02-19');") + .build(); + + when: + runAction(getConfig(), body, snapshot) + then: + 0 * errorCallback.receive(_) + 1 * dataCallback.receive({ it.getBody().getInt("updated") == 1 }) + + int records = getRecords("STARS").size() + expect: + records == 3 + } + + def "make select"() { + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body = Json.createObjectBuilder() + .add("query", "SELECT * FROM STARS;") + .build(); + + when: + runAction(getConfig(), body, snapshot) + then: + 0 * errorCallback.receive(_) + 2 * dataCallback.receive({ + JsonObject msgBody = it.getBody() + if (msgBody.getJsonArray("result") != null) { + msgBody.getJsonArray("result").size() == 2 + } else { + msgBody.getInt("updated") == 0 + } + } + ) + + } + + def "make delete"() { + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body = Json.createObjectBuilder() + .add("query", "DELETE FROM STARS WHERE ID = 1;") + .build(); + + when: + runAction(getConfig(), body, snapshot) + then: + 0 * errorCallback.receive(_) + 1 * dataCallback.receive({ it.getBody().getInt("updated") == 1 }) + + int records = getRecords("STARS").size() + expect: + records == 1 + } + + def getConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .add("nullableResult", "true") + .build(); + return config; + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/actions/delete_row_by_primary_key/DeleteActionFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/actions/delete_row_by_primary_key/DeleteActionFirebirdSpec.groovy new file mode 100644 index 0000000..faa0fd6 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/actions/delete_row_by_primary_key/DeleteActionFirebirdSpec.groovy @@ -0,0 +1,115 @@ +package io.elastic.jdbc.integration.actions.delete_row_by_primary_key + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.actions.DeleteRowByPrimaryKey +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet + +class DeleteActionFirebirdSpec extends Specification { + @Shared + JsonObject config = getStarsConfig() + + @Shared + EventEmitter.Callback errorCallback + @Shared + EventEmitter.Callback snapshotCallback + @Shared + EventEmitter.Callback dataCallback + @Shared + EventEmitter.Callback reboundCallback + @Shared + EventEmitter.Callback httpReplyCallback + @Shared + EventEmitter emitter + @Shared + DeleteRowByPrimaryKey action + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, NAME varchar(255) NOT NULL, DATET timestamp, RADIUS int, DESTINATION int, VISIBLE smallint, VISIBLEDATE date)" + @Shared + String sqlDropTable = "DROP TABLE STARS" + + def setup() { + createAction() + } + + def createAction() { + errorCallback = Mock(EventEmitter.Callback) + snapshotCallback = Mock(EventEmitter.Callback) + dataCallback = Mock(EventEmitter.Callback) + reboundCallback = Mock(EventEmitter.Callback) + httpReplyCallback = Mock(EventEmitter.Callback) + emitter = new EventEmitter.Builder().onData(dataCallback).onSnapshot(snapshotCallback).onError(errorCallback) + .onRebound(reboundCallback).onHttpReplyCallback(httpReplyCallback).build() + action = new DeleteRowByPrimaryKey() + } + + def runAction(JsonObject config, JsonObject body, JsonObject snapshot) { + Message msg = new Message.Builder().body(body).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, config, snapshot) + action.execute(params); + } + + def getStarsConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .add("nullableResult", "true") + .build(); + return config; + } + + def prepareStarsTable() { + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateTable) + connection.createStatement().execute("INSERT INTO stars values (1,'Taurus', '2015-02-19 10:10:10.0', 123, 5, 0, '2015-02-19')") + connection.createStatement().execute("INSERT INTO stars values (2,'Eridanus', '2017-02-19 10:10:10.0', 852, 5, 0, '2015-07-19')") + connection.close() + } + + def getRecords(tableName) { + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + ArrayList records = new ArrayList(); + String sql = "SELECT * FROM " + tableName; + ResultSet rs = connection.createStatement().executeQuery(sql); + while (rs.next()) { + records.add(rs.toRowResult().toString()); + } + rs.close() + connection.close() + return records + } + + def cleanupSpec() { + Connection connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "one delete"() { + prepareStarsTable(); + JsonObject snapshot = Json.createObjectBuilder().build() + JsonObject body = Json.createObjectBuilder() + .add("ID", 1) + .build(); + + runAction(getStarsConfig(), body, snapshot) + int first = getRecords("STARS").size() + JsonObject body2 = Json.createObjectBuilder() + .add("ID", 2) + .build() + runAction(getStarsConfig(), body2, snapshot) + int second = getRecords("STARS").size() + + expect: + first == 1 + second == 0 + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/actions/insert_action/InsertActionFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/actions/insert_action/InsertActionFirebirdSpec.groovy new file mode 100644 index 0000000..7751614 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/actions/insert_action/InsertActionFirebirdSpec.groovy @@ -0,0 +1,75 @@ +package io.elastic.jdbc.integration.actions.insert_action + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.actions.InsertAction +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet + +class InsertActionFirebirdSpec extends Specification { + @Shared + Connection connection + @Shared + EventEmitter emitter = TestUtils.getFakeEventEmitter(Mock(EventEmitter.Callback)) + @Shared + InsertAction action = new InsertAction() + @Shared + String dbEngine = "firebirdsql" + @Shared + JsonObject configuration = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", TestUtils.TEST_TABLE_NAME) + .build() + + def setupSpec() { + connection = DriverManager.getConnection(configuration.getString("connectionString"), configuration.getString("user"), configuration.getString("password")); + TestUtils.createTestTable(connection, dbEngine) + } + + def cleanupSpec() { + connection.close() + + connection = DriverManager.getConnection(configuration.getString("connectionString"), configuration.getString("user"), configuration.getString("password")); + TestUtils.deleteTestTable(connection, dbEngine) + connection.close() + } + + def getRecords(tableName) { + ArrayList records = new ArrayList(); + String sql = "SELECT * FROM " + tableName; + ResultSet rs = connection.createStatement().executeQuery(sql); + while (rs.next()) { + records.add(rs.toRowResult().toString()); + rs.toRowResult() + } + rs.close(); + return records; + } + + def "one insert"() { + JsonObject body = Json.createObjectBuilder() + .add("id", 1) + .add("name", "Taurus") + .add("radius", 12) + .add("visible", true) + .add("createdat", "2015-02-19 10:10:10.0") + .build(); + Message msg = new Message.Builder().body(body).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, configuration, Json.createObjectBuilder().build()) + + action.execute(params); + + ArrayList records = getRecords(TestUtils.TEST_TABLE_NAME) + + expect: + records.size() == 1 + records.get(0) == '{ID=1, NAME=Taurus, RADIUS=12, DESTINATION=null, VISIBLE=1, CREATEDAT=2015-02-19 10:10:10.0}' + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/actions/select_action/SelectFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/actions/select_action/SelectFirebirdSpec.groovy new file mode 100644 index 0000000..d4d3941 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/actions/select_action/SelectFirebirdSpec.groovy @@ -0,0 +1,94 @@ +package io.elastic.jdbc.integration.actions.select_action + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.actions.SelectAction +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager + +class SelectFirebirdSpec extends Specification { + + @Shared + String dbEngine = "firebirdsql" + @Shared + Connection connection + @Shared + JsonObject configuration = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", TestUtils.TEST_TABLE_NAME) + .build() + @Shared + EventEmitter.Callback errorCallback + @Shared + EventEmitter.Callback snapshotCallback + @Shared + EventEmitter.Callback dataCallback + @Shared + EventEmitter.Callback onHttpReplyCallback + @Shared + EventEmitter.Callback reboundCallback + @Shared + EventEmitter emitter + @Shared + SelectAction action + + def setupSpec() { + connection = DriverManager.getConnection(configuration.getString("connectionString"), configuration.getString("user"), configuration.getString("password")) + TestUtils.createTestTable(connection, dbEngine) + connection.createStatement().execute("INSERT INTO stars (id, name, radius, DESTINATION, visible, createdat) VALUES (1,'Hello', 1, 20, 0, '2015-02-19 10:10:10.0')"); + connection.createStatement().execute("INSERT INTO stars (id, name, radius, DESTINATION, visible, createdat) VALUES (2,'World', 1, 30, 1, '2015-02-19 10:10:10.0')"); + } + + def setup() { + action = new SelectAction() + } + + def runAction(JsonObject config, JsonObject body, JsonObject snapshot) { + Message msg = new Message.Builder().body(body).build() + errorCallback = Mock(EventEmitter.Callback) + snapshotCallback = Mock(EventEmitter.Callback) + dataCallback = Mock(EventEmitter.Callback) + reboundCallback = Mock(EventEmitter.Callback) + onHttpReplyCallback = Mock(EventEmitter.Callback) + emitter = new EventEmitter.Builder() + .onData(dataCallback) + .onSnapshot(snapshotCallback) + .onError(errorCallback) + .onRebound(reboundCallback) + .onHttpReplyCallback(onHttpReplyCallback).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, config, snapshot) + action.execute(params); + } + + def getStarsConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("sqlQuery", "SELECT * FROM STARS WHERE @id:number =id AND name=@name") + .build() + return config; + } + + def cleanupSpec() { + connection.close() + Connection deleteCon = DriverManager.getConnection(configuration.getString("connectionString"), configuration.getString("user"), configuration.getString("password")) + TestUtils.deleteTestTable(deleteCon, dbEngine) + } + + def "one select"() { + JsonObject snapshot = Json.createObjectBuilder().build(); + JsonObject body = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Hello") + .build() + when: + runAction(getStarsConfig(), body, snapshot) + then: + 0 * errorCallback.receive(_) + } + +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/actions/upsert_row_by_primary_key/UpsertRowByPrimaryFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/actions/upsert_row_by_primary_key/UpsertRowByPrimaryFirebirdSpec.groovy new file mode 100644 index 0000000..8169147 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/actions/upsert_row_by_primary_key/UpsertRowByPrimaryFirebirdSpec.groovy @@ -0,0 +1,251 @@ +package io.elastic.jdbc.integration.actions.upsert_row_by_primary_key + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.actions.UpsertRowByPrimaryKey +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet + +class UpsertRowByPrimaryFirebirdSpec extends Specification { + + @Shared + Connection connection + + @Shared + EventEmitter.Callback errorCallback + @Shared + EventEmitter.Callback snapshotCallback + @Shared + EventEmitter.Callback dataCallback + @Shared + EventEmitter.Callback reboundCallback + @Shared + EventEmitter.Callback httpReplyCallback + @Shared + EventEmitter emitter + @Shared + UpsertRowByPrimaryKey action + @Shared + JsonObject config = getStarsConfig(); + @Shared + String sqlDropStarsTable = "DROP TABLE STARS" + @Shared + String sqlCreateStarsTable = "RECREATE TABLE stars (ID int PRIMARY KEY, NAME varchar(255) NOT NULL, DATET timestamp, RADIUS int, DESTINATION int, VISIBLE smallint, VISIBLEDATE date)" + @Shared + String sqlDropPersonsTable = "DROP TABLE PERSONS" + @Shared + String sqlCreatePersonsTable = "RECREATE TABLE PERSONS (ID int, NAME varchar(255) NOT NULL, EMAIL varchar(255) NOT NULL PRIMARY KEY)" + + def cleanupSpec() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlDropStarsTable); + connection.createStatement().execute(sqlDropPersonsTable); + connection.close() + } + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateStarsTable); + connection.createStatement().execute(sqlCreatePersonsTable); + createAction() + } + def cleanup() { + connection.close() + } + + def createAction() { + errorCallback = Mock(EventEmitter.Callback) + snapshotCallback = Mock(EventEmitter.Callback) + dataCallback = Mock(EventEmitter.Callback) + reboundCallback = Mock(EventEmitter.Callback) + httpReplyCallback = Mock(EventEmitter.Callback) + emitter = new EventEmitter.Builder().onData(dataCallback).onSnapshot(snapshotCallback).onError(errorCallback) + .onRebound(reboundCallback).onHttpReplyCallback(httpReplyCallback).build() + action = new UpsertRowByPrimaryKey() + } + + def runAction(JsonObject config, JsonObject body, JsonObject snapshot) { + Message msg = new Message.Builder().body(body).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, config, snapshot) + action.execute(params); + } + + def getStarsConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .build(); + return config; + } + + def getRecords(tableName) { + ArrayList records = new ArrayList(); + String sql = "SELECT * FROM " + tableName; + ResultSet rs = connection.createStatement().executeQuery(sql); + while (rs.next()) { + records.add(rs.toRowResult().toString()); + } + rs.close(); + return records; + } + + def "one insert"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Taurus") + .add("DATET", "2015-02-19 10:10:10.0") + .add("RADIUS", 123) + .add("VISIBLE", 1) + .add("VISIBLEDATE", "2015-02-19") + .build(); + + runAction(getStarsConfig(), body, snapshot) + + ArrayList records = getRecords("STARS") + + expect: + records.size() == 1 + records.get(0) == '{ID=1, NAME=Taurus, DATET=2015-02-19 10:10:10.0, RADIUS=123, DESTINATION=null, VISIBLE=1, VISIBLEDATE=2015-02-19}' + } + + def "one insert, incorrect value: string in integer field"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Taurus") + .add("RADIUS", "test") + .build() + String exceptionClass = ""; + + try { + runAction(getStarsConfig(), body, snapshot) + } catch (Exception e) { + exceptionClass = e.getClass().getName(); + } + + expect: + exceptionClass.contains("Exception") + } + + def "two inserts"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body1 = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Taurus") + .add("RADIUS", 123) + .build() + runAction(getStarsConfig(), body1, snapshot) + + JsonObject body2 = Json.createObjectBuilder() + .add("ID", 2) + .add("NAME", "Eridanus") + .add("RADIUS", 456) + .build() + + runAction(getStarsConfig(), body2, snapshot) + + ArrayList records = getRecords("stars") + + expect: + records.size() == 2 + records.get(0) == '{ID=1, NAME=Taurus, DATET=null, RADIUS=123, DESTINATION=null, VISIBLE=null, VISIBLEDATE=null}' + records.get(1) == '{ID=2, NAME=Eridanus, DATET=null, RADIUS=456, DESTINATION=null, VISIBLE=null, VISIBLEDATE=null}' + } + + def "one insert, one update by ID"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body1 = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Taurus") + .add("RADIUS", 123) + .build() + runAction(getStarsConfig(), body1, snapshot) + + JsonObject body2 = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "Eridanus") + .build() + runAction(getStarsConfig(), body2, snapshot) + + ArrayList records = getRecords("stars") + + expect: + records.size() == 1 + records.get(0) == '{ID=1, NAME=Eridanus, DATET=null, RADIUS=123, DESTINATION=null, VISIBLE=null, VISIBLEDATE=null}' + } + + + def getPersonsConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "PERSONS") + .build() + return config + } + + def "one insert, name with quote"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body1 = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "O'Henry") + .add("EMAIL", "ohenry@elastic.io") + .build() + runAction(getPersonsConfig(), body1, snapshot) + + ArrayList records = getRecords("PERSONS") + + expect: + records.size() == 1 + records.get(0) == '{ID=1, NAME=O\'Henry, EMAIL=ohenry@elastic.io}' + } + + def "two inserts, one update by email"() { + + JsonObject snapshot = Json.createObjectBuilder().build() + + JsonObject body1 = Json.createObjectBuilder() + .add("ID", 1) + .add("NAME", "User1") + .add("EMAIL", "user1@elastic.io") + .build() + runAction(getPersonsConfig(), body1, snapshot) + + JsonObject body2 = Json.createObjectBuilder() + .add("ID", 2) + .add("NAME", "User2") + .add("EMAIL", "user2@elastic.io") + .build() + runAction(getPersonsConfig(), body2, snapshot) + + JsonObject body3 = Json.createObjectBuilder() + .add("ID", 3) + .add("NAME", "User3") + .add("EMAIL", "user2@elastic.io") + .build() + runAction(getPersonsConfig(), body3, snapshot) + + ArrayList records = getRecords("PERSONS") + + expect: + records.size() == 2 + records.get(0) == '{ID=1, NAME=User1, EMAIL=user1@elastic.io}' + records.get(1) == '{ID=3, NAME=User3, EMAIL=user2@elastic.io}' + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_for_insert_provider/ColumnNamesForInsertProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_for_insert_provider/ColumnNamesForInsertProviderFirebirdSpec.groovy new file mode 100644 index 0000000..e616076 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_for_insert_provider/ColumnNamesForInsertProviderFirebirdSpec.groovy @@ -0,0 +1,51 @@ +package io.elastic.jdbc.integration.providers.column_names_for_insert_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.ColumnNamesForInsertProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import javax.json.JsonReader +import java.sql.Connection +import java.sql.DriverManager + +class ColumnNamesForInsertProviderFirebirdSpec extends Specification { + + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (id INT PRIMARY KEY, name VARCHAR(255) NOT NULL, radius INT NOT NULL, destination FLOAT, createdat TIMESTAMP, diameter INT GENERATED ALWAYS AS (radius * 2))" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")); + connection.createStatement().execute(sqlCreateTable) + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "get metadata model, given table name"() { + ColumnNamesForInsertProvider provider = new ColumnNamesForInsertProvider() + JsonObject meta = provider.getMetaModel(config) + InputStream fis = new FileInputStream("src/test/resources/GeneratedMetadata/columnNameFirebird.json"); + JsonReader reader = Json.createReader(fis); + JsonObject expectedMetadata = reader.readObject(); + reader.close(); + print meta + expect: + meta.containsKey("in") + meta.containsKey("out") + meta.getJsonObject("in") == expectedMetadata.getJsonObject("in") + meta.getJsonObject("out") == expectedMetadata.getJsonObject("out") + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_provider/ColumnNamesProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_provider/ColumnNamesProviderFirebirdSpec.groovy new file mode 100644 index 0000000..5542010 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_provider/ColumnNamesProviderFirebirdSpec.groovy @@ -0,0 +1,42 @@ +package io.elastic.jdbc.integration.providers.column_names_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.ColumnNamesProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager + +class ColumnNamesProviderFirebirdSpec extends Specification { + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION float, CREATEDAT TIMESTAMP)" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")); + connection.createStatement().execute(sqlCreateTable) + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "get metadata model, given table name"() { + ColumnNamesProvider provider = new ColumnNamesProvider() + JsonObject meta = provider.getMetaModel(config) + print meta + expect: + meta.toString() == "{\"out\":{\"type\":\"object\",\"properties\":{\"ID\":{\"required\":false,\"title\":\"ID\",\"type\":\"number\"},\"NAME\":{\"required\":true,\"title\":\"NAME\",\"type\":\"string\"},\"RADIUS\":{\"required\":false,\"title\":\"RADIUS\",\"type\":\"number\"},\"DESTINATION\":{\"required\":false,\"title\":\"DESTINATION\",\"type\":\"number\"},\"CREATEDAT\":{\"required\":false,\"title\":\"CREATEDAT\",\"type\":\"string\"}}},\"in\":{\"type\":\"object\",\"properties\":{\"ID\":{\"required\":false,\"title\":\"ID\",\"type\":\"number\"},\"NAME\":{\"required\":true,\"title\":\"NAME\",\"type\":\"string\"},\"RADIUS\":{\"required\":false,\"title\":\"RADIUS\",\"type\":\"number\"},\"DESTINATION\":{\"required\":false,\"title\":\"DESTINATION\",\"type\":\"number\"},\"CREATEDAT\":{\"required\":false,\"title\":\"CREATEDAT\",\"type\":\"string\"}}}}" + } + +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_with_primary_key_provider/ColumnNamesWithPrimaryKeyProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_with_primary_key_provider/ColumnNamesWithPrimaryKeyProviderFirebirdSpec.groovy new file mode 100644 index 0000000..026b332 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/column_names_with_primary_key_provider/ColumnNamesWithPrimaryKeyProviderFirebirdSpec.groovy @@ -0,0 +1,42 @@ +package io.elastic.jdbc.integration.providers.column_names_with_primary_key_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.ColumnNamesWithPrimaryKeyProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager + +class ColumnNamesWithPrimaryKeyProviderFirebirdSpec extends Specification { + + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, ISDEAD smallint, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION float, CREATEDAT timestamp, PRIMARY KEY (ID))" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateTable) + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "get metadata model, given table name"() { + ColumnNamesWithPrimaryKeyProvider provider = new ColumnNamesWithPrimaryKeyProvider() + JsonObject meta = provider.getMetaModel(config) + print meta + expect: + meta.getJsonObject("in").toString() == "{\"type\":\"object\",\"properties\":{\"ID\":{\"required\":true,\"title\":\"ID\",\"type\":\"number\"},\"ISDEAD\":{\"required\":false,\"title\":\"ISDEAD\",\"type\":\"number\"},\"NAME\":{\"required\":false,\"title\":\"NAME\",\"type\":\"string\"},\"RADIUS\":{\"required\":false,\"title\":\"RADIUS\",\"type\":\"number\"},\"DESTINATION\":{\"required\":false,\"title\":\"DESTINATION\",\"type\":\"number\"},\"CREATEDAT\":{\"required\":false,\"title\":\"CREATEDAT\",\"type\":\"string\"}}}" + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/primary_column_names_provider/PrimaryColumnNamesProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/primary_column_names_provider/PrimaryColumnNamesProviderFirebirdSpec.groovy new file mode 100644 index 0000000..0298977 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/primary_column_names_provider/PrimaryColumnNamesProviderFirebirdSpec.groovy @@ -0,0 +1,44 @@ +package io.elastic.jdbc.integration.providers.primary_column_names_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.PrimaryColumnNamesProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.JsonObject +import javax.json.JsonObjectBuilder +import java.sql.Connection +import java.sql.DriverManager + +class PrimaryColumnNamesProviderFirebirdSpec extends Specification { + + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder().build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int NOT NULL, name varchar(255) NOT NULL, RADIUS int, DESTINATION float, CREATEDAT TIMESTAMP, PRIMARY KEY (ID))" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")); + connection.createStatement().execute(sqlCreateTable) + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "get metadata model, given table name"() { + + JsonObjectBuilder config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + PrimaryColumnNamesProvider provider = new PrimaryColumnNamesProvider() + JsonObject meta = provider.getMetaModel(config.build()); + print meta + expect: + meta.toString() == "{\"out\":{\"type\":\"object\",\"properties\":{\"ID\":{\"required\":true,\"title\":\"ID\",\"type\":\"number\"}}},\"in\":{\"type\":\"object\",\"properties\":{\"ID\":{\"required\":true,\"title\":\"ID\",\"type\":\"number\"}}}}" + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/table_name_provider/TableNameProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/table_name_provider/TableNameProviderFirebirdSpec.groovy new file mode 100644 index 0000000..4db3723 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/table_name_provider/TableNameProviderFirebirdSpec.groovy @@ -0,0 +1,59 @@ +package io.elastic.jdbc.integration.providers.table_name_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.TableNameProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.JsonObject +import javax.json.JsonObjectBuilder +import java.sql.Connection +import java.sql.DriverManager + +class TableNameProviderFirebirdSpec extends Specification { + + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder().build() + @Shared + String sqlDropUsersTable = "DROP TABLE USERS" + @Shared + String sqlDropOrdersTable = "DROP TABLE ORDERS" + @Shared + String sqlDropProductsTable = "DROP TABLE PRODUCTS" + @Shared + String sqlCreateUsersTable = "RECREATE TABLE USERS (ID int, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION int)" + @Shared + String sqlCreateOrdersTable = "RECREATE TABLE ORDERS (ID int, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION int)" + @Shared + String sqlCreateProductsTable = "RECREATE TABLE PRODUCTS (ID int, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION int)" + + def setupSpec() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateUsersTable); + connection.createStatement().execute(sqlCreateProductsTable); + connection.createStatement().execute(sqlCreateOrdersTable); + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropUsersTable); + connection.createStatement().execute(sqlDropProductsTable); + connection.createStatement().execute(sqlDropOrdersTable); + connection.close(); + } + + def "create tables, successful"() { + JsonObjectBuilder config = TestUtils.getFirebirdConfigurationBuilder() + TableNameProvider provider = new TableNameProvider(); + + when: + JsonObject model = provider.getSelectModel(config.build()); + + then: + print model + model.getString("ORDERS").equals("ORDERS") + model.getString("PRODUCTS").equals("PRODUCTS") + model.getString("USERS").equals("USERS") + } +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/providers/timestamp_column_names_provider/column_names_provider/TimeStampColumnNamesProviderFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/providers/timestamp_column_names_provider/column_names_provider/TimeStampColumnNamesProviderFirebirdSpec.groovy new file mode 100644 index 0000000..1639113 --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/providers/timestamp_column_names_provider/column_names_provider/TimeStampColumnNamesProviderFirebirdSpec.groovy @@ -0,0 +1,42 @@ +package io.elastic.jdbc.integration.providers.timestamp_column_names_provider.column_names_provider + +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.providers.TimeStampColumnNamesProvider +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager + +class TimeStampColumnNamesProviderFirebirdSpec extends Specification { + @Shared + Connection connection + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", "STARS") + .build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION float, CREATEDAT timestamp)" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateTable); + } + + def cleanupSpec() { + connection.createStatement().execute(sqlDropTable); + connection.close() + } + + def "get select model, given table name"() { + TimeStampColumnNamesProvider provider = new TimeStampColumnNamesProvider() + JsonObject meta = provider.getSelectModel(config) + print meta + expect: + meta.toString() == "{\"CREATEDAT\":\"CREATEDAT\"}" + } + +} diff --git a/src/test/groovy/io/elastic/jdbc/integration/triggers/get_rows_polling_trigger/GetRowsPollingTriggerFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/triggers/get_rows_polling_trigger/GetRowsPollingTriggerFirebirdSpec.groovy new file mode 100644 index 0000000..a1b5f2d --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/triggers/get_rows_polling_trigger/GetRowsPollingTriggerFirebirdSpec.groovy @@ -0,0 +1,86 @@ +package io.elastic.jdbc.integration.triggers.get_rows_polling_trigger + +import io.elastic.api.EventEmitter +import io.elastic.api.EventEmitter.Callback +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.triggers.GetRowsPollingTrigger +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import javax.json.JsonObjectBuilder +import java.sql.Connection +import java.sql.DriverManager + +class GetRowsPollingTriggerFirebirdSpec extends Specification { + + + @Shared + Connection connection; + @Shared + JsonObject config = TestUtils.getFirebirdConfigurationBuilder().build() + @Shared + String sqlDropTable = "DROP TABLE STARS" + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, ISDEAD smallint, NAME varchar(255) NOT NULL, RADIUS int, DESTINATION float, CREATEDAT timestamp)" + @Shared + String sqlInsertTable = "INSERT INTO STARS (ID, ISDEAD, NAME, RADIUS, DESTINATION, CREATEDAT) VALUES (1, 0, 'Sun', 50, 170, '2018-06-14 10:00:00')" + + def setup() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlCreateTable) + connection.createStatement().execute(sqlInsertTable) + connection.close() + } + + def cleanupSpec() { + connection = DriverManager.getConnection(config.getString("connectionString"), config.getString("user"), config.getString("password")) + connection.createStatement().execute(sqlDropTable) + connection.close() + } + + def "make a SELECT request"() { + + Callback errorCallback = Mock(Callback) + Callback snapshotCallback = Mock(Callback) + Callback dataCallback = Mock(Callback) + Callback onreboundCallback = Mock(Callback) + Callback onHttpCallback = Mock(Callback) + + EventEmitter emitter = new EventEmitter.Builder() + .onData(dataCallback) + .onSnapshot(snapshotCallback) + .onError(errorCallback) + .onHttpReplyCallback(onHttpCallback) + .onRebound(onreboundCallback).build(); + + GetRowsPollingTrigger getRowsPollingTrigger = new GetRowsPollingTrigger(); + + given: + Message msg = new Message.Builder().build(); + + JsonObjectBuilder config = TestUtils.getFirebirdConfigurationBuilder() + config.add("pollingField", "CREATEDAT") + .add("pollingValue", "2018-06-14 00:00:00") + .add("tableName", "STARS") + + JsonObjectBuilder snapshot = Json.createObjectBuilder() + snapshot.add("skipNumber", 0) + + when: + ExecutionParameters params = new ExecutionParameters(msg, emitter, config.build(), snapshot.build()) + getRowsPollingTrigger.execute(params) + + then: + 0 * errorCallback.receive(_) + dataCallback.receive({ + it.body.getInt("ID").equals(1) + it.body.getBoolean("ISDEAD").equals(0) + it.body.getString("NAME").equals("Sun") + it.body.getString("CREATEDAT").equals("2018-06-14 13:00:00.0") + }) + } +} \ No newline at end of file diff --git a/src/test/groovy/io/elastic/jdbc/integration/triggers/select_trigger/SelectTriggerFirebirdSpec.groovy b/src/test/groovy/io/elastic/jdbc/integration/triggers/select_trigger/SelectTriggerFirebirdSpec.groovy new file mode 100644 index 0000000..a129d9a --- /dev/null +++ b/src/test/groovy/io/elastic/jdbc/integration/triggers/select_trigger/SelectTriggerFirebirdSpec.groovy @@ -0,0 +1,86 @@ +package io.elastic.jdbc.integration.triggers.select_trigger + +import io.elastic.api.EventEmitter +import io.elastic.api.ExecutionParameters +import io.elastic.api.Message +import io.elastic.jdbc.TestUtils +import io.elastic.jdbc.triggers.SelectTrigger +import spock.lang.Shared +import spock.lang.Specification + +import javax.json.Json +import javax.json.JsonObject +import java.sql.Connection +import java.sql.DriverManager + +class SelectTriggerFirebirdSpec extends Specification { + + @Shared + Connection connection + @Shared + JsonObject configuration = TestUtils.getFirebirdConfigurationBuilder() + .add("tableName", TestUtils.TEST_TABLE_NAME) + .build() + + @Shared + EventEmitter.Callback errorCallback + @Shared + EventEmitter.Callback snapshotCallback + @Shared + EventEmitter.Callback dataCallback + @Shared + EventEmitter.Callback reboundCallback + @Shared + EventEmitter.Callback onHttpReplyCallback + @Shared + EventEmitter emitter + @Shared + SelectTrigger trigger + @Shared + String sqlCreateTable = "RECREATE TABLE STARS (ID int, NAME varchar(255) NOT NULL, DATET timestamp, RADIUS int, DESTINATION int)" + @Shared + String sqlInsertTable = "INSERT INTO STARS (ID, NAME) VALUES (1, 'Hello')" + + def setupSpec() { + connection = DriverManager.getConnection(configuration.getString("connectionString"), configuration.getString("user"), configuration.getString("password")); + connection.createStatement().execute(sqlCreateTable); + connection.createStatement().execute(sqlInsertTable); + connection.close() + } + + + def runTrigger(JsonObject config, JsonObject body, JsonObject snapshot) { + Message msg = new Message.Builder().body(body).build() + errorCallback = Mock(EventEmitter.Callback) + snapshotCallback = Mock(EventEmitter.Callback) + dataCallback = Mock(EventEmitter.Callback) + reboundCallback = Mock(EventEmitter.Callback) + onHttpReplyCallback = Mock(EventEmitter.Callback) + emitter = new EventEmitter.Builder() + .onData(dataCallback) + .onSnapshot(snapshotCallback) + .onError(errorCallback) + .onRebound(reboundCallback) + .onHttpReplyCallback(onHttpReplyCallback).build() + ExecutionParameters params = new ExecutionParameters(msg, emitter, config, snapshot) + trigger = new SelectTrigger() + trigger.execute(params); + } + + def getStarsConfig() { + JsonObject config = TestUtils.getFirebirdConfigurationBuilder() + .add("sqlQuery", "SELECT * from STARS where ID = 1") + .build(); + return config; + } + + def "one select"() { + JsonObject snapshot = Json.createObjectBuilder().build(); + JsonObject body = Json.createObjectBuilder().build() + when: + runTrigger(getStarsConfig(), body, snapshot) + then: + 0 * errorCallback.receive(_) + + } +} diff --git a/src/test/resources/GeneratedMetadata/columnNameFirebird.json b/src/test/resources/GeneratedMetadata/columnNameFirebird.json new file mode 100644 index 0000000..c2f373d --- /dev/null +++ b/src/test/resources/GeneratedMetadata/columnNameFirebird.json @@ -0,0 +1,47 @@ +{ + "out": { + "type": "object", + "properties": { + "result": { + "required": true, + "title": "result", + "type": "boolean" + } + } + }, + "in": { + "type": "object", + "properties": { + "ID": { + "required": true, + "title": "ID", + "type": "number" + }, + "NAME": { + "required": true, + "title": "NAME", + "type": "string" + }, + "RADIUS": { + "required": true, + "title": "RADIUS", + "type": "number" + }, + "DESTINATION": { + "required": false, + "title": "DESTINATION", + "type": "number" + }, + "CREATEDAT": { + "required": false, + "title": "CREATEDAT", + "type": "string" + }, + "DIAMETER": { + "required": false, + "title": "DIAMETER", + "type": "number" + } + } + } +} \ No newline at end of file