From 9dd5e2a83501e88d8d4d511ce862d93b0fa555a5 Mon Sep 17 00:00:00 2001 From: werner daehn Date: Mon, 13 Jan 2025 11:42:24 +0100 Subject: [PATCH 1/3] update --- ....eclipse.wst.common.project.facet.core.xml | 1 - appcontainerapp/pom.xml | 67 ++-- .../appcontainer/db/rest/DatabaseRead.java | 9 +- .../db/rest/DatabaseRecordLookup.java | 9 +- .../main/webapp/ui5libs/amcharts/base/Line.js | 4 +- .../ui5libs/amcharts/base/PercentSeries.js | 14 + dbactivationabap/pom.xml | 20 ++ .../src/main/java/META-INF/MANIFEST.MF | 3 + ...ntainer.plugins.database.IDatabaseProvider | 1 + .../AbapActivationServiceDirectory.java | 18 ++ .../databaseabap/AbapCatalogService.java | 79 +++++ .../plugins/databaseabap/AbapDataTypes.java | 61 ++++ .../databaseabap/AbapProcedureMetadata.java | 7 + .../plugins/databaseabap/AbapProvider.java | 132 ++++++++ .../databaseabap/AbapStoredProcedure.java | 304 ++++++++++++++++++ .../databaseabap/TableParameterMetadata.java | 43 +++ ....eclipse.wst.common.project.facet.core.xml | 1 - dbactivationbase/pom.xml | 21 +- .../dbunittest/JavaScriptExecutor.java | 5 +- .../plugins/database/IDatabaseProvider.java | 2 + .../dbactivationbase/TestProvider.java | 16 + dbactivationhana/pom.xml | 2 +- .../plugins/databasehana/HanaProvider.java | 16 + .../antlr/SqlscriptsBaseListener.java | 1 - dbactivationmysql/pom.xml | 2 +- .../plugins/databasemysql/MysqlProvider.java | 16 + .../antlr/SqlscriptsBaseListener.java | 1 - dbactivationpostgresql/pom.xml | 2 +- .../PostgresqlProvider.java | 16 + .../antlr/SqlscriptsBaseListener.java | 1 - dbactivationsnowflake/pom.xml | 2 +- .../databasesnowflake/SnowflakeProvider.java | 16 + .../antlr/SqlscriptsBaseListener.java | 1 - dbactivationsqlserver/pom.xml | 2 +- .../databasesqlserver/SqlserverProvider.java | 27 ++ .../antlr/SqlscriptsBaseListener.java | 1 - docker/Dockerfile-Abap | 15 + docker/server-abap.xml | 120 +++++++ pom.xml | 30 +- 39 files changed, 1014 insertions(+), 74 deletions(-) create mode 100644 dbactivationabap/pom.xml create mode 100644 dbactivationabap/src/main/java/META-INF/MANIFEST.MF create mode 100644 dbactivationabap/src/main/java/META-INF/services/io.rtdi.appcontainer.plugins.database.IDatabaseProvider create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapActivationServiceDirectory.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapCatalogService.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapDataTypes.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProcedureMetadata.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProvider.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapStoredProcedure.java create mode 100644 dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/TableParameterMetadata.java create mode 100644 docker/Dockerfile-Abap create mode 100644 docker/server-abap.xml diff --git a/appcontainerapp/.settings/org.eclipse.wst.common.project.facet.core.xml b/appcontainerapp/.settings/org.eclipse.wst.common.project.facet.core.xml index b5d7c1fb..c31799d6 100644 --- a/appcontainerapp/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/appcontainerapp/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -1,6 +1,5 @@ - diff --git a/appcontainerapp/pom.xml b/appcontainerapp/pom.xml index d3468819..0e71f7ea 100644 --- a/appcontainerapp/pom.xml +++ b/appcontainerapp/pom.xml @@ -15,9 +15,8 @@ https://github.com/rtdi/AppContainer - 3.1.0 - 2.2.7 - 10.0.13 + 3.1.8 + 2.2.22 @@ -29,7 +28,7 @@ github - https://maven.pkg.github.com/rtdi/* + https://maven.pkg.github.com/rtdi true @@ -53,19 +52,24 @@ src/main/webapp + + org.codehaus.mojo + versions-maven-plugin + 2.17.1 + jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 provided io.rtdi.appcontainer JDBCoData - 0.2.1 + 0.2.2 org.apache.logging.log4j @@ -78,9 +82,9 @@ ${log4j.version} - org.slf4j - slf4j-nop - 2.0.6 + org.slf4j + slf4j-nop + 2.1.0-alpha1 org.glassfish.jersey.containers @@ -100,7 +104,7 @@ org.glassfish.jaxb jaxb-runtime - 4.0.1 + 4.0.5 org.glassfish.jersey.media @@ -113,9 +117,9 @@ ${jersey} - javax.xml.bind - jaxb-api - 2.4.0-b180830.0359 + javax.xml.bind + jaxb-api + 2.4.0-b180830.0359 org.junit.jupiter @@ -151,9 +155,9 @@ ${swagger} - org.apache.httpcomponents.client5 - httpclient5 - 5.2.1 + org.apache.httpcomponents.client5 + httpclient5 + 5.4-beta1 test @@ -171,6 +175,11 @@ dbactivationhana ${revision} + + io.rtdi.appcontainer + dbactivationabap + ${revision} + io.rtdi.appcontainer dbactivationsqlserver @@ -189,7 +198,7 @@ org.eclipse.jgit org.eclipse.jgit - 6.4.0.202211300538-r + 6.10.0.202406032230-r io.rtdi.appcontainer @@ -206,7 +215,13 @@ net.snowflake snowflake-jdbc - 3.13.29 + 3.19.0 + test + + + io.rtdi.appcontainer + abaprealm + ${appcontainerrealm} test @@ -218,7 +233,7 @@ com.sap.cloud.db.jdbc ngdbc - 2.15.10 + 2.21.11 test @@ -230,7 +245,7 @@ com.microsoft.sqlserver mssql-jdbc - 11.2.2.jre17 + 12.8.1.jre11 test @@ -242,7 +257,7 @@ com.mysql mysql-connector-j - 8.0.31 + 9.0.0 test @@ -254,14 +269,14 @@ org.postgresql postgresql - 42.5.1 + 42.7.4 test - org.apache.tomcat.embed - tomcat-embed-core - ${tomcatversion} - test + org.apache.tomcat.embed + tomcat-embed-core + ${tomcatversion} + test diff --git a/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRead.java b/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRead.java index 47683c18..a96d4c2e 100644 --- a/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRead.java +++ b/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRead.java @@ -11,10 +11,12 @@ import io.rtdi.appcontainer.databaseloginrealm.IDatabaseLoginPrincipal; import io.rtdi.appcontainer.db.rest.entity.SQLQueryResult; import io.rtdi.appcontainer.dbactivationbase.AppContainerSQLException; +import io.rtdi.appcontainer.plugins.database.IDatabaseProvider; import io.rtdi.appcontainer.rest.RestService; import io.rtdi.appcontainer.rest.entity.CustomSuccessMessage; import io.rtdi.appcontainer.rest.entity.ErrorMessage; import io.rtdi.appcontainer.servlets.DatabaseServlet; +import io.rtdi.appcontainer.utils.DatabaseProvider; import io.rtdi.appcontainer.utils.Util; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -114,8 +116,8 @@ public Response readTable( if (limit < 1) { limit = 1000; } - StringBuffer sql = new StringBuffer(); - sql.append("select top ").append(limit).append(" "); + StringBuilder sql = new StringBuilder(); + sql.append("select "); if (select != null) { String[] projections = select.split("\\,"); boolean first = true; @@ -141,7 +143,8 @@ public Response readTable( if (where != null && where.length() != 0) { sql.append("where ").append(where); } - + IDatabaseProvider provider = DatabaseProvider.getDatabaseProvider(servletContext, dbprincipal.getDriver()); + provider.addLimitClause(sql, limit, null); try (PreparedStatement stmt = conn.prepareStatement(sql.toString());) { SQLQueryResult result = new SQLQueryResult(sql.toString()); try (ResultSet rs = stmt.executeQuery(); ) { diff --git a/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRecordLookup.java b/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRecordLookup.java index 248f7f08..27d17a78 100644 --- a/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRecordLookup.java +++ b/appcontainerapp/src/main/java/io/rtdi/appcontainer/db/rest/DatabaseRecordLookup.java @@ -11,10 +11,12 @@ import io.rtdi.appcontainer.databaseloginrealm.IDatabaseLoginPrincipal; import io.rtdi.appcontainer.db.rest.entity.SQLResultSet; import io.rtdi.appcontainer.dbactivationbase.AppContainerSQLException; +import io.rtdi.appcontainer.plugins.database.IDatabaseProvider; import io.rtdi.appcontainer.rest.RestService; import io.rtdi.appcontainer.rest.entity.CustomSuccessMessage; import io.rtdi.appcontainer.rest.entity.ErrorMessage; import io.rtdi.appcontainer.servlets.DatabaseServlet; +import io.rtdi.appcontainer.utils.DatabaseProvider; import io.rtdi.appcontainer.utils.Util; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -105,8 +107,8 @@ public Response lookupRecord( try (Connection conn = dbprincipal.getConnection();) { String schema = Util.decodeURIfull(schemaraw); schema = Util.getSchema(schema, request); - StringBuffer sql = new StringBuffer(); - sql.append("select top 1 "); + StringBuilder sql = new StringBuilder(); + sql.append("select "); if (select != null) { String[] projections = select.split("\\,"); boolean first = true; @@ -132,7 +134,8 @@ public Response lookupRecord( if (where != null && where.length() != 0) { sql.append("where ").append(where); } - + IDatabaseProvider provider = DatabaseProvider.getDatabaseProvider(servletContext, dbprincipal.getDriver()); + sql = provider.addLimitClause(sql, 1, null); try (PreparedStatement stmt = conn.prepareStatement(sql.toString());) { try (ResultSet rs = stmt.executeQuery(); ) { SQLResultSet rootnode = new SQLResultSet(rs, sql.toString()); diff --git a/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/Line.js b/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/Line.js index bc75f769..aee7b652 100644 --- a/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/Line.js +++ b/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/Line.js @@ -4,8 +4,8 @@ sap.ui.define([ return Graphics.extend("ui5libs.amcharts.base.Line", { metadata: { properties: { - points : {type: "ui5libs.amcharts.IPoint[]"}, - segments : {type: "ui5libs.amcharts.IPoint[]"}, + points : {type: "ui5libs.amcharts.base.IPoint[]"}, + segments : {type: "ui5libs.amcharts.base.IPoint[]"}, }, }, _buildSettings : function() { diff --git a/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/PercentSeries.js b/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/PercentSeries.js index a0b594d6..60bdef08 100644 --- a/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/PercentSeries.js +++ b/appcontainerapp/src/main/webapp/ui5libs/amcharts/base/PercentSeries.js @@ -17,6 +17,9 @@ sap.ui.define([ }, aggregations: { colors: {type: "ui5libs.amcharts.base.ColorSet", multiple: false }, + labels: {type: "ui5libs.amcharts.base.RadialLabel", multiple: false }, + ticks: {type: "ui5libs.amcharts.base.Tick", multiple: false }, + slices: {type: "ui5libs.amcharts.base.Tick", multiple: false }, } }, _buildSettings : function() { @@ -34,5 +37,16 @@ sap.ui.define([ getDefaultTemplate : function() { return this.container.slices.template; }, + applyTemplates : function() { + Series.prototype.applyTemplates.apply(this); + var labels = this.getLabels(); + if (labels) { + this.container.labels.template.setAll(labels._buildSettings()); + } + var ticks = this.getTicks(); + if (ticks) { + this.container.ticks.template.setAll(ticks._buildSettings()); + } + }, }); }); \ No newline at end of file diff --git a/dbactivationabap/pom.xml b/dbactivationabap/pom.xml new file mode 100644 index 00000000..4ff2993d --- /dev/null +++ b/dbactivationabap/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + io.rtdi.appcontainer + appcontainerbase + ${revision} + + dbactivationabap + + + + + + + io.rtdi.appcontainer + dbactivationbase + ${revision} + + + \ No newline at end of file diff --git a/dbactivationabap/src/main/java/META-INF/MANIFEST.MF b/dbactivationabap/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 00000000..254272e1 --- /dev/null +++ b/dbactivationabap/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/dbactivationabap/src/main/java/META-INF/services/io.rtdi.appcontainer.plugins.database.IDatabaseProvider b/dbactivationabap/src/main/java/META-INF/services/io.rtdi.appcontainer.plugins.database.IDatabaseProvider new file mode 100644 index 00000000..341070ea --- /dev/null +++ b/dbactivationabap/src/main/java/META-INF/services/io.rtdi.appcontainer.plugins.database.IDatabaseProvider @@ -0,0 +1 @@ +io.rtdi.appcontainer.plugins.databaseabap.ABAPProvider \ No newline at end of file diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapActivationServiceDirectory.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapActivationServiceDirectory.java new file mode 100644 index 00000000..6c6d1da1 --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapActivationServiceDirectory.java @@ -0,0 +1,18 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import java.nio.file.Path; + +import io.rtdi.appcontainer.plugins.database.ActivationServiceDirectory; + +public class AbapActivationServiceDirectory extends ActivationServiceDirectory { + + protected AbapActivationServiceDirectory(Path rootpath) { + super(rootpath); + } + + @Override + protected void addServices() { + } + + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapCatalogService.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapCatalogService.java new file mode 100644 index 00000000..36f0720a --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapCatalogService.java @@ -0,0 +1,79 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import io.rtdi.appcontainer.dbactivationbase.AppContainerSQLException; +import io.rtdi.appcontainer.plugins.database.DatabaseObjectTree; +import io.rtdi.appcontainer.plugins.database.ICatalogService; +import io.rtdi.appcontainer.plugins.database.ObjectType; +import io.rtdi.appcontainer.plugins.database.SelectSource; + +public class AbapCatalogService implements ICatalogService { + + @Override + public ObjectType objectExists(Connection conn, String schema, String name) throws SQLException { + ResultSet rs = conn.getMetaData().getTables(null, null, name, new String[] {"TABLE", "VIEW"}); + if (rs.next()) { + return ObjectType.valueOf(rs.getString(4)); + } else { + return null; + } + } + + @Override + public boolean columnExists(Connection conn, String schema, String name, String columnname) throws SQLException { + ResultSet rs = conn.getMetaData().getColumns(null, null, name, columnname); + return rs.next(); + } + + private String getDDL(Connection conn, String schema, String name, String type) throws AppContainerSQLException { + return null; + } + + @Override + public String getTableDDL(Connection conn, String schema, String name) throws SQLException { + return getDDL(conn, schema, name, "TABLE"); + } + + @Override + public String getViewDDL(Connection conn, String schema, String name) throws SQLException { + return getDDL(conn, schema, name, "VIEW"); + } + + @Override + public String getFunctionDDL(Connection conn, String schema, String name) throws SQLException { + return getDDL(conn, schema, name, "FUNCTION"); + } + + @Override + public String getProcedureDDL(Connection conn, String schema, String name) throws SQLException { + return getDDL(conn, schema, name, "PRODEDURE"); + } + + @Override + public DatabaseObjectTree getDependencies(Connection conn, String schema, String name) throws SQLException { + return null; + } + + @Override + public DatabaseObjectTree getImpact(Connection conn, String schema, String name) throws SQLException { + return null; + } + + @Override + public List getAllSelectSources(Connection conn, boolean showinternal) throws SQLException { + List res = new ArrayList<>(); + ResultSet rs = conn.getMetaData().getTables(null, null, null, new String[] {"TABLE", "VIEW"}); + while (rs.next()) { + res.add(new SelectSource(rs.getString(1), rs.getString(2), ObjectType.valueOfOrNull(rs.getString(3)), + rs.getString(1), rs.getString(2), ObjectType.valueOfOrNull(rs.getString(3)), + null)); + } + return res; + } + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapDataTypes.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapDataTypes.java new file mode 100644 index 00000000..03a9017f --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapDataTypes.java @@ -0,0 +1,61 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import io.rtdi.appcontainer.dbactivationbase.AppContainerSQLException; + +public class AbapDataTypes { + + public static String getDataTypeString(String datatypename, Integer length, Integer scale) throws AppContainerSQLException { + return getDataTypeString(datatypename, length, length, scale); + } + + public static String getDataTypeString(String datatypename, Integer length, Integer precision, Integer scale) throws AppContainerSQLException { + switch (datatypename) { + case "TINYINT": + case "SMALLINT": + case "INTEGER": + case "BIGINT": + case "REAL": + case "DOUBLE": + case "DATE": + case "TIME": + case "TIMESTAMP": + case "CLOB": + case "BLOB": + case "NCLOB": + case "SMALLDECIMAL": + case "TEXT": + case "BINTEXT": + case "SECONDDATE": + case "ST_POINT": + case "ST_GEOMETRY": + case "BOOLEAN": + return datatypename; + case "CHAR": + case "VARCHAR": + case "BINARY": + case "VARBINARY": + case "NCHAR": + case "NVARCHAR": + case "ALPHANUM": + case "SHORTTEXT": + if (length == null || length.intValue() == 0) { + throw new AppContainerSQLException("The datatype needs a length information", datatypename, null); + } + return datatypename + " (" + String.valueOf(length) + ")"; + case "DECIMAL": + if (precision == null || precision.intValue() == 0) { + if (length == null || length.intValue() == 0) { + throw new AppContainerSQLException("The datatype needs a length/precision information", datatypename, null); + } + precision = length; + } + if (scale == null) { + scale = 0; + } + return datatypename + " (" + String.valueOf(precision) + "," + String.valueOf(scale) + ")"; + default: + throw new AppContainerSQLException("The datatype is not known", datatypename, null); + } + } + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProcedureMetadata.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProcedureMetadata.java new file mode 100644 index 00000000..e2e2efa0 --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProcedureMetadata.java @@ -0,0 +1,7 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import io.rtdi.appcontainer.plugins.database.entity.ProcedureMetadata; + +public class AbapProcedureMetadata extends ProcedureMetadata { + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProvider.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProvider.java new file mode 100644 index 00000000..fc5a01e0 --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapProvider.java @@ -0,0 +1,132 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import java.nio.file.Path; +import java.sql.SQLException; + +import org.antlr.v4.runtime.CodePointCharStream; + +import io.rtdi.appcontainer.db.rest.entity.sql.SqlStatement; +import io.rtdi.appcontainer.dbactivationbase.JDBCDataTypeConversion; +import io.rtdi.appcontainer.plugins.activation.ActivationResult; +import io.rtdi.appcontainer.plugins.activation.SQLParserContext; +import io.rtdi.appcontainer.plugins.database.ActivationServiceDirectory; +import io.rtdi.appcontainer.plugins.database.ICatalogService; +import io.rtdi.appcontainer.plugins.database.IDatabaseProvider; +import io.rtdi.appcontainer.plugins.database.IStoredProcedure; + +public class AbapProvider implements IDatabaseProvider { + public static final String JDBC_DRIVER = "io.rtdi.jdbcabap.AbapDriver"; + private static ActivationServiceDirectory activationservice; + private static ICatalogService catalogservice; + private static IStoredProcedure procedureservice; + private static JDBCDataTypeConversion conv; + + public AbapProvider() { + conv = new JDBCDataTypeConversion(); + } + + @Override + public ActivationServiceDirectory getActivationServices(Path rootpath) { + if (activationservice == null) { + activationservice = new AbapActivationServiceDirectory(rootpath); + } + return activationservice; + } + + @Override + public ICatalogService getCatalogService() { + if (catalogservice == null) { + catalogservice = new AbapCatalogService(); + } + return catalogservice; + } + + public IStoredProcedure getProcedureService() { + if (procedureservice == null) { + procedureservice = new AbapStoredProcedure(); + } + return procedureservice; + } + + @Override + public String getJDBCDriverName() { + return JDBC_DRIVER; + } + + @Override + public JDBCDataTypeConversion getConversionClass() { + return conv; + } + + @Override + public ActivationResult activateSQL(CodePointCharStream reader, SQLParserContext ctx) throws SQLException { + return null; + } + + @Override + public String createSql(SqlStatement sql) throws SQLException { + /* + * The SQLApp produces a Json in the form of + * + *
+		 * - with
+		 *   - alias
+		 *   - subquery
+		 * - subquery
+		 *   - projections
+		 *     - value
+		 *     - alias
+		 *     - order
+		 *   - from
+		 *     - jointype
+		 *     - value
+		 *     - on
+		 *       - left
+		 *       - right
+		 *   - where
+		 *     - left
+		 *     - op
+		 *     - right
+		 * - limit
+		 * 
+ * + * Note that this is the UI tree, so it will have empty elements. + * + */ + String sqltext = sql.toString(0, this); + if (sql.getOffset() != null) { + sqltext += " OFFSET " + sql.getOffset(); + } + if (sql.getLimit() != null) { + sqltext += " LIMIT " + sql.getLimit(); + } + return sqltext; + } + + @Override + public String[] getAggregationFunctions() { + return null; + } + + @Override + public boolean isAggregationExpression(String text) { + return false; + } + + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapStoredProcedure.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapStoredProcedure.java new file mode 100644 index 00000000..07cae6da --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/AbapStoredProcedure.java @@ -0,0 +1,304 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.JDBCType; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.benmanes.caffeine.cache.Cache; + +import io.rtdi.appcontainer.dbactivationbase.AppContainerSQLException; +import io.rtdi.appcontainer.dbactivationbase.JDBCDataTypeConversion; +import io.rtdi.appcontainer.plugins.database.IDatabaseProvider; +import io.rtdi.appcontainer.plugins.database.IStoredProcedure; +import io.rtdi.appcontainer.plugins.database.entity.ProcedureMetadata; +import io.rtdi.appcontainer.plugins.database.entity.ProcedureParameter; +import io.rtdi.appcontainer.plugins.database.entity.ProcedureParameterInOutType; + +public class AbapStoredProcedure implements IStoredProcedure { + + private static JDBCDataTypeConversion conv = new JDBCDataTypeConversion(); + + + /** + * There are multiple cases to consider: + * Input + * a) Scalar inputs: create procedure (in name varchar(10),... + * b) Anonymous Table Type parameters: create procedure (in namelist table (name varchar(50), type varchar(10)),... + * c) Table Type: create procedure (in names user1.type1,... + * d) Array input + * Output + * a) Scalar output: create procedure (in name varchar(10),... + * b) Table Resultset: begin select now() from dummy; end; + * c) Output parameter (anonymous): create procedure (out namelist table (name varchar(50), type varchar(10)),... + * d) Output parameter with table type: create procedure (out names user1.type1,... + * e) Array output + * InOut + * a) Scalar in/out: create procedure (inout name varchar(10),... + * b) In/Output parameter (anonymous): create procedure (out namelist table (name varchar(50), type varchar(10)),... + * c) In/Output parameter with table type: create procedure (out names user1.type1,... + * + * @param conn + * @param schema + * @param procedurename + * @param data + * @param cache + * @return the Json payload + * @throws AppContainerSQLException + * + */ + @Override + public ObjectNode callProcedure( + Connection conn, + String schema, + String procedurename, + JsonNode data, + Cache cache, IDatabaseProvider provider) throws AppContainerSQLException { + String cachekey = schema + "." + procedurename; + ProcedureMetadata metadata = cache.getIfPresent(cachekey); + if (metadata == null) { + metadata = new AbapProcedureMetadata(); + // Output parameters + try (ResultSet rs = conn.getMetaData().getProcedureColumns(null, schema, procedurename, null); ) { + while (rs.next()) { + short inouttype = rs.getShort(5); + JDBCType dt = JDBCType.valueOf(rs.getInt(6)); + ProcedureParameterInOutType io = ProcedureParameterInOutType.value(inouttype); + ProcedureParameter p = new ProcedureParameter(rs.getInt(18), rs.getString(4), dt, rs.getString(7), io); + metadata.addParameter(p); + } + cache.put(cachekey, metadata); + } catch (SQLException e) { + throw AppContainerSQLException.cloneFrom(e, null, "reading stored procedure metadata threw an error"); + } + } + + /* + * The call statement is built based on the input json, because if parameters are missing, they must get the default values from + * the procedure definition. + */ + StringBuffer sql = new StringBuffer(); + sql.append("{ call \"").append(schema).append("\".\"").append(procedurename).append("\"("); + Iterator iter = data.fieldNames(); + boolean first = true; + while (iter.hasNext()) { + String fieldname = iter.next(); + if (first) { + first = false; + } else { + sql.append(", "); + } + sql.append('"').append(fieldname).append("\" => "); + if (data.get(fieldname).getNodeType() == JsonNodeType.ARRAY) { + /* + * Array nodes are passed in via temporary tables + */ + ProcedureParameter p = metadata.getParameter(fieldname); + if (p == null) { + throw new AppContainerSQLException("The procedure does not have a parameter \"" + fieldname + "\"", null, null); + } else if (!(p instanceof TableParameterMetadata)) { + throw new AppContainerSQLException("The input for the parameter \"" + fieldname + "\" is not a table, hence no Json array is allowed as input", null, null); + } else { + String tablename = create_temporary_table(conn, schema, procedurename, fieldname, (ArrayNode) data.get(fieldname), (TableParameterMetadata) p); + sql.append(tablename); + } + } else { + sql.append("?"); + } + } + + /* + * Next add all output parameters with name => ? + */ + for (ProcedureParameter p : metadata.getOutputParameters()) { + if (first) { + first = false; + } else { + sql.append(", "); + } + sql.append("\"").append(p.getParametername()).append("\" => ?"); + } + + + sql.append(") }"); + + try (CallableStatement stmt = conn.prepareCall(sql.toString());) { + int counter = 1; + if (data != null) { // procedures without input parameters are fine + iter = data.fieldNames(); + while (iter.hasNext()) { + String fieldname = iter.next(); + if (data.get(fieldname).getNodeType() == JsonNodeType.ARRAY) { + // nothing to do, a table with the data has been created already + } else { + stmt.setObject(counter++, conv.convertJsonNodeJDBC(data.get(fieldname), metadata.getParameter(fieldname).getJDBCDataType() )); + } + } + } + /* + * Scalar and table output parameters are prepared + */ + List tableout = new ArrayList<>(); + for ( ProcedureParameter outputparameter : metadata.getOutputParameters()) { + String datatype = outputparameter.getDatatypename(); + if (datatype != null) { + switch (datatype) { + case "TABLE_TYPE": + // not used, data is returned as result set in Hana + tableout.add(outputparameter); + break; + default: + stmt.registerOutParameter(counter++, outputparameter.getJDBCDataType().getVendorTypeNumber()); + } + } + } + boolean hasrs = stmt.execute(); + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode rootnode = objectMapper.createObjectNode(); + /* + * Table Output Parameters and anonymous outputs are read here. + * Adds one arraynode per table with 0..n rows each + */ + if (hasrs) { + int rscount = 0; + while (hasrs) { + ArrayNode arraynode = objectMapper.createArrayNode(); + try (ResultSet rs = stmt.getResultSet();) { + while (rs.next()) { + ObjectNode rsnode = objectMapper.createObjectNode(); + arraynode.add(rsnode); + for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { + rsnode.set(rs.getMetaData().getColumnName(i), + conv.convertJDBCToJsonNode( + rs.getObject(i), + JDBCType.valueOf(rs.getMetaData().getColumnType(i)))); + } + } + String outname = null; + if (rscount < tableout.size()) { + outname = tableout.get(rscount).getParametername(); + } else { + outname = "OUT" + String.valueOf(rscount + 1 - tableout.size()); + } + rootnode.set(outname, arraynode); + rscount++; + } + if (stmt.isClosed()) { + hasrs = false; + } else { + hasrs = stmt.getMoreResults(); + } + } + } + /* + * Scalar output parameters are read + */ + for ( ProcedureParameter outputparameter : metadata.getOutputParameters()) { + String datatype = outputparameter.getDatatypename(); + if (datatype != null) { + switch (datatype) { + case "TABLE_TYPE": + // ignore + break; + default: + rootnode.set(outputparameter.getParametername(), + conv.convertJDBCToJsonNode( + stmt.getObject(outputparameter.getParametername()), + outputparameter.getJDBCDataType())); + } + } + } + return rootnode; + } catch (SQLException e) { + throw AppContainerSQLException.cloneFrom(e, sql.toString(), "Executed SQL statement threw an error"); + } + } + + private static String create_temporary_table(Connection conn, String schema, String procedurename, + String fieldname, ArrayNode data, TableParameterMetadata tablemetadata) throws AppContainerSQLException { + String tablename = "#" + fieldname; + try (PreparedStatement stmt = conn.prepareStatement("drop table \"" + tablename + "\""); ) { + stmt.execute(); + } catch (SQLException e) { + System.out.println("Exception " + e.getMessage()); + } + if (tablemetadata.getCreateTableSQL() == null) { + StringBuffer sql = new StringBuffer(); + StringBuffer columnlist = new StringBuffer(); + StringBuffer parameterlist = new StringBuffer(); + List columnindex = new ArrayList<>(); + sql.append("create local temporary table \"").append(tablename).append("\" ("); + String selectsql = "SELECT column_name, data_type_name, length, scale FROM PROCEDURE_PARAMETER_COLUMNS " + + "where schema_name = ? and procedure_name = ? and parameter_name = ? " + + "order by position"; + try (PreparedStatement stmt = conn.prepareStatement(selectsql); ) { + stmt.setString(1, schema); + stmt.setString(2, procedurename); + stmt.setString(3, fieldname); + try (ResultSet rs = stmt.executeQuery(); ) { + boolean first = true; + while (rs.next()) { + if (first) { + first = false; + } else { + sql.append(", "); + columnlist.append(", "); + parameterlist.append(", "); + } + String columnname = rs.getString(1); + sql.append('"').append(columnname).append("\" "); + sql.append(AbapDataTypes.getDataTypeString(rs.getString(2), rs.getInt(3), rs.getInt(4))); + columnlist.append('"').append(columnname).append("\" "); + parameterlist.append('?'); + columnindex.add(columnname); + } + } + } catch (SQLException e) { + throw AppContainerSQLException.cloneFrom(e, selectsql, "Executed SQL statement threw an error"); + } + sql.append(')'); + tablemetadata.setCreateTableSQL(sql.toString()); + + String insertsql = "insert into \"" + tablename + "\" (" + columnlist + ") values (" + parameterlist + ")"; + tablemetadata.setInsertSQL(insertsql); + + tablemetadata.setColumnIndex(columnindex); + } + try (PreparedStatement stmt = conn.prepareStatement(tablemetadata.getCreateTableSQL()); ) { + stmt.execute(); + } catch (SQLException e) { + throw AppContainerSQLException.cloneFrom(e, tablemetadata.getCreateTableSQL(), "Executed SQL statement threw an error"); + } + + // Insert data + try (PreparedStatement stmt = conn.prepareStatement(tablemetadata.getInsertSQL()); ) { + Iterator iter = data.elements(); + while (iter.hasNext()) { + JsonNode row = iter.next(); + if (row.getNodeType() == JsonNodeType.OBJECT) { + for (int i=0; i < tablemetadata.getColumnIndex().size(); i++) { + JsonNode n = row.get(tablemetadata.getColumnIndex().get(i)); + stmt.setObject(i+1, (n==null?null:conv.convertJsonNodeJDBC(n, JDBCType.valueOf(stmt.getMetaData().getColumnType(i+1))))); + } + stmt.addBatch(); + } + } + stmt.executeBatch(); + } catch (SQLException e) { + throw AppContainerSQLException.cloneFrom(e, tablemetadata.getInsertSQL(), "Executed SQL statement threw an error"); + } + return tablename; + } + +} diff --git a/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/TableParameterMetadata.java b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/TableParameterMetadata.java new file mode 100644 index 00000000..6961db33 --- /dev/null +++ b/dbactivationabap/src/main/java/io/rtdi/appcontainer/plugins/databaseabap/TableParameterMetadata.java @@ -0,0 +1,43 @@ +package io.rtdi.appcontainer.plugins.databaseabap; + +import java.sql.JDBCType; +import java.util.List; + +import io.rtdi.appcontainer.plugins.database.entity.ProcedureParameter; +import io.rtdi.appcontainer.plugins.database.entity.ProcedureParameterInOutType; + +public class TableParameterMetadata extends ProcedureParameter { + + private String createtablestring; + private String insertsqlstring; + private List columnindex; + + public TableParameterMetadata(int index, String parametername, String datatypename, ProcedureParameterInOutType inouttype) { + super(index, parametername, JDBCType.STRUCT, datatypename, inouttype); + } + + public void setCreateTableSQL(String string) { + this.createtablestring = string; + } + + public List getColumnIndex() { + return columnindex; + } + + public void setColumnIndex(List columnindex) { + this.columnindex = columnindex; + } + + public String getInsertSQL() { + return insertsqlstring; + } + + public void setInsertSQL(String insertsql) { + this.insertsqlstring = insertsql; + } + + public String getCreateTableSQL() { + return createtablestring; + } + +} \ No newline at end of file diff --git a/dbactivationbase/.settings/org.eclipse.wst.common.project.facet.core.xml b/dbactivationbase/.settings/org.eclipse.wst.common.project.facet.core.xml index 9a6b2413..9184552e 100644 --- a/dbactivationbase/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/dbactivationbase/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -1,6 +1,5 @@ - diff --git a/dbactivationbase/pom.xml b/dbactivationbase/pom.xml index 24860c75..d90265bf 100644 --- a/dbactivationbase/pom.xml +++ b/dbactivationbase/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.12.0 + ${antlr.version} @@ -20,18 +20,23 @@ + + org.codehaus.mojo + versions-maven-plugin + 2.17.1 + io.swagger.core.v3 swagger-annotations - 2.2.9 + 2.2.23 org.glassfish.jersey.media jersey-media-json-jackson - 3.1.1 + 4.0.0-M1 com.univocity @@ -41,12 +46,12 @@ com.github.ben-manes.caffeine caffeine - 3.1.6 + 3.1.8 org.graalvm.js js - 22.3.2 + 23.0.5 io.rtdi.appcontainer @@ -57,12 +62,12 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.14.1 + 2.17.2 com.fasterxml.jackson.module jackson-module-jakarta-xmlbind-annotations - 2.14.1 + 2.17.2 org.junit.jupiter @@ -85,7 +90,7 @@ org.antlr antlr4-runtime - 4.12.0 + ${antlr.version} \ No newline at end of file diff --git a/dbactivationbase/src/main/java/io/rtdi/appcontainer/dbunittest/JavaScriptExecutor.java b/dbactivationbase/src/main/java/io/rtdi/appcontainer/dbunittest/JavaScriptExecutor.java index 20976f05..100c01ed 100644 --- a/dbactivationbase/src/main/java/io/rtdi/appcontainer/dbunittest/JavaScriptExecutor.java +++ b/dbactivationbase/src/main/java/io/rtdi/appcontainer/dbunittest/JavaScriptExecutor.java @@ -14,6 +14,7 @@ import org.graalvm.polyglot.Context; import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.io.FileSystem; +import org.graalvm.polyglot.io.IOAccess; import io.rtdi.appcontainer.plugins.activation.ActivationResult; import io.rtdi.appcontainer.plugins.activation.ActivationSuccess; @@ -40,7 +41,7 @@ public ActivationResult activate(File file, Connection conn, GlobalSchemaMapping ActivationResult result = new ActivationResult(file.toPath(), "Executing test"); FileSystem fs = new RestrictedFileSystem(rootpath); - try (Context context = Context.newBuilder("js").logHandler(log).out(out).err(err).fileSystem(fs).allowIO(true).build(); ) { + try (Context context = Context.newBuilder("js").logHandler(log).out(out).err(err).allowIO(IOAccess.newBuilder().fileSystem(fs).build()).allowIO(IOAccess.ALL).build(); ) { Commands commands = new Commands(conn, rootpath, provider); context.getBindings("js").putMember("db", commands); context.eval("js", code); @@ -62,7 +63,7 @@ public ActivationResult activate(File file, Connection conn, GlobalSchemaMapping } } if (result.getChildren() == null || result.getChildren().size() == 0) { - // If neiter sdtout nor stderr has an output add a dummy success message + // If neither stdout nor stderr has an output add a dummy success message result.addResult("No (error) messages produced", null, ActivationSuccess.SUCCESS); } return result; diff --git a/dbactivationbase/src/main/java/io/rtdi/appcontainer/plugins/database/IDatabaseProvider.java b/dbactivationbase/src/main/java/io/rtdi/appcontainer/plugins/database/IDatabaseProvider.java index 3617602b..db360547 100644 --- a/dbactivationbase/src/main/java/io/rtdi/appcontainer/plugins/database/IDatabaseProvider.java +++ b/dbactivationbase/src/main/java/io/rtdi/appcontainer/plugins/database/IDatabaseProvider.java @@ -21,5 +21,7 @@ public interface IDatabaseProvider { String createSql(SqlStatement sql) throws SQLException; String[] getAggregationFunctions(); boolean isAggregationExpression(String text); + String addLimitClause(String sql, Integer limit, Integer offset); + StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset); } diff --git a/dbactivationbase/src/test/java/io/rtdi/appcontainer/dbactivationbase/TestProvider.java b/dbactivationbase/src/test/java/io/rtdi/appcontainer/dbactivationbase/TestProvider.java index 75419d29..b2820258 100644 --- a/dbactivationbase/src/test/java/io/rtdi/appcontainer/dbactivationbase/TestProvider.java +++ b/dbactivationbase/src/test/java/io/rtdi/appcontainer/dbactivationbase/TestProvider.java @@ -79,4 +79,20 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + } diff --git a/dbactivationhana/pom.xml b/dbactivationhana/pom.xml index a343ec67..7c8f9a5f 100644 --- a/dbactivationhana/pom.xml +++ b/dbactivationhana/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} src/main/java src/main/java diff --git a/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/HanaProvider.java b/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/HanaProvider.java index ce4fe651..93d517c3 100644 --- a/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/HanaProvider.java +++ b/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/HanaProvider.java @@ -146,4 +146,20 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + } diff --git a/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/activation/antlr/SqlscriptsBaseListener.java b/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/activation/antlr/SqlscriptsBaseListener.java index 31b9838d..afcbd7b6 100644 --- a/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/activation/antlr/SqlscriptsBaseListener.java +++ b/dbactivationhana/src/main/java/io/rtdi/appcontainer/plugins/databasehana/activation/antlr/SqlscriptsBaseListener.java @@ -10,7 +10,6 @@ * which can be extended to create a listener which only needs to handle a subset * of the available methods. */ -@SuppressWarnings("CheckReturnValue") public class SqlscriptsBaseListener implements SqlscriptsListener { /** * {@inheritDoc} diff --git a/dbactivationmysql/pom.xml b/dbactivationmysql/pom.xml index 087dde4c..c37f1645 100644 --- a/dbactivationmysql/pom.xml +++ b/dbactivationmysql/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} src/main/java src/main/java diff --git a/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/MysqlProvider.java b/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/MysqlProvider.java index 1abe8294..c7a387fa 100644 --- a/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/MysqlProvider.java +++ b/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/MysqlProvider.java @@ -116,4 +116,20 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + } diff --git a/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/activation/antlr/SqlscriptsBaseListener.java b/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/activation/antlr/SqlscriptsBaseListener.java index 17f4bfdb..62dd0630 100644 --- a/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/activation/antlr/SqlscriptsBaseListener.java +++ b/dbactivationmysql/src/main/java/io/rtdi/appcontainer/plugins/databasemysql/activation/antlr/SqlscriptsBaseListener.java @@ -10,7 +10,6 @@ * which can be extended to create a listener which only needs to handle a subset * of the available methods. */ -@SuppressWarnings("CheckReturnValue") public class SqlscriptsBaseListener implements SqlscriptsListener { /** * {@inheritDoc} diff --git a/dbactivationpostgresql/pom.xml b/dbactivationpostgresql/pom.xml index 6087b602..f3ae0bd4 100644 --- a/dbactivationpostgresql/pom.xml +++ b/dbactivationpostgresql/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} src/main/java src/main/java diff --git a/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/PostgresqlProvider.java b/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/PostgresqlProvider.java index 87a20399..0f4b3431 100644 --- a/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/PostgresqlProvider.java +++ b/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/PostgresqlProvider.java @@ -116,4 +116,20 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + } diff --git a/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/activation/antlr/SqlscriptsBaseListener.java b/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/activation/antlr/SqlscriptsBaseListener.java index 39917e9e..161408dc 100644 --- a/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/activation/antlr/SqlscriptsBaseListener.java +++ b/dbactivationpostgresql/src/main/java/io/rtdi/appcontainer/plugins/databasepostgresql/activation/antlr/SqlscriptsBaseListener.java @@ -10,7 +10,6 @@ * which can be extended to create a listener which only needs to handle a subset * of the available methods. */ -@SuppressWarnings("CheckReturnValue") public class SqlscriptsBaseListener implements SqlscriptsListener { /** * {@inheritDoc} diff --git a/dbactivationsnowflake/pom.xml b/dbactivationsnowflake/pom.xml index 4269f2e7..1d96eccb 100644 --- a/dbactivationsnowflake/pom.xml +++ b/dbactivationsnowflake/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} src/main/java src/main/java diff --git a/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/SnowflakeProvider.java b/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/SnowflakeProvider.java index 7adb6a7c..29e8db25 100644 --- a/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/SnowflakeProvider.java +++ b/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/SnowflakeProvider.java @@ -116,4 +116,20 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + return sql + (limit != null?" limit " + String.valueOf(limit):"") + (offset != null?" offset " + String.valueOf(offset):""); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + if (limit != null) { + sql.append(" limit ").append(limit); + } + if (offset != null) { + sql.append(" offset ").append(offset); + } + return sql; + } + } diff --git a/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/activation/antlr/SqlscriptsBaseListener.java b/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/activation/antlr/SqlscriptsBaseListener.java index e5089db7..778e351a 100644 --- a/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/activation/antlr/SqlscriptsBaseListener.java +++ b/dbactivationsnowflake/src/main/java/io/rtdi/appcontainer/plugins/databasesnowflake/activation/antlr/SqlscriptsBaseListener.java @@ -10,7 +10,6 @@ * which can be extended to create a listener which only needs to handle a subset * of the available methods. */ -@SuppressWarnings("CheckReturnValue") public class SqlscriptsBaseListener implements SqlscriptsListener { /** * {@inheritDoc} diff --git a/dbactivationsqlserver/pom.xml b/dbactivationsqlserver/pom.xml index abba4d8e..0226b9fb 100644 --- a/dbactivationsqlserver/pom.xml +++ b/dbactivationsqlserver/pom.xml @@ -11,7 +11,7 @@ org.antlr antlr4-maven-plugin - 4.11.1 + ${antlr.version} src/main/java src/main/java diff --git a/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/SqlserverProvider.java b/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/SqlserverProvider.java index ef6d7320..4be6a71c 100644 --- a/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/SqlserverProvider.java +++ b/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/SqlserverProvider.java @@ -2,6 +2,7 @@ import java.nio.file.Path; import java.sql.SQLException; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.antlr.v4.runtime.CodePointCharStream; @@ -31,6 +32,8 @@ public class SqlserverProvider implements IDatabaseProvider { public static final String[] AGGREGATION_FUNCTIONS = {"AVG", "COUNT", "MAX", "MIN", "SUM"}; public static Pattern[] AGGREGATION_PATTERNS; final static String regex = ".*((%s\\s*\\(.*\\))).*"; + final String regexorderby = "\\sorder\\s*by\\s"; + final Pattern patternorderby = Pattern.compile(regex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); static { AGGREGATION_PATTERNS = new Pattern[AGGREGATION_FUNCTIONS.length]; @@ -116,4 +119,28 @@ public boolean isAggregationExpression(String text) { return false; } + @Override + public String addLimitClause(String sql, Integer limit, Integer offset) { + StringBuilder sb = new StringBuilder(sql); + return addLimitClause(sb, limit, offset).toString(); + } + + @Override + public StringBuilder addLimitClause(StringBuilder sql, Integer limit, Integer offset) { + /* + * ORDER BY ... + * OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; + */ + Matcher matcher = patternorderby.matcher(sql); + if (!matcher.find()) { + sql.append(" order by 1"); // offset/next requires an order by + } + if (offset == null) { + offset = 0; + } + sql.append(" offset ").append(offset).append(" rows "); + sql.append(" fetch next ").append(limit).append(" rows only"); + return sql; + } + } diff --git a/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/activation/antlr/SqlscriptsBaseListener.java b/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/activation/antlr/SqlscriptsBaseListener.java index e66bac50..9c1165c2 100644 --- a/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/activation/antlr/SqlscriptsBaseListener.java +++ b/dbactivationsqlserver/src/main/java/io/rtdi/appcontainer/plugins/databasesqlserver/activation/antlr/SqlscriptsBaseListener.java @@ -10,7 +10,6 @@ * which can be extended to create a listener which only needs to handle a subset * of the available methods. */ -@SuppressWarnings("CheckReturnValue") public class SqlscriptsBaseListener implements SqlscriptsListener { /** * {@inheritDoc} diff --git a/docker/Dockerfile-Abap b/docker/Dockerfile-Abap new file mode 100644 index 00000000..54a06135 --- /dev/null +++ b/docker/Dockerfile-Abap @@ -0,0 +1,15 @@ +FROM rtdi/connectorrootapp + +LABEL maintainer="info@rtdi.io" + +ADD https://github.com/rtdi/AppContainerRealm/releases/latest/download/dbrealmbase.jar ./lib +ADD https://github.com/rtdi/AppContainerRealm/releases/latest/download/abaprealm.jar ./lib +ADD https://repo1.maven.org/maven2/io/rtdi/sap/jdbcabap/0.4.0/jdbcabap-0.4.0.jar ./lib +ADD https://raw.githubusercontent.com/rtdi/AppContainer/master/docker/server-abap.xml conf/server.xml +ADD https://github.com/rtdi/AppContainer/releases/latest/download/AppContainer.war ./webapps/ + +EXPOSE 8080 +EXPOSE 8443 + + +CMD ["appcontainer.sh", "run"] diff --git a/docker/server-abap.xml b/docker/server-abap.xml new file mode 100644 index 00000000..dd47ef8e --- /dev/null +++ b/docker/server-abap.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index f4ff0115..32cff84c 100644 --- a/pom.xml +++ b/pom.xml @@ -29,11 +29,12 @@ UTF-8 - 0.11.8 - 10.0.14 - 0.9.12 - 2.19.0 - 5.8.1 + 0.11.9 + 10.1.28 + 0.9.19 + 2.23.1 + 5.11.0 + 4.13.2 appcontainerapp @@ -43,20 +44,8 @@ dbactivationsqlserver dbactivationmysql dbactivationpostgresql + dbactivationabap - - - central - https://repo1.maven.org/maven2 - - - github - https://maven.pkg.github.com/rtdi/* - - true - - - @@ -67,6 +56,11 @@ 17 + + org.codehaus.mojo + versions-maven-plugin + 2.17.1 + From 77a01fe28438e81a5d02c90f44c569c19adfb387 Mon Sep 17 00:00:00 2001 From: werner daehn Date: Mon, 13 Jan 2025 12:02:29 +0100 Subject: [PATCH 2/3] update bom --- appcontainerapp/pom.xml | 12 ++++++------ dbactivationbase/pom.xml | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/appcontainerapp/pom.xml b/appcontainerapp/pom.xml index 0e71f7ea..e39d620a 100644 --- a/appcontainerapp/pom.xml +++ b/appcontainerapp/pom.xml @@ -157,7 +157,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4-beta1 + 5.4.1 test @@ -198,7 +198,7 @@ org.eclipse.jgit org.eclipse.jgit - 6.10.0.202406032230-r + 7.1.0.202411261347-r io.rtdi.appcontainer @@ -215,7 +215,7 @@ net.snowflake snowflake-jdbc - 3.19.0 + 3.21.0 test @@ -233,7 +233,7 @@ com.sap.cloud.db.jdbc ngdbc - 2.21.11 + 2.23.7 test @@ -245,7 +245,7 @@ com.microsoft.sqlserver mssql-jdbc - 12.8.1.jre11 + 12.9.0.jre11-preview test @@ -257,7 +257,7 @@ com.mysql mysql-connector-j - 9.0.0 + 9.1.0 test diff --git a/dbactivationbase/pom.xml b/dbactivationbase/pom.xml index d90265bf..4a645f83 100644 --- a/dbactivationbase/pom.xml +++ b/dbactivationbase/pom.xml @@ -31,7 +31,7 @@ io.swagger.core.v3 swagger-annotations - 2.2.23 + 2.2.27 org.glassfish.jersey.media @@ -51,7 +51,7 @@ org.graalvm.js js - 23.0.5 + 24.1.1 io.rtdi.appcontainer @@ -62,12 +62,12 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.17.2 + 2.18.2 com.fasterxml.jackson.module jackson-module-jakarta-xmlbind-annotations - 2.17.2 + 2.18.2 org.junit.jupiter From e434be27e478cea652fb25496d99f404351a1f30 Mon Sep 17 00:00:00 2001 From: werner daehn Date: Mon, 13 Jan 2025 12:19:40 +0100 Subject: [PATCH 3/3] update --- dbactivationbase/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbactivationbase/pom.xml b/dbactivationbase/pom.xml index 4a645f83..a645c7df 100644 --- a/dbactivationbase/pom.xml +++ b/dbactivationbase/pom.xml @@ -50,7 +50,7 @@ org.graalvm.js - js + js-scriptengine 24.1.1 diff --git a/pom.xml b/pom.xml index 32cff84c..60cb2e3c 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ UTF-8 - 0.11.9 + 0.11.10 10.1.28 0.9.19 2.23.1