From 3e5b9031684a1fe54d37ae8986ccd5188d335133 Mon Sep 17 00:00:00 2001 From: nls-jajuko Date: Sat, 16 Dec 2023 17:04:18 +0200 Subject: [PATCH 1/4] WIP move generic sql to hakunapi-sql to enable other db implementations --- .gitignore | 3 ++- pom.xml | 5 ++++ src/hakunapi-source-postgis/pom.xml | 4 +++ .../hakunapi/simple/postgis/PostGISUtil.java | 27 ++++++++++--------- .../simple/postgis/SimplePostGIS.java | 1 + .../filter/PostGISGeometryFunction.java | 1 + .../filter/PostGISIntersectsIndex.java | 1 + .../postgis/filter/PostgresArrayOverlaps.java | 1 + src/hakunapi-sql/pom.xml | 26 ++++++++++++++++++ .../nls/hakunapi/sql}/BufferedResultSet.java | 2 +- .../hakunapi/sql}/ResultSetValueProvider.java | 2 +- .../java/fi/nls/hakunapi/sql}/SQLUtil.java | 2 +- .../fi/nls/hakunapi/sql}/filter/SQLAnd.java | 2 +- .../hakunapi/sql}/filter/SQLComparison.java | 4 +-- .../nls/hakunapi/sql}/filter/SQLEqualTo.java | 2 +- .../nls/hakunapi/sql}/filter/SQLFilter.java | 2 +- .../hakunapi/sql}/filter/SQLGreaterThan.java | 2 +- .../sql}/filter/SQLGreaterThanOrEqualTo.java | 2 +- .../hakunapi/sql}/filter/SQLIsNotNull.java | 4 +-- .../nls/hakunapi/sql}/filter/SQLIsNull.java | 4 +-- .../nls/hakunapi/sql}/filter/SQLLessThan.java | 2 +- .../sql}/filter/SQLLessThanOrEqualTo.java | 2 +- .../fi/nls/hakunapi/sql}/filter/SQLLike.java | 4 +-- .../fi/nls/hakunapi/sql}/filter/SQLNot.java | 2 +- .../hakunapi/sql}/filter/SQLNotEqualTo.java | 2 +- .../fi/nls/hakunapi/sql}/filter/SQLOr.java | 2 +- .../nls/hakunapi/sql}/filter/SQLLikeTest.java | 4 ++- src/pom.xml | 1 + 28 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 src/hakunapi-sql/pom.xml rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/BufferedResultSet.java (98%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/ResultSetValueProvider.java (99%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/SQLUtil.java (94%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLAnd.java (97%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLComparison.java (95%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLEqualTo.java (72%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLFilter.java (86%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLGreaterThan.java (72%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLGreaterThanOrEqualTo.java (74%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLIsNotNull.java (85%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLIsNull.java (85%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLLessThan.java (72%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLLessThanOrEqualTo.java (73%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLLike.java (97%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLNot.java (95%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLNotEqualTo.java (72%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/filter/SQLOr.java (97%) rename src/{hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/test/java/fi/nls/hakunapi/sql}/filter/SQLLikeTest.java (97%) diff --git a/.gitignore b/.gitignore index f607e35..e17b429 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .classpath .project .settings/ +.vscode # Intellij .idea/ @@ -20,4 +21,4 @@ bin/ hakuna.log pom.xml.versionsBackup -**/pom.xml.versionsBackup \ No newline at end of file +**/pom.xml.versionsBackup diff --git a/pom.xml b/pom.xml index f29fb8d..a91b27d 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,11 @@ hakunapi-simple-webapp-test-javax ${project.version} + + fi.nls.hakunapi + hakunapi-sql + ${project.version} + fi.nls.hakunapi hakunapi-source-postgis diff --git a/src/hakunapi-source-postgis/pom.xml b/src/hakunapi-source-postgis/pom.xml index 793e8d3..cd9caa6 100644 --- a/src/hakunapi-source-postgis/pom.xml +++ b/src/hakunapi-source-postgis/pom.xml @@ -9,6 +9,10 @@ hakunapi-source-postgis + + fi.nls.hakunapi + hakunapi-sql + fi.nls.hakunapi hakunapi-core diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISUtil.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISUtil.java index c7ae2d4..bbf0c80 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISUtil.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISUtil.java @@ -28,19 +28,20 @@ import fi.nls.hakunapi.simple.postgis.filter.PostGISTouches; import fi.nls.hakunapi.simple.postgis.filter.PostGISWithin; import fi.nls.hakunapi.simple.postgis.filter.PostgresArrayOverlaps; -import fi.nls.hakunapi.simple.postgis.filter.SQLAnd; -import fi.nls.hakunapi.simple.postgis.filter.SQLEqualTo; -import fi.nls.hakunapi.simple.postgis.filter.SQLFilter; -import fi.nls.hakunapi.simple.postgis.filter.SQLGreaterThan; -import fi.nls.hakunapi.simple.postgis.filter.SQLGreaterThanOrEqualTo; -import fi.nls.hakunapi.simple.postgis.filter.SQLIsNotNull; -import fi.nls.hakunapi.simple.postgis.filter.SQLIsNull; -import fi.nls.hakunapi.simple.postgis.filter.SQLLessThan; -import fi.nls.hakunapi.simple.postgis.filter.SQLLessThanOrEqualTo; -import fi.nls.hakunapi.simple.postgis.filter.SQLLike; -import fi.nls.hakunapi.simple.postgis.filter.SQLNot; -import fi.nls.hakunapi.simple.postgis.filter.SQLNotEqualTo; -import fi.nls.hakunapi.simple.postgis.filter.SQLOr; +import fi.nls.hakunapi.sql.SQLUtil; +import fi.nls.hakunapi.sql.filter.SQLAnd; +import fi.nls.hakunapi.sql.filter.SQLEqualTo; +import fi.nls.hakunapi.sql.filter.SQLFilter; +import fi.nls.hakunapi.sql.filter.SQLGreaterThan; +import fi.nls.hakunapi.sql.filter.SQLGreaterThanOrEqualTo; +import fi.nls.hakunapi.sql.filter.SQLIsNotNull; +import fi.nls.hakunapi.sql.filter.SQLIsNull; +import fi.nls.hakunapi.sql.filter.SQLLessThan; +import fi.nls.hakunapi.sql.filter.SQLLessThanOrEqualTo; +import fi.nls.hakunapi.sql.filter.SQLLike; +import fi.nls.hakunapi.sql.filter.SQLNot; +import fi.nls.hakunapi.sql.filter.SQLNotEqualTo; +import fi.nls.hakunapi.sql.filter.SQLOr; public class PostGISUtil { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java index 0323463..36dd0b7 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java @@ -29,6 +29,7 @@ import fi.nls.hakunapi.core.request.GetFeatureRequest; import fi.nls.hakunapi.core.util.EmptyFeatureStream; import fi.nls.hakunapi.core.util.U; +import fi.nls.hakunapi.sql.BufferedResultSet; /** * Streaming implementation diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISGeometryFunction.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISGeometryFunction.java index 46e7c07..e69859a 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISGeometryFunction.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISGeometryFunction.java @@ -11,6 +11,7 @@ import fi.nls.hakunapi.core.projection.ProjectionHelper; import fi.nls.hakunapi.core.property.HakunaProperty; import fi.nls.hakunapi.core.property.simple.HakunaPropertyGeometry; +import fi.nls.hakunapi.sql.filter.SQLFilter; public abstract class PostGISGeometryFunction implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISIntersectsIndex.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISIntersectsIndex.java index 5ae6383..30d2700 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISIntersectsIndex.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostGISIntersectsIndex.java @@ -11,6 +11,7 @@ import fi.nls.hakunapi.core.projection.ProjectionHelper; import fi.nls.hakunapi.core.property.HakunaProperty; import fi.nls.hakunapi.core.property.simple.HakunaPropertyGeometry; +import fi.nls.hakunapi.sql.filter.SQLFilter; public class PostGISIntersectsIndex implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostgresArrayOverlaps.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostgresArrayOverlaps.java index 2cff5a9..7d47f21 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostgresArrayOverlaps.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/PostgresArrayOverlaps.java @@ -9,6 +9,7 @@ import fi.nls.hakunapi.core.filter.Filter; import fi.nls.hakunapi.core.property.HakunaProperty; import fi.nls.hakunapi.core.property.HakunaPropertyArray; +import fi.nls.hakunapi.sql.filter.SQLFilter; public class PostgresArrayOverlaps implements SQLFilter { diff --git a/src/hakunapi-sql/pom.xml b/src/hakunapi-sql/pom.xml new file mode 100644 index 0000000..367d3a8 --- /dev/null +++ b/src/hakunapi-sql/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + fi.nls.hakunapi + src + 1.3.0-SNAPSHOT + + hakunapi-sql + + + + + fi.nls.hakunapi + hakunapi-core + + + com.zaxxer + HikariCP + + + junit + junit + test + + + \ No newline at end of file diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/BufferedResultSet.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java similarity index 98% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/BufferedResultSet.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java index efa9f65..d3260f0 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/BufferedResultSet.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis; +package fi.nls.hakunapi.sql; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/ResultSetValueProvider.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java similarity index 99% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/ResultSetValueProvider.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java index 658a898..5c18a40 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/ResultSetValueProvider.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis; +package fi.nls.hakunapi.sql; import java.nio.charset.StandardCharsets; import java.sql.Array; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLUtil.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLUtil.java similarity index 94% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLUtil.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLUtil.java index 56efc06..8b8af6f 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLUtil.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLUtil.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis; +package fi.nls.hakunapi.sql; import fi.nls.hakunapi.core.property.HakunaProperty; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLAnd.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLAnd.java similarity index 97% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLAnd.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLAnd.java index bb273b9..5bacb7d 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLAnd.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLAnd.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLComparison.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLComparison.java similarity index 95% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLComparison.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLComparison.java index a7b9f21..47183c9 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLComparison.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLComparison.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.Date; @@ -12,7 +12,7 @@ import fi.nls.hakunapi.core.filter.Filter; import fi.nls.hakunapi.core.property.HakunaProperty; -import fi.nls.hakunapi.simple.postgis.SQLUtil; +import fi.nls.hakunapi.sql.SQLUtil; public abstract class SQLComparison implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLEqualTo.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLEqualTo.java similarity index 72% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLEqualTo.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLEqualTo.java index f4c8170..258d788 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLEqualTo.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLEqualTo.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLEqualTo extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLFilter.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLFilter.java similarity index 86% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLFilter.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLFilter.java index 7ecef1c..6078426 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLFilter.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLFilter.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThan.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThan.java similarity index 72% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThan.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThan.java index 3ed2c3f..5ca59b5 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThan.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThan.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLGreaterThan extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThanOrEqualTo.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThanOrEqualTo.java similarity index 74% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThanOrEqualTo.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThanOrEqualTo.java index e9c2920..855c0c7 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLGreaterThanOrEqualTo.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLGreaterThanOrEqualTo.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLGreaterThanOrEqualTo extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNotNull.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNotNull.java similarity index 85% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNotNull.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNotNull.java index 1cd1e57..9619d36 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNotNull.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNotNull.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; @@ -6,7 +6,7 @@ import fi.nls.hakunapi.core.filter.Filter; import fi.nls.hakunapi.core.property.HakunaProperty; -import fi.nls.hakunapi.simple.postgis.SQLUtil; +import fi.nls.hakunapi.sql.SQLUtil; public class SQLIsNotNull implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNull.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNull.java similarity index 85% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNull.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNull.java index 43620fc..4c7d286 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLIsNull.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLIsNull.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; @@ -6,7 +6,7 @@ import fi.nls.hakunapi.core.filter.Filter; import fi.nls.hakunapi.core.property.HakunaProperty; -import fi.nls.hakunapi.simple.postgis.SQLUtil; +import fi.nls.hakunapi.sql.SQLUtil; public class SQLIsNull implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThan.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThan.java similarity index 72% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThan.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThan.java index 8b857c7..268970e 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThan.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThan.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLLessThan extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThanOrEqualTo.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThanOrEqualTo.java similarity index 73% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThanOrEqualTo.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThanOrEqualTo.java index 1de8964..bbc4e07 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLessThanOrEqualTo.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLessThanOrEqualTo.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLLessThanOrEqualTo extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLike.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLike.java similarity index 97% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLike.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLike.java index 7fed0d7..a0fbab2 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLLike.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLLike.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; @@ -7,7 +7,7 @@ import fi.nls.hakunapi.core.filter.Filter; import fi.nls.hakunapi.core.filter.LikeFilter; import fi.nls.hakunapi.core.property.HakunaProperty; -import fi.nls.hakunapi.simple.postgis.SQLUtil; +import fi.nls.hakunapi.sql.SQLUtil; public class SQLLike implements SQLFilter { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNot.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNot.java similarity index 95% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNot.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNot.java index de33db9..2122bab 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNot.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNot.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNotEqualTo.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNotEqualTo.java similarity index 72% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNotEqualTo.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNotEqualTo.java index 23a3b34..5747870 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLNotEqualTo.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLNotEqualTo.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; public class SQLNotEqualTo extends SQLComparison { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLOr.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLOr.java similarity index 97% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLOr.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLOr.java index 55001f4..ee4b5be 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/filter/SQLOr.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/filter/SQLOr.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/filter/SQLLikeTest.java b/src/hakunapi-sql/src/test/java/fi/nls/hakunapi/sql/filter/SQLLikeTest.java similarity index 97% rename from src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/filter/SQLLikeTest.java rename to src/hakunapi-sql/src/test/java/fi/nls/hakunapi/sql/filter/SQLLikeTest.java index 52c261a..a737aa7 100644 --- a/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/filter/SQLLikeTest.java +++ b/src/hakunapi-sql/src/test/java/fi/nls/hakunapi/sql/filter/SQLLikeTest.java @@ -1,9 +1,11 @@ -package fi.nls.hakunapi.simple.postgis.filter; +package fi.nls.hakunapi.sql.filter; import static org.junit.Assert.assertEquals; import org.junit.Test; +import fi.nls.hakunapi.sql.filter.SQLLike; + public class SQLLikeTest { @Test diff --git a/src/pom.xml b/src/pom.xml index 854cb78..726e544 100644 --- a/src/pom.xml +++ b/src/pom.xml @@ -14,6 +14,7 @@ hakunapi-config-test hakunapi-simple-servlet-javax hakunapi-simple-webapp-javax + hakunapi-sql hakunapi-source-postgis hakunapi-geojson hakunapi-jsonfg From 8ab832389f3e6c82453523c061b57e47521c1ecb Mon Sep 17 00:00:00 2001 From: nls-jajuko Date: Sun, 17 Dec 2023 15:22:14 +0200 Subject: [PATCH 2/4] private to protected and constructor for prepared valueprovider --- .../main/java/fi/nls/hakunapi/sql/BufferedResultSet.java | 8 ++++++++ .../java/fi/nls/hakunapi/sql/ResultSetValueProvider.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java index d3260f0..1a92f3d 100644 --- a/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/BufferedResultSet.java @@ -38,6 +38,14 @@ public BufferedResultSet(Connection c, PreparedStatement ps, ResultSet rs, int n this.mappers = mappers; this.buffer = new ValueContainer[bufSize]; } + public BufferedResultSet(Connection c, PreparedStatement ps, ResultSetValueProvider vp, ResultSet rs, List mappers, int bufSize) { + this.c = c; + this.ps = ps; + this.rs = rs; + this.valueProvider = vp; + this.mappers = mappers; + this.buffer = new ValueContainer[bufSize]; + } @Override public boolean hasNext() { diff --git a/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java index 5c18a40..fc41ebf 100644 --- a/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/ResultSetValueProvider.java @@ -16,8 +16,8 @@ public class ResultSetValueProvider implements ValueProvider { - private final ResultSet rs; - private final int numCols; + protected final ResultSet rs; + protected final int numCols; public ResultSetValueProvider(ResultSet rs, int numCols) { this.rs = rs; From 9099fca929ec2e27a20f77e5c423d8729f6430e4 Mon Sep 17 00:00:00 2001 From: nls-jajuko Date: Mon, 18 Dec 2023 08:42:45 +0200 Subject: [PATCH 3/4] sql generalisations --- .../simple/postgis/PostGISFeatureType.java | 13 +++++++++++++ .../simple/postgis/PostGISSimpleSource.java | 3 ++- .../hakunapi/simple/postgis/SimplePostGIS.java | 1 + .../simple/postgis/SimpleJoinGraphTest.java | 1 + .../fi/nls/hakunapi/sql}/SQLFeatureType.java | 18 ++++++++---------- .../fi/nls/hakunapi/sql}/SimpleJoinGraph.java | 2 +- 6 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISFeatureType.java rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/SQLFeatureType.java (71%) rename src/{hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis => hakunapi-sql/src/main/java/fi/nls/hakunapi/sql}/SimpleJoinGraph.java (98%) diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISFeatureType.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISFeatureType.java new file mode 100644 index 0000000..9cb080f --- /dev/null +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISFeatureType.java @@ -0,0 +1,13 @@ +package fi.nls.hakunapi.simple.postgis; + +import fi.nls.hakunapi.core.FeatureProducer; +import fi.nls.hakunapi.sql.SQLFeatureType; + +public class PostGISFeatureType extends SQLFeatureType { + + @Override + public FeatureProducer getFeatureProducer() { + return new SimplePostGIS(ds); + } + +} diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISSimpleSource.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISSimpleSource.java index 8195ac4..6b9c1bd 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISSimpleSource.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/PostGISSimpleSource.java @@ -66,6 +66,7 @@ import fi.nls.hakunapi.core.property.simple.HakunaPropertyString; import fi.nls.hakunapi.core.property.simple.HakunaPropertyUUID; import fi.nls.hakunapi.core.transformer.ValueTransformer; +import fi.nls.hakunapi.sql.SQLFeatureType; public class PostGISSimpleSource implements SimpleSource { @@ -169,7 +170,7 @@ public void close() throws IOException { public SimpleFeatureType parse(HakunaConfigParser cfg, Path path, String collectionId, int[] srids) throws Exception { // Current prefix for properties String p = "collections." + collectionId + "."; - SQLFeatureType ft = new SQLFeatureType(); + SQLFeatureType ft = new PostGISFeatureType(); ft.setName(collectionId); String db = cfg.get(p + "db"); diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java index 36dd0b7..d029ed6 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java +++ b/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimplePostGIS.java @@ -30,6 +30,7 @@ import fi.nls.hakunapi.core.util.EmptyFeatureStream; import fi.nls.hakunapi.core.util.U; import fi.nls.hakunapi.sql.BufferedResultSet; +import fi.nls.hakunapi.sql.SQLFeatureType; /** * Streaming implementation diff --git a/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraphTest.java b/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraphTest.java index 7d8ef80..e658b06 100644 --- a/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraphTest.java +++ b/src/hakunapi-source-postgis/src/test/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraphTest.java @@ -13,6 +13,7 @@ import fi.nls.hakunapi.core.join.Join; import fi.nls.hakunapi.core.join.OneToOneJoin; +import fi.nls.hakunapi.sql.SimpleJoinGraph; public class SimpleJoinGraphTest { diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLFeatureType.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLFeatureType.java similarity index 71% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLFeatureType.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLFeatureType.java index ea1b6c6..f1713fc 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SQLFeatureType.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLFeatureType.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis; +package fi.nls.hakunapi.sql; import java.util.List; @@ -8,12 +8,12 @@ import fi.nls.hakunapi.core.SimpleFeatureType; import fi.nls.hakunapi.core.join.Join; -public class SQLFeatureType extends SimpleFeatureType { +public abstract class SQLFeatureType extends SimpleFeatureType { - private String dbSchema; - private String primaryTable; - private List joins; - private DataSource ds; + protected String dbSchema; + protected String primaryTable; + protected List joins; + protected DataSource ds; public String getDbSchema() { return dbSchema; @@ -47,9 +47,7 @@ public void setDatabase(DataSource ds) { this.ds = ds; } - @Override - public FeatureProducer getFeatureProducer() { - return new SimplePostGIS(ds); - } + + public abstract FeatureProducer getFeatureProducer() ; } diff --git a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraph.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SimpleJoinGraph.java similarity index 98% rename from src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraph.java rename to src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SimpleJoinGraph.java index ef3159d..ce00a64 100644 --- a/src/hakunapi-source-postgis/src/main/java/fi/nls/hakunapi/simple/postgis/SimpleJoinGraph.java +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SimpleJoinGraph.java @@ -1,4 +1,4 @@ -package fi.nls.hakunapi.simple.postgis; +package fi.nls.hakunapi.sql; import java.util.ArrayList; import java.util.Collections; From 795e1056e8b5b02b83c1995472444a1118668842 Mon Sep 17 00:00:00 2001 From: nls-jajuko Date: Mon, 18 Dec 2023 09:43:28 +0200 Subject: [PATCH 4/4] SQLSimpleSource --- .../fi/nls/hakunapi/sql/SQLSimpleSource.java | 734 ++++++++++++++++++ 1 file changed, 734 insertions(+) create mode 100644 src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLSimpleSource.java diff --git a/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLSimpleSource.java b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLSimpleSource.java new file mode 100644 index 0000000..2c7e157 --- /dev/null +++ b/src/hakunapi-sql/src/main/java/fi/nls/hakunapi/sql/SQLSimpleSource.java @@ -0,0 +1,734 @@ +package fi.nls.hakunapi.sql; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.file.Path; +import java.sql.Array; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import fi.nls.hakunapi.core.HakunapiPlaceholder; +import fi.nls.hakunapi.core.PaginationStrategy; +import fi.nls.hakunapi.core.PaginationStrategyCursor; +import fi.nls.hakunapi.core.PaginationStrategyOffset; +import fi.nls.hakunapi.core.SimpleFeatureType; +import fi.nls.hakunapi.core.SimpleSource; +import fi.nls.hakunapi.core.config.HakunaConfigParser; +import fi.nls.hakunapi.core.geom.HakunaGeometryType; +import fi.nls.hakunapi.core.join.Join; +import fi.nls.hakunapi.core.join.ManyToManyJoin; +import fi.nls.hakunapi.core.join.ManyToOneJoin; +import fi.nls.hakunapi.core.join.OneToManyJoin; +import fi.nls.hakunapi.core.join.OneToOneJoin; +import fi.nls.hakunapi.core.property.HakunaProperty; +import fi.nls.hakunapi.core.property.HakunaPropertyComposite; +import fi.nls.hakunapi.core.property.HakunaPropertyHidden; +import fi.nls.hakunapi.core.property.HakunaPropertyNumberEnum; +import fi.nls.hakunapi.core.property.HakunaPropertyStatic; +import fi.nls.hakunapi.core.property.HakunaPropertyStringEnum; +import fi.nls.hakunapi.core.property.HakunaPropertyTransformed; +import fi.nls.hakunapi.core.property.HakunaPropertyType; +import fi.nls.hakunapi.core.property.HakunaPropertyWriter; +import fi.nls.hakunapi.core.property.HakunaPropertyWriters; +import fi.nls.hakunapi.core.property.simple.HakunaPropertyGeometry; +import fi.nls.hakunapi.core.property.simple.HakunaPropertyInt; +import fi.nls.hakunapi.core.property.simple.HakunaPropertyLong; +import fi.nls.hakunapi.core.property.simple.HakunaPropertyString; +import fi.nls.hakunapi.core.property.simple.HakunaPropertyUUID; +import fi.nls.hakunapi.core.transformer.ValueTransformer; + +public abstract class SQLSimpleSource implements SimpleSource { + + protected static final Logger LOG = LoggerFactory.getLogger(SQLSimpleSource.class); + + protected List dataSourcesToClose = new ArrayList<>(); + protected Map dataSources = new HashMap<>(); + + public abstract String getType(); + public abstract SQLFeatureType createFeatureType(); + protected abstract HakunaPropertyGeometry getGeometryColumn(DataSource ds, String schema, String table, String name, + String column, int[] srids, int sridStorage, boolean isDefault, boolean nullable, boolean hidden) throws SQLException ; + + + + protected DataSource getDataSource(HakunaConfigParser cfg, Path path, String name) throws NamingException { + DataSource ds = dataSources.get(name); + if (ds == null) { + if (name.startsWith("java:comp/env/")) { + ds = getJNDIDataSource(new InitialContext(), name); + } else { + HikariDataSource hds = readDataSource(cfg, path, name); + dataSourcesToClose.add(hds); + ds = hds; + } + dataSources.put(name, ds); + } + return ds; + } + + protected DataSource getJNDIDataSource(InitialContext ctx, String name) throws NamingException { + Object value = ctx.lookup(name); + if (value == null) { + throw new IllegalArgumentException("Could not find object from JNDI with name " + name); + } + if (!(value instanceof DataSource)) { + throw new IllegalArgumentException("JNDI object with name " + name + " not a DataSource"); + } + return (DataSource) value; + } + + protected HikariDataSource readDataSource(HakunaConfigParser cfg, Path path, String name) { + final Properties props; + + if (Arrays.stream(cfg.getMultiple("db", new String[0])).anyMatch(name::equals)) { + // if name appears in db=a,b,c,d listing then consider it's configuration to be inlined + props = getInlinedDBProperties(cfg, name); + } else { + // Not inlined, check separate file $name.properties + String dataSourcePath = getDataSourcePath(path, name); + props = loadProperties(dataSourcePath); + } + + HikariConfig config = new HikariConfig(props); + HikariDataSource ds = new HikariDataSource(config); + return ds; + } + + public static Properties getInlinedDBProperties(HakunaConfigParser cfg, String name) { + Properties props = new Properties(); + cfg.getAllStartingWith("db." + name + ".").forEach(props::setProperty); + return props; + } + + public static String getDataSourcePath(Path path, String name) { + String prefix = ""; + Path parent = path.getParent(); + if (parent != null) { + prefix = path.getParent().toString() + "/"; + } + return prefix + name + ".properties"; + } + + public Properties loadProperties(String path) { + try (InputStream in = getInputStream(path)) { + Properties props = new Properties(); + props.load(in); + return HakunapiPlaceholder.replacePlaceholders(props); + } catch (IOException e) { + throw new RuntimeException("Failed to read property file", e); + } + } + + protected InputStream getInputStream(String path) throws IOException { + File file = new File(path); + if (file.isFile()) { + return new FileInputStream(file); + } + InputStream resource = getClass().getResourceAsStream(path); + if (resource != null) { + return resource; + } + resource = getClass().getClassLoader().getResourceAsStream(path); + return resource; + } + + @Override + public void close() throws IOException { + dataSourcesToClose.forEach(HikariDataSource::close); + } + + @Override + public SimpleFeatureType parse(HakunaConfigParser cfg, Path path, String collectionId, int[] srids) throws Exception { + // Current prefix for properties + String p = "collections." + collectionId + "."; + SQLFeatureType ft = createFeatureType(); + ft.setName(collectionId); + + String db = cfg.get(p + "db"); + DataSource ds = getDataSource(cfg, path, db); + if (ds == null) { + throw new IllegalArgumentException("Unknown db: " + db + " collection: " + collectionId); + } + + String dbSchema = cfg.get(p + "schema", "public").replace("\"", ""); + + String primaryTable = cfg.get(p + "table"); + if (primaryTable == null || primaryTable.isEmpty()) { + throw new IllegalArgumentException("Missing required property " + (p + "table")); + } + primaryTable = primaryTable.replace("\"", ""); + + int sridStorage = Integer.parseInt(cfg.get(p + "srid.storage", "0")); + + List joins = new ArrayList<>(); + for (String join : cfg.getMultiple("joins")) { + Join j = parseJoin(cfg, join); + joins.add(j); + joins.add(j.swapOrder()); + } + + ft.setPrimaryTable(primaryTable); + ft.setDatabase(ds); + ft.setDbSchema(dbSchema); + ft.setJoins(joins); + + Map> columnsByTable = new HashMap<>(); + String[] idTables = cfg.getMultiple(p + "id.table"); + String[] idMappings = cfg.getMultiple(p + "id.mapping"); + String[] idAliases = cfg.getMultiple(p + "id.alias"); + if (idMappings.length == 0) { + throw new IllegalArgumentException("Missing required property " + (p + "id.mapping")); + } + for (int i = 0; i < idMappings.length; i++) { + String table = idTables.length > i ? idTables[i] : primaryTable; + String mapping = idMappings[i]; + String alias = idAliases.length > i ? idAliases[i] : null; + if (!HakunaConfigParser.isStaticMapping(mapping)) { + columnsByTable.computeIfAbsent(table, __ -> new HashSet<>()).add(new ColumnAlias(mapping, alias)); + } + } + + String geometryTable = cfg.get(p + "geometry.table", primaryTable); + String geometryMapping = cfg.get(p + "geometry.mapping"); + String geometryAlias = cfg.get(p + "geometry.alias"); + if (geometryMapping != null) { + geometryMapping = geometryMapping.replace("\"", ""); + } + if (geometryMapping != null) { + columnsByTable.computeIfAbsent(geometryTable, __ -> new HashSet<>()).add(new ColumnAlias(geometryMapping, geometryAlias)); + } + + String[] properties = cfg.getMultiple(p + "properties"); + if (properties == null || properties.length == 0 || "*".equals(properties[0])) { + Set reserved = new HashSet<>(); + if (idMappings.length == 1) { + reserved.add(idMappings[0]); + } + if (geometryMapping != null) { + reserved.add(geometryMapping); + } + for (int i = 1; i < properties.length; i++) { + if (properties[i].charAt(0) == '~' && properties[i].length() > 1) { + reserved.add(properties[i].substring(1)); + } + } + properties = discoverProperties(ds, dbSchema, primaryTable, column -> !reserved.contains(column)); + } + + for (String property : properties) { + String table = cfg.get(p + "properties." + property + ".table", primaryTable); + String mapping = cfg.get(p + "properties." + property + ".mapping", property); + String alias = cfg.get(p + "properties." + property + ".alias", null); + mapping = mapping.replace("\"", ""); + ColumnAlias ca = new ColumnAlias(mapping, alias); + if (!HakunaConfigParser.isStaticMapping(mapping)) { + columnsByTable.computeIfAbsent(table, __ -> new HashSet<>()).add(ca); + } + } + + Map>> propertyTypes = new HashMap<>(); + Map> propertyNullability = new HashMap<>(); + for (Map.Entry> entry : columnsByTable.entrySet()) { + String table = entry.getKey(); + Set columns = entry.getValue(); + String select = getSelectSchema(dbSchema, table, columns); + Map> tablePropertyTypes = getPropertyTypes(ds, select); + propertyTypes.put(table, tablePropertyTypes); + Map propertyNullable = getNullability(ds, select); + propertyNullability.put(table, propertyNullable); + } + + HakunaProperty idProperty; + if (idMappings.length == 1) { + // Simple + String table = idTables.length > 0 ? idTables[0] : primaryTable; + String mapping = idMappings[0]; + String alias = idAliases.length > 0 ? idAliases[0] : mapping; + HakunaPropertyType idColumnType = propertyTypes.get(table).get(alias).get(0); + boolean nullable = propertyNullability.get(table).get(alias); + HakunaPropertyWriter propWriter = HakunaPropertyWriters.getIdPropertyWriter(ft, ft.getName(), "id", idColumnType); + switch (idColumnType) { + case INT: + idProperty = new HakunaPropertyInt("id", table, mapping, nullable, true, propWriter); + break; + case LONG: + idProperty = new HakunaPropertyLong("id", table, mapping, nullable, true, propWriter); + break; + case STRING: + idProperty = new HakunaPropertyString("id", table, mapping, nullable, true, propWriter); + break; + case UUID: + idProperty = new HakunaPropertyUUID("id", table, mapping, nullable, true, propWriter); + break; + default: + throw new IllegalArgumentException("Invalid id type"); + } + } else { + // Composite + List parts = new ArrayList<>(); + for (int i = 0; i < idMappings.length; i++) { + String table = idTables.length > i ? idTables[i] : primaryTable; + String mapping = idMappings[i]; + String alias = idAliases.length > 0 ? idAliases[0] : mapping; + boolean nullable = propertyNullability.get(table).get(alias); + if (HakunaConfigParser.isStaticMapping(mapping)) { + parts.add(HakunaPropertyStatic.create("id", "table", mapping.substring(1, mapping.length() - 1))); + } else { + List typeChain = propertyTypes.get(table).get(alias); + boolean unique = false; + boolean hidden = true; + parts.add(getDynamicProperty(cfg, HakunaConfigParser.REF_ID_PROP, table, mapping, typeChain, nullable, unique, hidden)); + } + } + String transformerClass = cfg.get(p + "id.transformer"); + String transformerArg = cfg.get(p + "id.transformer.arg"); + ValueTransformer vt = cfg.instantiateTransformer(transformerClass); + HakunaPropertyWriter propWriter = HakunaPropertyWriters.getIdPropertyWriter(ft, ft.getName(), "id", vt.getPublicType()); + idProperty = new HakunaPropertyComposite("id", parts, vt, true, propWriter); + vt.init(idProperty, transformerArg); + } + ft.setId(idProperty); + + if (geometryMapping != null) { + boolean isDefaultGeometry = true; + boolean nullable = propertyNullability.get(geometryTable).get(geometryMapping); + boolean hidden = false; + HakunaPropertyGeometry geometryProperty = getGeometryColumn(ds, dbSchema, geometryTable, "geometry", + geometryMapping, srids, sridStorage, isDefaultGeometry, nullable, hidden); + ft.setGeom(geometryProperty); + } + + List hakunaProps = new ArrayList<>(); + for (String property : properties) { + String propertyPrefix = p + "properties." + property + "."; + String mapping = cfg.get(propertyPrefix + "mapping", property); + mapping = mapping.replace("\"", ""); + String[] enumeration = cfg.getMultiple(propertyPrefix + "enum"); + String transformerClass = cfg.get(propertyPrefix + "transformer"); + String transformerArg = cfg.get(propertyPrefix + "transformer.arg"); + boolean unique = Boolean.parseBoolean(cfg.get(propertyPrefix + "unique", "false")); + boolean hidden = Boolean.parseBoolean(cfg.get(propertyPrefix + "hidden", "false")); + String table = cfg.get(p + "properties." + property + ".table", primaryTable); + String alias = cfg.get(p + "properties." + property + ".alias", mapping); + + if (HakunaConfigParser.isStaticMapping(mapping)) { + String value = mapping.substring(1, mapping.length() - 1); + hakunaProps.add(HakunaPropertyStatic.create(property, table, value)); + continue; + } + + Map> tableTypes = propertyTypes.get(table); + List type = tableTypes.get(alias); + if (type == null) { + throw new IllegalArgumentException( + "Property " + property + " with mapping " + mapping + " can't find type from table " + table); + } + + HakunaProperty hakunaProperty; + if (type.size() == 1 && type.get(0) == HakunaPropertyType.GEOMETRY) { + boolean isDefaultGeometry = false; + boolean nullable = propertyNullability.get(table).get(mapping); + hakunaProperty = getGeometryColumn(ds, dbSchema, table, property, mapping, srids, sridStorage, + isDefaultGeometry, nullable, hidden); + } else { + boolean nullable = propertyNullability.get(table).get(mapping); + hakunaProperty = getDynamicProperty(cfg, property, table, mapping, type, nullable, unique, hidden); + if (transformerClass != null) { + ValueTransformer transformer = cfg.instantiateTransformer(transformerClass); + hakunaProperty = new HakunaPropertyTransformed(hakunaProperty, transformer); + transformer.init(hakunaProperty, transformerArg); + } + if (enumeration.length > 0) { + if (hakunaProperty.getType() == HakunaPropertyType.INT + || hakunaProperty.getType() == HakunaPropertyType.LONG) { + hakunaProperty = new HakunaPropertyNumberEnum(hakunaProperty, + Arrays.stream(enumeration).map(Long::parseLong).collect(Collectors.toSet())); + } else if (hakunaProperty.getType() == HakunaPropertyType.STRING) { + hakunaProperty = new HakunaPropertyStringEnum(hakunaProperty, + Arrays.stream(enumeration).collect(Collectors.toSet())); + } else { + // Log the fact that we are ignoring this + } + } + } + hakunaProps.add(hakunaProperty); + } + ft.setProperties(hakunaProps); + + ft.setPaginationStrategy(getPaginationStrategy(cfg, p, ft)); + + return ft; + } + + protected String[] discoverProperties(DataSource ds, String schema, String table, Predicate check) throws SQLException { + try (Connection c = ds.getConnection()) { + DatabaseMetaData md = c.getMetaData(); + try (ResultSet rs = md.getColumns(null, schema, table, null)) { + List columns = new ArrayList<>(); + while (rs.next()) { + String column = rs.getString(4); + if (check.test(column)) { + columns.add(column); + } + } + return columns.toArray(new String[0]); + } + } + } + + public static Map getNullability(DataSource ds, String select) throws SQLException { + try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(select)) { + LOG.debug(ps.toString()); + Map labelToNullable = new LinkedHashMap<>(); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + ResultSetMetaData rsmd = rs.getMetaData(); + int numCols = rsmd.getColumnCount(); + for (int i = 1; i <= numCols; i++) { + String label = rsmd.getColumnLabel(i); + boolean nullable = rsmd.isNullable(i) != ResultSetMetaData.columnNoNulls; + labelToNullable.put(label, nullable); + } + } + return labelToNullable; + } + } + + protected static List parseArrayType(Class cls) { + List chain = new ArrayList<>(); + while (cls.isArray()) { + chain.add(HakunaPropertyType.ARRAY); + cls = cls.getComponentType(); + } + if (cls.equals(Short.class) || cls.equals(Integer.class)) { + chain.add(HakunaPropertyType.INT); + } else if (cls.equals(Long.class) || cls.equals(BigInteger.class)) { + chain.add(HakunaPropertyType.LONG); + } else if (cls.equals(Float.class)) { + chain.add(HakunaPropertyType.FLOAT); + } else if (cls.equals(Double.class) || cls.equals(BigDecimal.class)) { + chain.add(HakunaPropertyType.DOUBLE); + } else if (cls.equals(Boolean.class)) { + chain.add(HakunaPropertyType.BOOLEAN); + } else if (cls.equals(String.class)) { + chain.add(HakunaPropertyType.STRING); + } else { + throw new IllegalArgumentException("Unsupported array type from class!"); + } + return chain; + } + + protected static HakunaPropertyType parseArrayType(String typeName) { + switch (typeName) { + case "_bool": + return HakunaPropertyType.BOOLEAN; + case "_int2": + case "_int4": + return HakunaPropertyType.INT; + case "_int8": + return HakunaPropertyType.LONG; + case "_float4": + return HakunaPropertyType.FLOAT; + case "_numeric": + case "_float8": + return HakunaPropertyType.DOUBLE; + case "_varchar": + case "_text": + return HakunaPropertyType.STRING; + } + throw new IllegalArgumentException("Can not determine array type from " + typeName); + } + + protected PaginationStrategy getPaginationStrategy(HakunaConfigParser cfg, String p, SimpleFeatureType sft) { + String paginationStrategy = cfg.get(p + "pagination.strategy", "cursor").toLowerCase(); + switch (paginationStrategy) { + case "cursor": + return getPaginationCursor(cfg, p, sft); + case "offset": + return PaginationStrategyOffset.INSTANCE; + default: + throw new IllegalArgumentException("Unknown pagination strategy " + paginationStrategy); + } + } + + protected PaginationStrategyCursor getPaginationCursor(HakunaConfigParser cfg, String p, SimpleFeatureType sft) { + String[] pagination = cfg.getMultiple(p + "pagination"); + String[] paginationOrder = cfg.getMultiple(p + "pagination.order"); + // int maxGroupSize = Integer.parseInt(get(p + "pagination.maxGroupSize", "1")); + + if (pagination.length == 0) { + HakunaProperty id = new HakunaPropertyHidden(sft.getId()); + boolean asc = true; + return new PaginationStrategyCursor( + Collections.singletonList(id), + Collections.singletonList(asc) + ); + } else { + List props = new ArrayList<>(pagination.length); + List ascending = new ArrayList<>(pagination.length); + for (int i = 0; i < pagination.length; i++) { + HakunaProperty prop = cfg.getProperty(sft, pagination[i]); + HakunaProperty hidden = new HakunaPropertyHidden(prop); + Boolean asc = paginationOrder.length > i ? !"DESC".equalsIgnoreCase(paginationOrder[i]) : true; + props.add(hidden); + ascending.add(asc); + } + return new PaginationStrategyCursor(props, ascending); + } + } + + public Map> getPropertyTypes(DataSource ds, String select) throws SQLException { + try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(select)) { + LOG.debug(ps.toString()); + Map> propertyTypes = new LinkedHashMap<>(); + try (ResultSet rs = ps.executeQuery()) { + boolean hasRow = rs.next(); + ResultSetMetaData rsmd = rs.getMetaData(); + int numCols = rsmd.getColumnCount(); + for (int i = 1; i <= numCols; i++) { + String label = rsmd.getColumnLabel(i); + int type = rsmd.getColumnType(i); + rsmd.isNullable(i); + String typeName = rsmd.getColumnTypeName(i); + HakunaPropertyType hakunaType = fromJDBCType(type, typeName); + if (hakunaType == null) { + throw new IllegalArgumentException("Unknown type " + type + " column " + label); + } + if (hakunaType != HakunaPropertyType.ARRAY) { + propertyTypes.put(label, Collections.singletonList(hakunaType)); + } else { + List chain = null; + if (hasRow) { + Array array = rs.getArray(i); + if (array != null) { + Object actualArray = array.getArray(); + Class cls = actualArray.getClass(); + array.free(); + chain = parseArrayType(cls); + } + } + if (chain == null) { + chain = Arrays.asList(HakunaPropertyType.ARRAY, parseArrayType(typeName)); + } + propertyTypes.put(label, chain); + } + } + } + return propertyTypes; + } + } + + public HakunaPropertyType fromJDBCType(int columnType, String columnTypeName) { + + if("json".equals(columnTypeName)||"jsonb".equals(columnTypeName)) { + return HakunaPropertyType.JSON; + } + + switch (columnType) { + case java.sql.Types.BIT: + case java.sql.Types.BOOLEAN: + return HakunaPropertyType.BOOLEAN; + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + return HakunaPropertyType.INT; + case java.sql.Types.BIGINT: + return HakunaPropertyType.LONG; + case java.sql.Types.REAL: + return HakunaPropertyType.FLOAT; + case java.sql.Types.FLOAT: + case java.sql.Types.DECIMAL: + case java.sql.Types.DOUBLE: + case java.sql.Types.NUMERIC: + return HakunaPropertyType.DOUBLE; + case java.sql.Types.CHAR: + case java.sql.Types.NCHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.NVARCHAR: + return HakunaPropertyType.STRING; + case java.sql.Types.DATE: + return HakunaPropertyType.DATE; + case java.sql.Types.TIMESTAMP: + switch (columnTypeName) { + case "timestamptz": + return HakunaPropertyType.TIMESTAMPTZ; + default: + return HakunaPropertyType.TIMESTAMP; + } + case java.sql.Types.OTHER: + switch (columnTypeName) { + case "uuid": + return HakunaPropertyType.UUID; + case "geometry": + return HakunaPropertyType.GEOMETRY; + default: + return null; + } + case java.sql.Types.ARRAY: + return HakunaPropertyType.ARRAY; + default: + return null; + } + } + + + protected int getSrid(String table, String column, int sridStorage, int dbSrid) { + if (dbSrid <= 0 && sridStorage <= 0) { + throw new IllegalArgumentException(String.format("For table %s column %s srid is unknown!", table, column)); + } else if (dbSrid <= 0) { + return sridStorage; + } else { + if (sridStorage > 0) { + LOG.warn("For table {} column {} using srid {} from database instead of configured {}", table, column, dbSrid, sridStorage); + } + return dbSrid; + } + } + + protected static String unquote(String column) { + if (column == null || column.isEmpty()) { + return column; + } + if (column.charAt(0) == '"') { + return column.substring(1, column.length() - 1); + } + return column; + } + + protected HakunaProperty getDynamicProperty(HakunaConfigParser cfg, String name, String table, String column, + List typeChain, boolean nullable, boolean unique, boolean hidden) { + return cfg.getDynamicProperty(name, table, column, typeChain, nullable, unique, hidden); + } + + + protected final class ColumnAlias { + + public final String column; + public final String alias; + public final boolean escape; + + public ColumnAlias(String column, String alias) { + this(column, alias, true); + } + + public ColumnAlias(String column, String alias, boolean escape) { + this.column = column; + this.alias = alias; + this.escape = escape; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((alias == null) ? 0 : alias.hashCode()); + result = prime * result + ((column == null) ? 0 : column.hashCode()); + result = prime * result + (escape ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ColumnAlias other = (ColumnAlias) obj; + if (alias == null) { + if (other.alias != null) + return false; + } else if (!alias.equals(other.alias)) + return false; + if (column == null) { + if (other.column != null) + return false; + } else if (!column.equals(other.column)) + return false; + if (escape != other.escape) + return false; + return true; + } + + } + + protected Join parseJoin(HakunaConfigParser cfg, String join) { + String propertyPrefix = "joins." + join + "."; + String type = cfg.get(propertyPrefix + "type"); + String leftTable = cfg.get(propertyPrefix + "leftTable"); + String leftColumn = cfg.get(propertyPrefix + "leftColumn"); + String rightTable = cfg.get(propertyPrefix + "rightTable"); + String rightColumn = cfg.get(propertyPrefix + "rightColumn"); + String joinTable = cfg.get(propertyPrefix + "joinTable"); + String joinColumnLeft = cfg.get(propertyPrefix + "joinColumnLeft"); + String joinColumnRight = cfg.get(propertyPrefix + "joinColumnRight"); + switch (type.toLowerCase()) { + case "onetoone": + return new OneToOneJoin(leftTable, leftColumn, rightTable, rightColumn); + case "onetomany": + return new OneToManyJoin(leftTable, leftColumn, rightTable, rightColumn); + case "manytoone": + return new ManyToOneJoin(leftTable, leftColumn, rightTable, rightColumn); + case "manytomany": + return new ManyToManyJoin(leftTable, leftColumn, rightTable, rightColumn, joinTable, joinColumnLeft, + joinColumnRight); + default: + throw new IllegalArgumentException("Invalid join type " + type + " for join " + join); + } + } + + protected String getSelectSchema(String schema, String table, Set columns) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT "); + for (ColumnAlias col : columns) { + if (col.escape) { + sb.append('"').append(col.column).append('"'); + } else { + sb.append(col.column); + } + if (col.alias != null) { + sb.append(" AS "); + sb.append('"').append(col.alias).append('"'); + } + sb.append(','); + } + sb.setLength(sb.length() - 1); + sb.append(" FROM "); + sb.append('"').append(schema).append('"').append('.'); + sb.append('"').append(table).append('"'); + sb.append(" LIMIT 1"); + return sb.toString(); + } + +}