From f08932cba32058974458c360dd0cf146d0d625ba Mon Sep 17 00:00:00 2001 From: AnnaHariprasad5123 Date: Thu, 25 Jul 2024 11:01:06 +0530 Subject: [PATCH 1/5] feat:Improved-error-messages-for-postgres-connection --- .../com/external/plugins/PostgresPlugin.java | 32 ++++++++++++++++--- .../exceptions/PostgresErrorMessages.java | 4 +++ .../external/plugins/PostgresPluginTest.java | 9 ++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java index 003b8ddde365..5322f1c1df75 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java @@ -43,6 +43,8 @@ import org.pf4j.Extension; import org.pf4j.PluginWrapper; import org.postgresql.util.PGobject; +import org.postgresql.util.PSQLException; +import org.postgresql.util.PSQLState; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import reactor.core.publisher.Mono; @@ -668,6 +670,9 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur invalids.add( String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); } + if (StringUtils.isEmpty(endpoint.getPort())) { + invalids.add(PostgresErrorMessages.DS_MISSING_PORT_ERROR_MSG); + } } } @@ -1131,7 +1136,7 @@ private static HikariDataSource createConnectionPool( StringBuilder urlBuilder = new StringBuilder("jdbc:postgresql://"); List hosts = datasourceConfiguration.getEndpoints().stream() - .map(endpoint -> endpoint.getHost() + ":" + ObjectUtils.defaultIfNull(endpoint.getPort(), 5432L)) + .map(endpoint -> endpoint.getHost() + ":" + endpoint.getPort()) .collect(Collectors.toList()); urlBuilder.append(String.join(",", hosts)).append("/"); @@ -1260,10 +1265,27 @@ private static HikariDataSource createConnectionPool( try { datasource = new HikariDataSource(config); } catch (PoolInitializationException e) { - throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, - e.getMessage()); + Throwable cause = e.getCause(); + if (cause instanceof PSQLException) { + PSQLException psqlException = (PSQLException) cause; + String sqlState = psqlException.getSQLState(); + if (PSQLState.CONNECTION_UNABLE_TO_CONNECT.getState().equals(sqlState)) { + throw new AppsmithPluginException( + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.DS_INVALID_HOSTNAME_AND_PORT_MSG, + psqlException.getMessage()); + } else { + throw new AppsmithPluginException( + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, + cause.getMessage()); + } + } else { + throw new AppsmithPluginException( + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, + cause != null ? cause.getMessage() : e.getMessage()); + } } return datasource; diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java index 06af60d122c3..d025c60b2942 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java @@ -37,6 +37,8 @@ public class PostgresErrorMessages extends BasePluginErrorMessages { public static final String DS_MISSING_HOSTNAME_ERROR_MSG = "Missing hostname."; + public static final String DS_MISSING_PORT_ERROR_MSG = "Missing port."; + public static final String DS_INVALID_HOSTNAME_ERROR_MSG = "Host value cannot contain `/` or `:` characters. Found `%s`."; @@ -47,4 +49,6 @@ public class PostgresErrorMessages extends BasePluginErrorMessages { public static final String DS_MISSING_USERNAME_ERROR_MSG = "Missing username for authentication."; public static final String DS_MISSING_DATABASE_NAME_ERROR_MSG = "Missing database name."; + + public static final String DS_INVALID_HOSTNAME_AND_PORT_MSG = "Please check the host and port."; } diff --git a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java index 7c2b084aeb8b..17b80c878a00 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java @@ -355,6 +355,15 @@ public void itShouldValidateDatasourceWithEmptyHost() { assertEquals(Set.of("Missing hostname."), pluginExecutor.validateDatasource(dsConfig)); } + @Test + public void itShouldValidateDatasourceWithEmptyPort() { + + DatasourceConfiguration dsConfig = createDatasourceConfiguration(); + dsConfig.getEndpoints().get(0).setPort(null); + + assertEquals(Set.of("Missing port."), pluginExecutor.validateDatasource(dsConfig)); + } + @Test public void itShouldValidateDatasourceWithInvalidHostname() { From 69965b57d0b6905a133d75062b86620d20c77052 Mon Sep 17 00:00:00 2001 From: AnnaHariprasad5123 Date: Tue, 3 Sep 2024 07:45:36 +0530 Subject: [PATCH 2/5] fix-cypress-tests --- .../main/java/com/external/plugins/PostgresPlugin.java | 3 --- .../plugins/exceptions/PostgresErrorMessages.java | 2 -- .../java/com/external/plugins/PostgresPluginTest.java | 9 --------- 3 files changed, 14 deletions(-) diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java index 8c779f9c1137..265aacc7cc2d 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java @@ -689,9 +689,6 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur invalids.add( String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); } - if (StringUtils.isEmpty(endpoint.getPort())) { - invalids.add(PostgresErrorMessages.DS_MISSING_PORT_ERROR_MSG); - } } } diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java index 0c789faeeab4..8cee7970b58a 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/exceptions/PostgresErrorMessages.java @@ -37,8 +37,6 @@ public class PostgresErrorMessages extends BasePluginErrorMessages { public static final String DS_MISSING_HOSTNAME_ERROR_MSG = "Missing hostname."; - public static final String DS_MISSING_PORT_ERROR_MSG = "Missing port."; - public static final String DS_INVALID_HOSTNAME_ERROR_MSG = "Host value cannot contain `/` or `:` characters. Found `%s`."; diff --git a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java index bb9e2cca08d4..2ec1943e85a4 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java @@ -598,15 +598,6 @@ public void itShouldValidateDatasourceWithEmptyHost() { assertEquals(Set.of("Missing hostname."), pluginExecutor.validateDatasource(dsConfig)); } - @Test - public void itShouldValidateDatasourceWithEmptyPort() { - - DatasourceConfiguration dsConfig = createDatasourceConfiguration(); - dsConfig.getEndpoints().get(0).setPort(null); - - assertEquals(Set.of("Missing port."), pluginExecutor.validateDatasource(dsConfig)); - } - @Test public void itShouldValidateDatasourceWithInvalidHostname() { From 6efcd27fbbf7b1aa9ff305930211ce4d95ebf9eb Mon Sep 17 00:00:00 2001 From: AnnaHariprasad5123 Date: Fri, 6 Sep 2024 11:49:29 +0530 Subject: [PATCH 3/5] reduced-code-duplication --- .../com/external/plugins/PostgresPlugin.java | 1215 ++++++++--------- 1 file changed, 605 insertions(+), 610 deletions(-) diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java index 265aacc7cc2d..7eb85832c817 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java @@ -160,45 +160,45 @@ public static class PostgresPluginExecutor implements SmartSubstitutionInterface private final Scheduler scheduler = Schedulers.boundedElastic(); private static final String TABLES_QUERY = - "select a.attname as name,\n" - + " t1.typname as column_type,\n" - + " case when a.atthasdef then pg_get_expr(d.adbin, d.adrelid) end as default_expr,\n" - + " c.relkind as kind,\n" - + " c.relname as table_name,\n" - + " n.nspname as schema_name\n" - + "from pg_catalog.pg_attribute a\n" - + " left join pg_catalog.pg_type t1 on t1.oid = a.atttypid\n" - + " inner join pg_catalog.pg_class c on a.attrelid = c.oid\n" - + " left join pg_catalog.pg_namespace n on c.relnamespace = n.oid\n" - + " left join pg_catalog.pg_attrdef d on d.adrelid = c.oid and d.adnum = a.attnum\n" - + "where a.attnum > 0\n" - + " and not a.attisdropped\n" - + " and n.nspname not in ('information_schema', 'pg_catalog')\n" - + " and c.relkind in ('r', 'v')\n" - + "order by c.relname, a.attnum;"; + "select a.attname as name,\n" + + " t1.typname as column_type,\n" + + " case when a.atthasdef then pg_get_expr(d.adbin, d.adrelid) end as default_expr,\n" + + " c.relkind as kind,\n" + + " c.relname as table_name,\n" + + " n.nspname as schema_name\n" + + "from pg_catalog.pg_attribute a\n" + + " left join pg_catalog.pg_type t1 on t1.oid = a.atttypid\n" + + " inner join pg_catalog.pg_class c on a.attrelid = c.oid\n" + + " left join pg_catalog.pg_namespace n on c.relnamespace = n.oid\n" + + " left join pg_catalog.pg_attrdef d on d.adrelid = c.oid and d.adnum = a.attnum\n" + + "where a.attnum > 0\n" + + " and not a.attisdropped\n" + + " and n.nspname not in ('information_schema', 'pg_catalog')\n" + + " and c.relkind in ('r', 'v')\n" + + "order by c.relname, a.attnum;"; public static final String KEYS_QUERY = - "select c.conname as constraint_name,\n" - + " c.contype as constraint_type,\n" - + " sch.nspname as self_schema,\n" - + " tbl.relname as self_table,\n" - + " array_agg(col.attname order by u.attposition) as self_columns,\n" - + " f_sch.nspname as foreign_schema,\n" - + " f_tbl.relname as foreign_table,\n" - + " array_agg(f_col.attname order by f_u.attposition) as foreign_columns,\n" - + " pg_get_constraintdef(c.oid) as definition\n" - + "from pg_constraint c\n" - + " left join lateral unnest(c.conkey) with ordinality as u(attnum, attposition) on true\n" - + " left join lateral unnest(c.confkey) with ordinality as f_u(attnum, attposition)\n" - + " on f_u.attposition = u.attposition\n" - + " join pg_class tbl on tbl.oid = c.conrelid\n" - + " join pg_namespace sch on sch.oid = tbl.relnamespace\n" - + " left join pg_attribute col on (col.attrelid = tbl.oid and col.attnum = u.attnum)\n" - + " left join pg_class f_tbl on f_tbl.oid = c.confrelid\n" - + " left join pg_namespace f_sch on f_sch.oid = f_tbl.relnamespace\n" - + " left join pg_attribute f_col on (f_col.attrelid = f_tbl.oid and f_col.attnum = f_u.attnum)\n" - + "group by constraint_name, constraint_type, self_schema, self_table, definition, foreign_schema, foreign_table\n" - + "order by self_schema, self_table;"; + "select c.conname as constraint_name,\n" + + " c.contype as constraint_type,\n" + + " sch.nspname as self_schema,\n" + + " tbl.relname as self_table,\n" + + " array_agg(col.attname order by u.attposition) as self_columns,\n" + + " f_sch.nspname as foreign_schema,\n" + + " f_tbl.relname as foreign_table,\n" + + " array_agg(f_col.attname order by f_u.attposition) as foreign_columns,\n" + + " pg_get_constraintdef(c.oid) as definition\n" + + "from pg_constraint c\n" + + " left join lateral unnest(c.conkey) with ordinality as u(attnum, attposition) on true\n" + + " left join lateral unnest(c.confkey) with ordinality as f_u(attnum, attposition)\n" + + " on f_u.attposition = u.attposition\n" + + " join pg_class tbl on tbl.oid = c.conrelid\n" + + " join pg_namespace sch on sch.oid = tbl.relnamespace\n" + + " left join pg_attribute col on (col.attrelid = tbl.oid and col.attnum = u.attnum)\n" + + " left join pg_class f_tbl on f_tbl.oid = c.confrelid\n" + + " left join pg_namespace f_sch on f_sch.oid = f_tbl.relnamespace\n" + + " left join pg_attribute f_col on (f_col.attrelid = f_tbl.oid and f_col.attnum = f_u.attnum)\n" + + "group by constraint_name, constraint_type, self_schema, self_table, definition, foreign_schema, foreign_table\n" + + "order by self_schema, self_table;"; private static final int PREPARED_STATEMENT_INDEX = 0; @@ -236,18 +236,18 @@ public PostgresPluginExecutor(SharedConfig sharedConfig, ConnectionPoolConfig co */ @Override public Mono executeParameterized( - HikariDataSource connection, - ExecuteActionDTO executeActionDTO, - DatasourceConfiguration datasourceConfiguration, - ActionConfiguration actionConfiguration) { + HikariDataSource connection, + ExecuteActionDTO executeActionDTO, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration) { String query = actionConfiguration.getBody(); // Check for query parameter before performing the probably expensive fetch // connection from the pool op. if (!StringUtils.hasLength(query)) { return Mono.error(new AppsmithPluginException( - AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, - PostgresErrorMessages.MISSING_QUERY_ERROR_MSG)); + AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, + PostgresErrorMessages.MISSING_QUERY_ERROR_MSG)); } Boolean isPreparedStatement; @@ -284,13 +284,13 @@ public Mono executeParameterized( List explicitCastDataTypes = extractExplicitCasting(updatedQuery); actionConfiguration.setBody(updatedQuery); return executeCommon( - connection, - datasourceConfiguration, - actionConfiguration, - TRUE, - mustacheKeysInOrder, - executeActionDTO, - explicitCastDataTypes); + connection, + datasourceConfiguration, + actionConfiguration, + TRUE, + mustacheKeysInOrder, + executeActionDTO, + explicitCastDataTypes); } @Override @@ -324,22 +324,22 @@ public Mono getEndpointIdentifierForRateLimit(DatasourceConfiguration da } } if (SSHUtils.isSSHEnabled(datasourceConfiguration, CONNECTION_METHOD_INDEX) - && sshProxy != null - && !isBlank(sshProxy.getHost())) { + && sshProxy != null + && !isBlank(sshProxy.getHost())) { identifier += "_" + sshProxy.getHost() + "_" - + SSHUtils.getSSHPortFromConfigOrDefault(datasourceConfiguration); + + SSHUtils.getSSHPortFromConfigOrDefault(datasourceConfiguration); } return Mono.just(identifier); } private Mono executeCommon( - HikariDataSource connection, - DatasourceConfiguration datasourceConfiguration, - ActionConfiguration actionConfiguration, - Boolean preparedStatement, - List mustacheValuesInOrder, - ExecuteActionDTO executeActionDTO, - List explicitCastDataTypes) { + HikariDataSource connection, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration, + Boolean preparedStatement, + List mustacheValuesInOrder, + ExecuteActionDTO executeActionDTO, + List explicitCastDataTypes) { final Map requestData = new HashMap<>(); requestData.put("preparedStatement", TRUE.equals(preparedStatement) ? true : false); @@ -348,276 +348,276 @@ private Mono executeCommon( Map psParams = preparedStatement ? new LinkedHashMap<>() : null; String transformedQuery = preparedStatement ? replaceQuestionMarkWithDollarIndex(query) : query; List requestParams = - List.of(new RequestParamDTO(ACTION_CONFIGURATION_BODY, transformedQuery, null, null, psParams)); + List.of(new RequestParamDTO(ACTION_CONFIGURATION_BODY, transformedQuery, null, null, psParams)); Instant requestedAt = Instant.now(); return Mono.fromCallable(() -> { - Connection connectionFromPool; - - try { - connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool( - connection, POSTGRES_PLUGIN_NAME); - } catch (SQLException | StaleConnectionException e) { - // The function can throw either StaleConnectionException or SQLException. The - // underlying hikari - // library throws SQLException in case the pool is closed or there is an issue - // initializing - // the connection pool which can also be translated in our world to - // StaleConnectionException - // and should then trigger the destruction and recreation of the pool. - return Mono.error( - e instanceof StaleConnectionException - ? e - : new StaleConnectionException(e.getMessage())); + Connection connectionFromPool; + + try { + connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool( + connection, POSTGRES_PLUGIN_NAME); + } catch (SQLException | StaleConnectionException e) { + // The function can throw either StaleConnectionException or SQLException. The + // underlying hikari + // library throws SQLException in case the pool is closed or there is an issue + // initializing + // the connection pool which can also be translated in our world to + // StaleConnectionException + // and should then trigger the destruction and recreation of the pool. + return Mono.error( + e instanceof StaleConnectionException + ? e + : new StaleConnectionException(e.getMessage())); + } + + List> rowsList = new ArrayList<>(50); + final List columnsList = new ArrayList<>(); + + Statement statement = null; + ResultSet resultSet = null; + PreparedStatement preparedQuery = null; + boolean isResultSet; + + HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean(); + + int idleConnections = poolProxy.getIdleConnections(); + int activeConnections = poolProxy.getActiveConnections(); + int totalConnections = poolProxy.getTotalConnections(); + int threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); + log.debug( + "Before executing postgres query [{}] Hikari Pool stats : active - {} , idle - {} , awaiting - {} , total - {}", + query, + activeConnections, + idleConnections, + threadsAwaitingConnection, + totalConnections); + try { + if (FALSE.equals(preparedStatement)) { + statement = connectionFromPool.createStatement(); + isResultSet = statement.execute(query); + resultSet = statement.getResultSet(); + } else { + preparedQuery = connectionFromPool.prepareStatement(query); + + List> parameters = new ArrayList<>(); + preparedQuery = (PreparedStatement) smartSubstitutionOfBindings( + preparedQuery, + mustacheValuesInOrder, + executeActionDTO.getParams(), + parameters, + connectionFromPool, + explicitCastDataTypes); + + IntStream.range(0, parameters.size()) + .forEachOrdered(i -> psParams.put( + getPSParamLabel(i + 1), + new PsParameterDTO( + parameters.get(i).getKey(), + parameters.get(i).getValue()))); + + requestData.put("ps-parameters", parameters); + isResultSet = preparedQuery.execute(); + resultSet = preparedQuery.getResultSet(); } - List> rowsList = new ArrayList<>(50); - final List columnsList = new ArrayList<>(); + if (!isResultSet) { - Statement statement = null; - ResultSet resultSet = null; - PreparedStatement preparedQuery = null; - boolean isResultSet; + Object updateCount = FALSE.equals(preparedStatement) + ? ObjectUtils.defaultIfNull(statement.getUpdateCount(), 0) + : ObjectUtils.defaultIfNull(preparedQuery.getUpdateCount(), 0); - HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean(); + rowsList.add(Map.of("affectedRows", updateCount)); - int idleConnections = poolProxy.getIdleConnections(); - int activeConnections = poolProxy.getActiveConnections(); - int totalConnections = poolProxy.getTotalConnections(); - int threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); - log.debug( - "Before executing postgres query [{}] Hikari Pool stats : active - {} , idle - {} , awaiting - {} , total - {}", - query, - activeConnections, - idleConnections, - threadsAwaitingConnection, - totalConnections); - try { - if (FALSE.equals(preparedStatement)) { - statement = connectionFromPool.createStatement(); - isResultSet = statement.execute(query); - resultSet = statement.getResultSet(); - } else { - preparedQuery = connectionFromPool.prepareStatement(query); - - List> parameters = new ArrayList<>(); - preparedQuery = (PreparedStatement) smartSubstitutionOfBindings( - preparedQuery, - mustacheValuesInOrder, - executeActionDTO.getParams(), - parameters, - connectionFromPool, - explicitCastDataTypes); - - IntStream.range(0, parameters.size()) - .forEachOrdered(i -> psParams.put( - getPSParamLabel(i + 1), - new PsParameterDTO( - parameters.get(i).getKey(), - parameters.get(i).getValue()))); - - requestData.put("ps-parameters", parameters); - isResultSet = preparedQuery.execute(); - resultSet = preparedQuery.getResultSet(); - } + } else { - if (!isResultSet) { + ResultSetMetaData metaData = resultSet.getMetaData(); + int colCount = metaData.getColumnCount(); + columnsList.addAll(getColumnsListForJdbcPlugin(metaData)); - Object updateCount = FALSE.equals(preparedStatement) - ? ObjectUtils.defaultIfNull(statement.getUpdateCount(), 0) - : ObjectUtils.defaultIfNull(preparedQuery.getUpdateCount(), 0); + int iterator = 0; + while (resultSet.next()) { - rowsList.add(Map.of("affectedRows", updateCount)); + // Only check the data size at low frequency to ensure the performance is not + // impacted heavily + if (iterator % HEAVY_OP_FREQUENCY == 0) { + int objectSize = sizeof(rowsList); - } else { + if (objectSize > MAX_SIZE_SUPPORTED) { + log.debug( + "[PostgresPlugin] Result size greater than maximum supported size of {} bytes. Current size : {}", + MAX_SIZE_SUPPORTED, + objectSize); + return Mono.error(new AppsmithPluginException( + PostgresPluginError.RESPONSE_SIZE_TOO_LARGE, + (float) (MAX_SIZE_SUPPORTED / (1024 * 1024)))); + } + } - ResultSetMetaData metaData = resultSet.getMetaData(); - int colCount = metaData.getColumnCount(); - columnsList.addAll(getColumnsListForJdbcPlugin(metaData)); + // Use `LinkedHashMap` here so that the column ordering is preserved in the + // response. + Map row = new LinkedHashMap<>(colCount); - int iterator = 0; - while (resultSet.next()) { + for (int i = 1; i <= colCount; i++) { + Object value; + final String typeName = metaData.getColumnTypeName(i); - // Only check the data size at low frequency to ensure the performance is not - // impacted heavily - if (iterator % HEAVY_OP_FREQUENCY == 0) { - int objectSize = sizeof(rowsList); + if (resultSet.getObject(i) == null) { + value = null; - if (objectSize > MAX_SIZE_SUPPORTED) { - log.debug( - "[PostgresPlugin] Result size greater than maximum supported size of {} bytes. Current size : {}", - MAX_SIZE_SUPPORTED, - objectSize); - return Mono.error(new AppsmithPluginException( - PostgresPluginError.RESPONSE_SIZE_TOO_LARGE, - (float) (MAX_SIZE_SUPPORTED / (1024 * 1024)))); - } - } + } else if (DATE_COLUMN_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = DateTimeFormatter.ISO_DATE.format( + resultSet.getDate(i).toLocalDate()); - // Use `LinkedHashMap` here so that the column ordering is preserved in the - // response. - Map row = new LinkedHashMap<>(colCount); - - for (int i = 1; i <= colCount; i++) { - Object value; - final String typeName = metaData.getColumnTypeName(i); - - if (resultSet.getObject(i) == null) { - value = null; - - } else if (DATE_COLUMN_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = DateTimeFormatter.ISO_DATE.format( - resultSet.getDate(i).toLocalDate()); - - } else if (TIMESTAMP_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.of( - resultSet.getDate(i).toLocalDate(), - resultSet.getTime(i).toLocalTime())) - + "Z"; - - } else if (TIMESTAMPTZ_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = DateTimeFormatter.ISO_DATE_TIME.format( - resultSet.getObject(i, OffsetDateTime.class)); - - } else if (TIME_TYPE_NAME.equalsIgnoreCase(typeName) - || TIMETZ_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = resultSet.getString(i); - - } else if (INTERVAL_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = resultSet.getObject(i).toString(); - - } else if (typeName.startsWith("_")) { - value = resultSet.getArray(i).getArray(); - - } else if (JSON_TYPE_NAME.equalsIgnoreCase(typeName) - || JSONB_TYPE_NAME.equalsIgnoreCase(typeName)) { - value = objectMapper.readTree(resultSet.getString(i)); - } else { - value = resultSet.getObject(i); - - /** - * Any type that JDBC does not understand gets mapped to PGobject. PGobject has - * two attributes: type and value. Hence, when PGobject gets serialized, it gets - * converted into a JSON like {"type":"citext", "value":"someText"}. Since we - * are - * only interested in the value and not the type, it makes sense to extract out - * the value as a string. - * Reference: - * https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/util/PGobject.html - */ - if (value instanceof PGobject) { - value = ((PGobject) value).getValue(); - } - } + } else if (TIMESTAMP_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.of( + resultSet.getDate(i).toLocalDate(), + resultSet.getTime(i).toLocalTime())) + + "Z"; - row.put(metaData.getColumnName(i), value); - } + } else if (TIMESTAMPTZ_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = DateTimeFormatter.ISO_DATE_TIME.format( + resultSet.getObject(i, OffsetDateTime.class)); - rowsList.add(row); + } else if (TIME_TYPE_NAME.equalsIgnoreCase(typeName) + || TIMETZ_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = resultSet.getString(i); - iterator++; - } - } + } else if (INTERVAL_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = resultSet.getObject(i).toString(); - } catch (SQLException e) { - log.debug("In the PostgresPlugin, got action execution error"); - return Mono.error(new AppsmithPluginException( - PostgresPluginError.QUERY_EXECUTION_FAILED, - PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, - e.getMessage(), - "SQLSTATE: " + e.getSQLState())); - } catch (IOException e) { - // Since postgres json type field can only hold valid json data, this exception - // is not expected - // to occur. - log.debug("In the PostgresPlugin, got action execution error"); - return Mono.error(new AppsmithPluginException( - PostgresPluginError.QUERY_EXECUTION_FAILED, - PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, - e.getMessage())); - } finally { - idleConnections = poolProxy.getIdleConnections(); - activeConnections = poolProxy.getActiveConnections(); - totalConnections = poolProxy.getTotalConnections(); - threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); - log.debug( - "After executing postgres query, Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", - activeConnections, - idleConnections, - threadsAwaitingConnection, - totalConnections); - if (resultSet != null) { - try { - resultSet.close(); - } catch (SQLException e) { - log.debug("Execute Error closing Postgres ResultSet", e); - } - } + } else if (typeName.startsWith("_")) { + value = resultSet.getArray(i).getArray(); - if (statement != null) { - try { - statement.close(); - } catch (SQLException e) { - log.debug("Execute Error closing Postgres Statement", e); + } else if (JSON_TYPE_NAME.equalsIgnoreCase(typeName) + || JSONB_TYPE_NAME.equalsIgnoreCase(typeName)) { + value = objectMapper.readTree(resultSet.getString(i)); + } else { + value = resultSet.getObject(i); + + /** + * Any type that JDBC does not understand gets mapped to PGobject. PGobject has + * two attributes: type and value. Hence, when PGobject gets serialized, it gets + * converted into a JSON like {"type":"citext", "value":"someText"}. Since we + * are + * only interested in the value and not the type, it makes sense to extract out + * the value as a string. + * Reference: + * https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/util/PGobject.html + */ + if (value instanceof PGobject) { + value = ((PGobject) value).getValue(); + } + } + + row.put(metaData.getColumnName(i), value); } + + rowsList.add(row); + + iterator++; } + } - if (preparedQuery != null) { - try { - preparedQuery.close(); - } catch (SQLException e) { - log.debug("Execute Error closing Postgres Statement", e); - } + } catch (SQLException e) { + log.debug("In the PostgresPlugin, got action execution error"); + return Mono.error(new AppsmithPluginException( + PostgresPluginError.QUERY_EXECUTION_FAILED, + PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, + e.getMessage(), + "SQLSTATE: " + e.getSQLState())); + } catch (IOException e) { + // Since postgres json type field can only hold valid json data, this exception + // is not expected + // to occur. + log.debug("In the PostgresPlugin, got action execution error"); + return Mono.error(new AppsmithPluginException( + PostgresPluginError.QUERY_EXECUTION_FAILED, + PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, + e.getMessage())); + } finally { + idleConnections = poolProxy.getIdleConnections(); + activeConnections = poolProxy.getActiveConnections(); + totalConnections = poolProxy.getTotalConnections(); + threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); + log.debug( + "After executing postgres query, Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", + activeConnections, + idleConnections, + threadsAwaitingConnection, + totalConnections); + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException e) { + log.debug("Execute Error closing Postgres ResultSet", e); } + } - if (connectionFromPool != null) { - try { - // Return the connection back to the pool - connectionFromPool.close(); - } catch (SQLException e) { - log.debug("Execute Error returning Postgres connection to pool", e); - } + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + log.debug("Execute Error closing Postgres Statement", e); } } - ActionExecutionResult result = new ActionExecutionResult(); - result.setBody(objectMapper.valueToTree(rowsList)); - result.setMessages(populateHintMessages(columnsList)); - result.setIsExecutionSuccess(true); - log.debug("In the PostgresPlugin, got action execution result"); - return Mono.just(result); - }) - .flatMap(obj -> obj) - .map(obj -> (ActionExecutionResult) obj) - .onErrorResume(error -> { - if (error instanceof StaleConnectionException) { - return Mono.error(error); - } else if (!(error instanceof AppsmithPluginException)) { - error = new AppsmithPluginException( - PostgresPluginError.QUERY_EXECUTION_FAILED, - PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, - error); + if (preparedQuery != null) { + try { + preparedQuery.close(); + } catch (SQLException e) { + log.debug("Execute Error closing Postgres Statement", e); + } } - ActionExecutionResult result = new ActionExecutionResult(); - result.setIsExecutionSuccess(false); - result.setErrorInfo(error); - return Mono.just(result); - }) - // Now set the request in the result to be returned to the server - .map(actionExecutionResult -> { - ActionExecutionRequest request = new ActionExecutionRequest(); - request.setQuery(query); - request.setProperties(requestData); - request.setRequestParams(requestParams); - if (request.getRequestedAt() == null) { - request.setRequestedAt(requestedAt); + + if (connectionFromPool != null) { + try { + // Return the connection back to the pool + connectionFromPool.close(); + } catch (SQLException e) { + log.debug("Execute Error returning Postgres connection to pool", e); + } } - ActionExecutionResult result = actionExecutionResult; - result.setRequest(request); - return result; - }) - .timeout(Duration.ofMillis(actionConfiguration.getTimeoutInMillisecond())) - .subscribeOn(scheduler); + } + + ActionExecutionResult result = new ActionExecutionResult(); + result.setBody(objectMapper.valueToTree(rowsList)); + result.setMessages(populateHintMessages(columnsList)); + result.setIsExecutionSuccess(true); + log.debug("In the PostgresPlugin, got action execution result"); + return Mono.just(result); + }) + .flatMap(obj -> obj) + .map(obj -> (ActionExecutionResult) obj) + .onErrorResume(error -> { + if (error instanceof StaleConnectionException) { + return Mono.error(error); + } else if (!(error instanceof AppsmithPluginException)) { + error = new AppsmithPluginException( + PostgresPluginError.QUERY_EXECUTION_FAILED, + PostgresErrorMessages.QUERY_EXECUTION_FAILED_ERROR_MSG, + error); + } + ActionExecutionResult result = new ActionExecutionResult(); + result.setIsExecutionSuccess(false); + result.setErrorInfo(error); + return Mono.just(result); + }) + // Now set the request in the result to be returned to the server + .map(actionExecutionResult -> { + ActionExecutionRequest request = new ActionExecutionRequest(); + request.setQuery(query); + request.setProperties(requestData); + request.setRequestParams(requestParams); + if (request.getRequestedAt() == null) { + request.setRequestedAt(requestedAt); + } + ActionExecutionResult result = actionExecutionResult; + result.setRequest(request); + return result; + }) + .timeout(Duration.ofMillis(actionConfiguration.getTimeoutInMillisecond())) + .subscribeOn(scheduler); } private Set populateHintMessages(List columnNames) { @@ -627,9 +627,9 @@ private Set populateHintMessages(List columnNames) { List identicalColumns = getIdenticalColumns(columnNames); if (!CollectionUtils.isEmpty(identicalColumns)) { messages.add("Your PostgreSQL query result may not have all the columns because duplicate column " - + "names were found for the column(s): " - + String.join(", ", identicalColumns) + ". You may use" - + " the SQL keyword 'as' to rename the duplicate column name(s) and resolve this issue."); + + "names were found for the column(s): " + + String.join(", ", identicalColumns) + ". You may use" + + " the SQL keyword 'as' to rename the duplicate column name(s) and resolve this issue."); } return messages; @@ -637,12 +637,12 @@ private Set populateHintMessages(List columnNames) { @Override public Mono execute( - HikariDataSource connection, - DatasourceConfiguration datasourceConfiguration, - ActionConfiguration actionConfiguration) { + HikariDataSource connection, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration) { // Unused function return Mono.error( - new AppsmithPluginException(PostgresPluginError.QUERY_EXECUTION_FAILED, "Unsupported Operation")); + new AppsmithPluginException(PostgresPluginError.QUERY_EXECUTION_FAILED, "Unsupported Operation")); } @Override @@ -651,20 +651,20 @@ public Mono datasourceCreate(DatasourceConfiguration datasourc Class.forName(JDBC_DRIVER); } catch (ClassNotFoundException e) { return Mono.error(new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, - PostgresErrorMessages.POSTGRES_JDBC_DRIVER_LOADING_ERROR_MSG, - e.getMessage())); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, + PostgresErrorMessages.POSTGRES_JDBC_DRIVER_LOADING_ERROR_MSG, + e.getMessage())); } return connectionPoolConfig - .getMaxConnectionPoolSize() - .flatMap(maxPoolSize -> { - return Mono.fromCallable(() -> { - log.debug("Connecting to Postgres db"); - return createConnectionPool(datasourceConfiguration, maxPoolSize); - }); - }) - .subscribeOn(scheduler); + .getMaxConnectionPoolSize() + .flatMap(maxPoolSize -> { + return Mono.fromCallable(() -> { + log.debug("Connecting to Postgres db"); + return createConnectionPool(datasourceConfiguration, maxPoolSize); + }); + }) + .subscribeOn(scheduler); } @Override @@ -685,15 +685,15 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur if (StringUtils.isEmpty(endpoint.getHost())) { invalids.add(PostgresErrorMessages.DS_MISSING_HOSTNAME_ERROR_MSG); } else if (endpoint.getHost().contains("/") - || endpoint.getHost().contains(":")) { + || endpoint.getHost().contains(":")) { invalids.add( - String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); + String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); } } } if (datasourceConfiguration.getConnection() != null - && datasourceConfiguration.getConnection().getMode() == null) { + && datasourceConfiguration.getConnection().getMode() == null) { invalids.add(PostgresErrorMessages.DS_MISSING_CONNECTION_MODE_ERROR_MSG); } @@ -720,14 +720,14 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur * a initial value. */ if (datasourceConfiguration.getConnection() == null - || datasourceConfiguration.getConnection().getSsl() == null - || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { + || datasourceConfiguration.getConnection().getSsl() == null + || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { invalids.add(PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); } if (isSSHEnabled(datasourceConfiguration, CONNECTION_METHOD_INDEX)) { if (datasourceConfiguration.getSshProxy() == null - || isBlank(datasourceConfiguration.getSshProxy().getHost())) { + || isBlank(datasourceConfiguration.getSshProxy().getHost())) { invalids.add(DS_MISSING_SSH_HOSTNAME_ERROR_MSG); } else { String sshHost = datasourceConfiguration.getSshProxy().getHost(); @@ -741,12 +741,12 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur } if (datasourceConfiguration.getSshProxy().getPrivateKey() == null - || datasourceConfiguration.getSshProxy().getPrivateKey().getKeyFile() == null - || isBlank(datasourceConfiguration - .getSshProxy() - .getPrivateKey() - .getKeyFile() - .getBase64Content())) { + || datasourceConfiguration.getSshProxy().getPrivateKey().getKeyFile() == null + || isBlank(datasourceConfiguration + .getSshProxy() + .getPrivateKey() + .getKeyFile() + .getBase64Content())) { invalids.add(DS_MISSING_SSH_KEY_ERROR_MSG); } } @@ -756,260 +756,260 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur @Override public Mono getStructure( - HikariDataSource connection, DatasourceConfiguration datasourceConfiguration) { + HikariDataSource connection, DatasourceConfiguration datasourceConfiguration) { final DatasourceStructure structure = new DatasourceStructure(); final Map tablesByName = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); return Mono.fromSupplier(() -> { - Connection connectionFromPool; - try { - connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool( - connection, POSTGRES_PLUGIN_NAME); - } catch (SQLException | StaleConnectionException e) { - // The function can throw either StaleConnectionException or SQLException. The - // underlying hikari - // library throws SQLException in case the pool is closed or there is an issue - // initializing - // the connection pool which can also be translated in our world to - // StaleConnectionException - // and should then trigger the destruction and recreation of the pool. - return Mono.error( - e instanceof StaleConnectionException - ? e - : new StaleConnectionException(e.getMessage())); - } - - HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean(); + Connection connectionFromPool; + try { + connectionFromPool = postgresDatasourceUtils.getConnectionFromHikariConnectionPool( + connection, POSTGRES_PLUGIN_NAME); + } catch (SQLException | StaleConnectionException e) { + // The function can throw either StaleConnectionException or SQLException. The + // underlying hikari + // library throws SQLException in case the pool is closed or there is an issue + // initializing + // the connection pool which can also be translated in our world to + // StaleConnectionException + // and should then trigger the destruction and recreation of the pool. + return Mono.error( + e instanceof StaleConnectionException + ? e + : new StaleConnectionException(e.getMessage())); + } - int idleConnections = poolProxy.getIdleConnections(); - int activeConnections = poolProxy.getActiveConnections(); - int totalConnections = poolProxy.getTotalConnections(); - int threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); - log.debug( - "Before getting postgres db structure Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", - activeConnections, - idleConnections, - threadsAwaitingConnection, - totalConnections); - - // Ref: - // . - try (Statement statement = connectionFromPool.createStatement()) { - - // Get tables and fill up their columns. - try (ResultSet columnsResultSet = statement.executeQuery(TABLES_QUERY)) { - while (columnsResultSet.next()) { - final char kind = - columnsResultSet.getString("kind").charAt(0); - final String schemaName = columnsResultSet.getString("schema_name"); - final String tableName = columnsResultSet.getString("table_name"); - final String fullTableName = schemaName + "." + tableName; - if (!tablesByName.containsKey(fullTableName)) { - tablesByName.put( - fullTableName, - new DatasourceStructure.Table( - kind == 'r' - ? DatasourceStructure.TableType.TABLE - : DatasourceStructure.TableType.VIEW, - schemaName, - fullTableName, - new ArrayList<>(), - new ArrayList<>(), - new ArrayList<>())); - } - final DatasourceStructure.Table table = tablesByName.get(fullTableName); - final String defaultExpr = columnsResultSet.getString("default_expr"); - boolean isAutogenerated = !StringUtils.isEmpty(defaultExpr) - && defaultExpr.toLowerCase().contains("nextval"); - - table.getColumns() - .add(new DatasourceStructure.Column( - columnsResultSet.getString("name"), - columnsResultSet.getString("column_type"), - defaultExpr, - isAutogenerated)); + HikariPoolMXBean poolProxy = connection.getHikariPoolMXBean(); + + int idleConnections = poolProxy.getIdleConnections(); + int activeConnections = poolProxy.getActiveConnections(); + int totalConnections = poolProxy.getTotalConnections(); + int threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); + log.debug( + "Before getting postgres db structure Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", + activeConnections, + idleConnections, + threadsAwaitingConnection, + totalConnections); + + // Ref: + // . + try (Statement statement = connectionFromPool.createStatement()) { + + // Get tables and fill up their columns. + try (ResultSet columnsResultSet = statement.executeQuery(TABLES_QUERY)) { + while (columnsResultSet.next()) { + final char kind = + columnsResultSet.getString("kind").charAt(0); + final String schemaName = columnsResultSet.getString("schema_name"); + final String tableName = columnsResultSet.getString("table_name"); + final String fullTableName = schemaName + "." + tableName; + if (!tablesByName.containsKey(fullTableName)) { + tablesByName.put( + fullTableName, + new DatasourceStructure.Table( + kind == 'r' + ? DatasourceStructure.TableType.TABLE + : DatasourceStructure.TableType.VIEW, + schemaName, + fullTableName, + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>())); } + final DatasourceStructure.Table table = tablesByName.get(fullTableName); + final String defaultExpr = columnsResultSet.getString("default_expr"); + boolean isAutogenerated = !StringUtils.isEmpty(defaultExpr) + && defaultExpr.toLowerCase().contains("nextval"); + + table.getColumns() + .add(new DatasourceStructure.Column( + columnsResultSet.getString("name"), + columnsResultSet.getString("column_type"), + defaultExpr, + isAutogenerated)); } + } - // Get tables' constraints and fill those up. - try (ResultSet constraintsResultSet = statement.executeQuery(KEYS_QUERY)) { - while (constraintsResultSet.next()) { - final String constraintName = constraintsResultSet.getString("constraint_name"); - final char constraintType = constraintsResultSet - .getString("constraint_type") - .charAt(0); - final String selfSchema = constraintsResultSet.getString("self_schema"); - final String tableName = constraintsResultSet.getString("self_table"); - final String fullTableName = selfSchema + "." + tableName; - if (!tablesByName.containsKey(fullTableName)) { - continue; - } - - final DatasourceStructure.Table table = tablesByName.get(fullTableName); - - if (constraintType == 'p') { - final DatasourceStructure.PrimaryKey key = new DatasourceStructure.PrimaryKey( - constraintName, List.of((String[]) constraintsResultSet - .getArray("self_columns") - .getArray())); - table.getKeys().add(key); - - } else if (constraintType == 'f') { - final String foreignSchema = constraintsResultSet.getString("foreign_schema"); - final String prefix = - (foreignSchema.equalsIgnoreCase(selfSchema) ? "" : foreignSchema + ".") - + constraintsResultSet.getString("foreign_table") - + "."; - - final DatasourceStructure.ForeignKey key = new DatasourceStructure.ForeignKey( - constraintName, - List.of((String[]) constraintsResultSet - .getArray("self_columns") - .getArray()), - Stream.of((String[]) constraintsResultSet - .getArray("foreign_columns") - .getArray()) - .map(name -> prefix + name) - .collect(Collectors.toList())); - - table.getKeys().add(key); - } + // Get tables' constraints and fill those up. + try (ResultSet constraintsResultSet = statement.executeQuery(KEYS_QUERY)) { + while (constraintsResultSet.next()) { + final String constraintName = constraintsResultSet.getString("constraint_name"); + final char constraintType = constraintsResultSet + .getString("constraint_type") + .charAt(0); + final String selfSchema = constraintsResultSet.getString("self_schema"); + final String tableName = constraintsResultSet.getString("self_table"); + final String fullTableName = selfSchema + "." + tableName; + if (!tablesByName.containsKey(fullTableName)) { + continue; } - } - // Get/compute templates for each table and put those in. - for (DatasourceStructure.Table table : tablesByName.values()) { - final List columnsWithoutDefault = - table.getColumns().stream() - .filter(column -> column.getDefaultValue() == null) - .collect(Collectors.toList()); - - final List columnNames = new ArrayList<>(); - final List columnValues = new ArrayList<>(); - final StringBuilder setFragments = new StringBuilder(); - - for (DatasourceStructure.Column column : columnsWithoutDefault) { - final String name = column.getName(); - final String type = column.getType(); - String value; - - if (type == null) { - value = "null"; - } else if ("text".equals(type) || "varchar".equals(type)) { - value = "''"; - } else if (type.startsWith("int")) { - value = "1"; - } else if (type.startsWith("float") || type.startsWith("double")) { - value = "1.0"; - } else if ("date".equals(type)) { - value = "'2019-07-01'"; - } else if ("time".equals(type)) { - value = "'18:32:45'"; - } else if ("timetz".equals(type)) { - value = "'04:05:06 PST'"; - } else if ("timestamp".equals(type)) { - value = "TIMESTAMP '2019-07-01 10:00:00'"; - } else if ("timestamptz".equals(type)) { - value = "TIMESTAMP WITH TIME ZONE '2019-07-01 06:30:00 CET'"; - } else if (type.startsWith("_int")) { - value = "'{1, 2, 3}'"; - } else if ("_varchar".equals(type)) { - value = "'{\"first\", \"second\"}'"; - } else { - value = "''"; - } - - columnNames.add("\"" + name + "\""); - columnValues.add(value); - setFragments - .append("\n \"") - .append(name) - .append("\" = ") - .append(value) - .append(","); + final DatasourceStructure.Table table = tablesByName.get(fullTableName); + + if (constraintType == 'p') { + final DatasourceStructure.PrimaryKey key = new DatasourceStructure.PrimaryKey( + constraintName, List.of((String[]) constraintsResultSet + .getArray("self_columns") + .getArray())); + table.getKeys().add(key); + + } else if (constraintType == 'f') { + final String foreignSchema = constraintsResultSet.getString("foreign_schema"); + final String prefix = + (foreignSchema.equalsIgnoreCase(selfSchema) ? "" : foreignSchema + ".") + + constraintsResultSet.getString("foreign_table") + + "."; + + final DatasourceStructure.ForeignKey key = new DatasourceStructure.ForeignKey( + constraintName, + List.of((String[]) constraintsResultSet + .getArray("self_columns") + .getArray()), + Stream.of((String[]) constraintsResultSet + .getArray("foreign_columns") + .getArray()) + .map(name -> prefix + name) + .collect(Collectors.toList())); + + table.getKeys().add(key); } + } + } - // Delete the last comma - if (setFragments.length() > 0) { - setFragments.deleteCharAt(setFragments.length() - 1); + // Get/compute templates for each table and put those in. + for (DatasourceStructure.Table table : tablesByName.values()) { + final List columnsWithoutDefault = + table.getColumns().stream() + .filter(column -> column.getDefaultValue() == null) + .collect(Collectors.toList()); + + final List columnNames = new ArrayList<>(); + final List columnValues = new ArrayList<>(); + final StringBuilder setFragments = new StringBuilder(); + + for (DatasourceStructure.Column column : columnsWithoutDefault) { + final String name = column.getName(); + final String type = column.getType(); + String value; + + if (type == null) { + value = "null"; + } else if ("text".equals(type) || "varchar".equals(type)) { + value = "''"; + } else if (type.startsWith("int")) { + value = "1"; + } else if (type.startsWith("float") || type.startsWith("double")) { + value = "1.0"; + } else if ("date".equals(type)) { + value = "'2019-07-01'"; + } else if ("time".equals(type)) { + value = "'18:32:45'"; + } else if ("timetz".equals(type)) { + value = "'04:05:06 PST'"; + } else if ("timestamp".equals(type)) { + value = "TIMESTAMP '2019-07-01 10:00:00'"; + } else if ("timestamptz".equals(type)) { + value = "TIMESTAMP WITH TIME ZONE '2019-07-01 06:30:00 CET'"; + } else if (type.startsWith("_int")) { + value = "'{1, 2, 3}'"; + } else if ("_varchar".equals(type)) { + value = "'{\"first\", \"second\"}'"; + } else { + value = "''"; } - final String quotedTableName = table.getName().replaceFirst("\\.(.+)", ".\"$1\""); - table.getTemplates() - .addAll(List.of( - new DatasourceStructure.Template( - "SELECT", - "SELECT * FROM " + quotedTableName + " LIMIT 10;", - true), - new DatasourceStructure.Template( - "INSERT", - "INSERT INTO " + quotedTableName - + " (" + String.join(", ", columnNames) + ")\n" - + " VALUES (" + String.join(", ", columnValues) - + ");", - false), - new DatasourceStructure.Template( - "UPDATE", - "UPDATE " + quotedTableName + " SET" - + setFragments.toString() + "\n" - + " WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may update every row in the table!", - false), - new DatasourceStructure.Template( - "DELETE", - "DELETE FROM " + quotedTableName - + "\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may delete everything in the table!", - false))); + columnNames.add("\"" + name + "\""); + columnValues.add(value); + setFragments + .append("\n \"") + .append(name) + .append("\" = ") + .append(value) + .append(","); } - } catch (SQLException throwable) { - return Mono.error(new AppsmithPluginException( - AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR, - PostgresErrorMessages.GET_STRUCTURE_ERROR_MSG, - throwable.getMessage(), - "SQLSTATE: " + throwable.getSQLState())); - } finally { - idleConnections = poolProxy.getIdleConnections(); - activeConnections = poolProxy.getActiveConnections(); - totalConnections = poolProxy.getTotalConnections(); - threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); - log.debug( - "After postgres db structure, Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", - activeConnections, - idleConnections, - threadsAwaitingConnection, - totalConnections); - - if (connectionFromPool != null) { - try { - // Return the connection back to the pool - connectionFromPool.close(); - } catch (SQLException e) { - log.debug("Error returning Postgres connection to pool during get structure", e); - } + // Delete the last comma + if (setFragments.length() > 0) { + setFragments.deleteCharAt(setFragments.length() - 1); } + + final String quotedTableName = table.getName().replaceFirst("\\.(.+)", ".\"$1\""); + table.getTemplates() + .addAll(List.of( + new DatasourceStructure.Template( + "SELECT", + "SELECT * FROM " + quotedTableName + " LIMIT 10;", + true), + new DatasourceStructure.Template( + "INSERT", + "INSERT INTO " + quotedTableName + + " (" + String.join(", ", columnNames) + ")\n" + + " VALUES (" + String.join(", ", columnValues) + + ");", + false), + new DatasourceStructure.Template( + "UPDATE", + "UPDATE " + quotedTableName + " SET" + + setFragments.toString() + "\n" + + " WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may update every row in the table!", + false), + new DatasourceStructure.Template( + "DELETE", + "DELETE FROM " + quotedTableName + + "\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may delete everything in the table!", + false))); } - structure.setTables(new ArrayList<>(tablesByName.values())); - for (DatasourceStructure.Table table : structure.getTables()) { - table.getKeys().sort(Comparator.naturalOrder()); + } catch (SQLException throwable) { + return Mono.error(new AppsmithPluginException( + AppsmithPluginError.PLUGIN_GET_STRUCTURE_ERROR, + PostgresErrorMessages.GET_STRUCTURE_ERROR_MSG, + throwable.getMessage(), + "SQLSTATE: " + throwable.getSQLState())); + } finally { + idleConnections = poolProxy.getIdleConnections(); + activeConnections = poolProxy.getActiveConnections(); + totalConnections = poolProxy.getTotalConnections(); + threadsAwaitingConnection = poolProxy.getThreadsAwaitingConnection(); + log.debug( + "After postgres db structure, Hikari Pool stats active - {} , idle - {} , awaiting - {} , total - {} ", + activeConnections, + idleConnections, + threadsAwaitingConnection, + totalConnections); + + if (connectionFromPool != null) { + try { + // Return the connection back to the pool + connectionFromPool.close(); + } catch (SQLException e) { + log.debug("Error returning Postgres connection to pool during get structure", e); + } } - log.debug("Got the structure of postgres db"); - return structure; - }) - .map(resultStructure -> (DatasourceStructure) resultStructure) - .subscribeOn(scheduler); + } + + structure.setTables(new ArrayList<>(tablesByName.values())); + for (DatasourceStructure.Table table : structure.getTables()) { + table.getKeys().sort(Comparator.naturalOrder()); + } + log.debug("Got the structure of postgres db"); + return structure; + }) + .map(resultStructure -> (DatasourceStructure) resultStructure) + .subscribeOn(scheduler); } @Override public Object substituteValueInInput( - int index, - String binding, - String value, - Object input, - List> insertedParams, - Object... args) - throws AppsmithPluginException { + int index, + String binding, + String value, + Object input, + List> insertedParams, + Object... args) + throws AppsmithPluginException { PreparedStatement preparedStatement = (PreparedStatement) input; HikariProxyConnection connection = (HikariProxyConnection) args[0]; @@ -1021,7 +1021,7 @@ public Object substituteValueInInput( valueType = explicitCastDataTypes.get(index - 1); } else { AppsmithType appsmithType = DataTypeServiceUtils.getAppsmithType( - param.getClientDataType(), value, PostgresSpecificDataTypes.pluginSpecificTypes); + param.getClientDataType(), value, PostgresSpecificDataTypes.pluginSpecificTypes); valueType = appsmithType.type(); } @@ -1082,7 +1082,7 @@ public Object substituteValueInInput( // Find the type of the entries in the list Object firstEntry = arrayListFromInput.get(0); AppsmithType appsmithType = DataTypeServiceUtils.getAppsmithType( - param.getDataTypesOfArrayElements().get(0), String.valueOf(firstEntry)); + param.getDataTypesOfArrayElements().get(0), String.valueOf(firstEntry)); DataType dataType = appsmithType.type(); String typeName = toPostgresqlPrimitiveTypeName(dataType); @@ -1108,9 +1108,9 @@ public Object substituteValueInInput( // the query. Ignore the exception } else { throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, - String.format(PostgresErrorMessages.QUERY_PREPARATION_FAILED_ERROR_MSG, value, binding), - e.getMessage()); + AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, + String.format(PostgresErrorMessages.QUERY_PREPARATION_FAILED_ERROR_MSG, value, binding), + e.getMessage()); } } @@ -1139,7 +1139,7 @@ private static String toPostgresqlPrimitiveTypeName(DataType type) { throw new IllegalArgumentException("Array of Array datatype is not supported."); default: throw new IllegalArgumentException( - "Unable to map the computed data type to primitive Postgresql type"); + "Unable to map the computed data type to primitive Postgresql type"); } } } @@ -1152,8 +1152,8 @@ private static String toPostgresqlPrimitiveTypeName(DataType type) { * @return connection pool */ private static HikariDataSource createConnectionPool( - DatasourceConfiguration datasourceConfiguration, Integer maximumConfigurablePoolSize) - throws AppsmithPluginException { + DatasourceConfiguration datasourceConfiguration, Integer maximumConfigurablePoolSize) + throws AppsmithPluginException { HikariConfig config = new HikariConfig(); config.setDriverClassName(JDBC_DRIVER); @@ -1190,10 +1190,10 @@ private static HikariDataSource createConnectionPool( } else { ConnectionContext connectionContext; connectionContext = getConnectionContext( - datasourceConfiguration, CONNECTION_METHOD_INDEX, DEFAULT_POSTGRES_PORT, HikariDataSource.class); + datasourceConfiguration, CONNECTION_METHOD_INDEX, DEFAULT_POSTGRES_PORT, HikariDataSource.class); hosts.add(LOCALHOST + ":" - + connectionContext.getSshTunnelContext().getServerSocket().getLocalPort()); + + connectionContext.getSshTunnelContext().getServerSocket().getLocalPort()); } urlBuilder.append(String.join(",", hosts)).append("/"); @@ -1221,17 +1221,17 @@ private static HikariDataSource createConnectionPool( * a initial value. */ if (datasourceConfiguration.getConnection() == null - || datasourceConfiguration.getConnection().getSsl() == null - || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { + || datasourceConfiguration.getConnection().getSsl() == null + || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { throw new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); } /* * - By default, the driver configures SSL in the preferred mode. */ SSLDetails.AuthType sslAuthType = - datasourceConfiguration.getConnection().getSsl().getAuthType(); + datasourceConfiguration.getConnection().getSsl().getAuthType(); switch (sslAuthType) { case ALLOW: case PREFER: @@ -1260,39 +1260,39 @@ private static HikariDataSource createConnectionPool( // Common properties for both VERIFY_CA and VERIFY_FULL config.addDataSourceProperty("sslfactory", MutualTLSCertValidatingFactory.class.getName()); config.addDataSourceProperty( - "clientCertString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getClientCACertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "clientCertString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getClientCACertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); config.addDataSourceProperty( - "clientKeyString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getClientKeyCertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "clientKeyString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getClientKeyCertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); config.addDataSourceProperty( - "serverCACertString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getServerCACertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "serverCACertString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getServerCACertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); break; default: throw new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, - String.format(PostgresErrorMessages.INVALID_SSL_OPTION_ERROR_MSG, sslAuthType)); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, + String.format(PostgresErrorMessages.INVALID_SSL_OPTION_ERROR_MSG, sslAuthType)); } String url = urlBuilder.toString(); @@ -1328,21 +1328,16 @@ private static HikariDataSource createConnectionPool( String sqlState = psqlException.getSQLState(); if (PSQLState.CONNECTION_UNABLE_TO_CONNECT.getState().equals(sqlState)) { throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.DS_INVALID_HOSTNAME_AND_PORT_MSG, - psqlException.getMessage()); - } else { - throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, - cause.getMessage()); - } - } else { - throw new AppsmithPluginException( AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, - cause != null ? cause.getMessage() : e.getMessage()); + PostgresErrorMessages.DS_INVALID_HOSTNAME_AND_PORT_MSG, + psqlException.getMessage()); + } } + throw new AppsmithPluginException( + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, + cause != null ? cause.getMessage() : e.getMessage()); + } return datasource; From 5723974fc861d24ed35a55735d31f6a6a4cc4951 Mon Sep 17 00:00:00 2001 From: AnnaHariprasad5123 Date: Mon, 9 Sep 2024 11:25:00 +0530 Subject: [PATCH 4/5] removed-comma-in-error-messages --- app/client/src/sagas/DatasourcesSagas.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/client/src/sagas/DatasourcesSagas.ts b/app/client/src/sagas/DatasourcesSagas.ts index b05c609664ed..2b9f3cd0e501 100644 --- a/app/client/src/sagas/DatasourcesSagas.ts +++ b/app/client/src/sagas/DatasourcesSagas.ts @@ -923,13 +923,18 @@ function* testDatasourceSaga(actionPayload: ReduxAction) { errorMessages: responseData.invalids, messages: responseData.messages, }); + responseData.invalids.forEach((message: string) => { + toast.show(message, { + kind: "error", + }); + }); yield put({ type: ReduxActionErrorTypes.TEST_DATASOURCE_ERROR, payload: { + show: false, id: datasource.id, environmentId: currentEnvironment, - show: true, - error: { message: responseData.invalids.join(", ") }, + messages: messages, }, }); AppsmithConsole.error({ From 5a727b76a9a1d6dfb32c851d3da2d9e273a78e3f Mon Sep 17 00:00:00 2001 From: AnnaHariprasad5123 Date: Mon, 9 Sep 2024 13:23:39 +0530 Subject: [PATCH 5/5] fix-error-messages --- app/client/src/sagas/DatasourcesSagas.ts | 9 +- .../com/external/plugins/PostgresPlugin.java | 260 +++++++++--------- 2 files changed, 131 insertions(+), 138 deletions(-) diff --git a/app/client/src/sagas/DatasourcesSagas.ts b/app/client/src/sagas/DatasourcesSagas.ts index 2b9f3cd0e501..8d9b77bf4e32 100644 --- a/app/client/src/sagas/DatasourcesSagas.ts +++ b/app/client/src/sagas/DatasourcesSagas.ts @@ -923,18 +923,13 @@ function* testDatasourceSaga(actionPayload: ReduxAction) { errorMessages: responseData.invalids, messages: responseData.messages, }); - responseData.invalids.forEach((message: string) => { - toast.show(message, { - kind: "error", - }); - }); yield put({ type: ReduxActionErrorTypes.TEST_DATASOURCE_ERROR, payload: { - show: false, id: datasource.id, environmentId: currentEnvironment, - messages: messages, + show: true, + error: { message: responseData.invalids.join("\n") }, }, }); AppsmithConsole.error({ diff --git a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java index 4205b07623a3..a27e2ecc8e9b 100644 --- a/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java +++ b/app/server/appsmith-plugins/postgresPlugin/src/main/java/com/external/plugins/PostgresPlugin.java @@ -161,45 +161,45 @@ public static class PostgresPluginExecutor implements SmartSubstitutionInterface private final Scheduler scheduler = Schedulers.boundedElastic(); private static final String TABLES_QUERY = - "select a.attname as name,\n" - + " t1.typname as column_type,\n" - + " case when a.atthasdef then pg_get_expr(d.adbin, d.adrelid) end as default_expr,\n" - + " c.relkind as kind,\n" - + " c.relname as table_name,\n" - + " n.nspname as schema_name\n" - + "from pg_catalog.pg_attribute a\n" - + " left join pg_catalog.pg_type t1 on t1.oid = a.atttypid\n" - + " inner join pg_catalog.pg_class c on a.attrelid = c.oid\n" - + " left join pg_catalog.pg_namespace n on c.relnamespace = n.oid\n" - + " left join pg_catalog.pg_attrdef d on d.adrelid = c.oid and d.adnum = a.attnum\n" - + "where a.attnum > 0\n" - + " and not a.attisdropped\n" - + " and n.nspname not in ('information_schema', 'pg_catalog')\n" - + " and c.relkind in ('r', 'v')\n" - + "order by c.relname, a.attnum;"; + "select a.attname as name,\n" + + " t1.typname as column_type,\n" + + " case when a.atthasdef then pg_get_expr(d.adbin, d.adrelid) end as default_expr,\n" + + " c.relkind as kind,\n" + + " c.relname as table_name,\n" + + " n.nspname as schema_name\n" + + "from pg_catalog.pg_attribute a\n" + + " left join pg_catalog.pg_type t1 on t1.oid = a.atttypid\n" + + " inner join pg_catalog.pg_class c on a.attrelid = c.oid\n" + + " left join pg_catalog.pg_namespace n on c.relnamespace = n.oid\n" + + " left join pg_catalog.pg_attrdef d on d.adrelid = c.oid and d.adnum = a.attnum\n" + + "where a.attnum > 0\n" + + " and not a.attisdropped\n" + + " and n.nspname not in ('information_schema', 'pg_catalog')\n" + + " and c.relkind in ('r', 'v')\n" + + "order by c.relname, a.attnum;"; public static final String KEYS_QUERY = - "select c.conname as constraint_name,\n" - + " c.contype as constraint_type,\n" - + " sch.nspname as self_schema,\n" - + " tbl.relname as self_table,\n" - + " array_agg(col.attname order by u.attposition) as self_columns,\n" - + " f_sch.nspname as foreign_schema,\n" - + " f_tbl.relname as foreign_table,\n" - + " array_agg(f_col.attname order by f_u.attposition) as foreign_columns,\n" - + " pg_get_constraintdef(c.oid) as definition\n" - + "from pg_constraint c\n" - + " left join lateral unnest(c.conkey) with ordinality as u(attnum, attposition) on true\n" - + " left join lateral unnest(c.confkey) with ordinality as f_u(attnum, attposition)\n" - + " on f_u.attposition = u.attposition\n" - + " join pg_class tbl on tbl.oid = c.conrelid\n" - + " join pg_namespace sch on sch.oid = tbl.relnamespace\n" - + " left join pg_attribute col on (col.attrelid = tbl.oid and col.attnum = u.attnum)\n" - + " left join pg_class f_tbl on f_tbl.oid = c.confrelid\n" - + " left join pg_namespace f_sch on f_sch.oid = f_tbl.relnamespace\n" - + " left join pg_attribute f_col on (f_col.attrelid = f_tbl.oid and f_col.attnum = f_u.attnum)\n" - + "group by constraint_name, constraint_type, self_schema, self_table, definition, foreign_schema, foreign_table\n" - + "order by self_schema, self_table;"; + "select c.conname as constraint_name,\n" + + " c.contype as constraint_type,\n" + + " sch.nspname as self_schema,\n" + + " tbl.relname as self_table,\n" + + " array_agg(col.attname order by u.attposition) as self_columns,\n" + + " f_sch.nspname as foreign_schema,\n" + + " f_tbl.relname as foreign_table,\n" + + " array_agg(f_col.attname order by f_u.attposition) as foreign_columns,\n" + + " pg_get_constraintdef(c.oid) as definition\n" + + "from pg_constraint c\n" + + " left join lateral unnest(c.conkey) with ordinality as u(attnum, attposition) on true\n" + + " left join lateral unnest(c.confkey) with ordinality as f_u(attnum, attposition)\n" + + " on f_u.attposition = u.attposition\n" + + " join pg_class tbl on tbl.oid = c.conrelid\n" + + " join pg_namespace sch on sch.oid = tbl.relnamespace\n" + + " left join pg_attribute col on (col.attrelid = tbl.oid and col.attnum = u.attnum)\n" + + " left join pg_class f_tbl on f_tbl.oid = c.confrelid\n" + + " left join pg_namespace f_sch on f_sch.oid = f_tbl.relnamespace\n" + + " left join pg_attribute f_col on (f_col.attrelid = f_tbl.oid and f_col.attnum = f_u.attnum)\n" + + "group by constraint_name, constraint_type, self_schema, self_table, definition, foreign_schema, foreign_table\n" + + "order by self_schema, self_table;"; private static final int PREPARED_STATEMENT_INDEX = 0; @@ -237,10 +237,10 @@ public PostgresPluginExecutor(SharedConfig sharedConfig, ConnectionPoolConfig co */ @Override public Mono executeParameterized( - HikariDataSource connection, - ExecuteActionDTO executeActionDTO, - DatasourceConfiguration datasourceConfiguration, - ActionConfiguration actionConfiguration) { + HikariDataSource connection, + ExecuteActionDTO executeActionDTO, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration) { String printMessage = Thread.currentThread().getName() + ": executeParameterized() called for Postgres plugin."; @@ -250,8 +250,8 @@ public Mono executeParameterized( // connection from the pool op. if (!StringUtils.hasLength(query)) { return Mono.error(new AppsmithPluginException( - AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, - PostgresErrorMessages.MISSING_QUERY_ERROR_MSG)); + AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, + PostgresErrorMessages.MISSING_QUERY_ERROR_MSG)); } Boolean isPreparedStatement; @@ -288,13 +288,13 @@ public Mono executeParameterized( List explicitCastDataTypes = extractExplicitCasting(updatedQuery); actionConfiguration.setBody(updatedQuery); return executeCommon( - connection, - datasourceConfiguration, - actionConfiguration, - TRUE, - mustacheKeysInOrder, - executeActionDTO, - explicitCastDataTypes); + connection, + datasourceConfiguration, + actionConfiguration, + TRUE, + mustacheKeysInOrder, + executeActionDTO, + explicitCastDataTypes); } @Override @@ -334,15 +334,14 @@ public Mono getEndpointIdentifierForRateLimit(DatasourceConfiguration da } } if (SSHUtils.isSSHEnabled(datasourceConfiguration, CONNECTION_METHOD_INDEX) - && sshProxy != null - && !isBlank(sshProxy.getHost())) { + && sshProxy != null + && !isBlank(sshProxy.getHost())) { identifier += "_" + sshProxy.getHost() + "_" - + SSHUtils.getSSHPortFromConfigOrDefault(datasourceConfiguration); + + SSHUtils.getSSHPortFromConfigOrDefault(datasourceConfiguration); } return Mono.just(identifier); } - private Mono executeCommon( HikariDataSource connection, DatasourceConfiguration datasourceConfiguration, @@ -643,7 +642,7 @@ private Mono executeCommon( .timeout(Duration.ofMillis(actionConfiguration.getTimeoutInMillisecond())) .subscribeOn(scheduler); } - + private Set populateHintMessages(List columnNames) { Set messages = new HashSet<>(); @@ -651,9 +650,9 @@ private Set populateHintMessages(List columnNames) { List identicalColumns = getIdenticalColumns(columnNames); if (!CollectionUtils.isEmpty(identicalColumns)) { messages.add("Your PostgreSQL query result may not have all the columns because duplicate column " - + "names were found for the column(s): " - + String.join(", ", identicalColumns) + ". You may use" - + " the SQL keyword 'as' to rename the duplicate column name(s) and resolve this issue."); + + "names were found for the column(s): " + + String.join(", ", identicalColumns) + ". You may use" + + " the SQL keyword 'as' to rename the duplicate column name(s) and resolve this issue."); } return messages; @@ -661,12 +660,12 @@ private Set populateHintMessages(List columnNames) { @Override public Mono execute( - HikariDataSource connection, - DatasourceConfiguration datasourceConfiguration, - ActionConfiguration actionConfiguration) { + HikariDataSource connection, + DatasourceConfiguration datasourceConfiguration, + ActionConfiguration actionConfiguration) { // Unused function return Mono.error( - new AppsmithPluginException(PostgresPluginError.QUERY_EXECUTION_FAILED, "Unsupported Operation")); + new AppsmithPluginException(PostgresPluginError.QUERY_EXECUTION_FAILED, "Unsupported Operation")); } @Override @@ -677,9 +676,9 @@ public Mono datasourceCreate(DatasourceConfiguration datasourc Class.forName(JDBC_DRIVER); } catch (ClassNotFoundException e) { return Mono.error(new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, - PostgresErrorMessages.POSTGRES_JDBC_DRIVER_LOADING_ERROR_MSG, - e.getMessage())); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, + PostgresErrorMessages.POSTGRES_JDBC_DRIVER_LOADING_ERROR_MSG, + e.getMessage())); } return connectionPoolConfig.getMaxConnectionPoolSize().flatMap(maxPoolSize -> Mono.fromCallable(() -> { @@ -710,15 +709,15 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur if (StringUtils.isEmpty(endpoint.getHost())) { invalids.add(PostgresErrorMessages.DS_MISSING_HOSTNAME_ERROR_MSG); } else if (endpoint.getHost().contains("/") - || endpoint.getHost().contains(":")) { + || endpoint.getHost().contains(":")) { invalids.add( - String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); + String.format(PostgresErrorMessages.DS_INVALID_HOSTNAME_ERROR_MSG, endpoint.getHost())); } } } if (datasourceConfiguration.getConnection() != null - && datasourceConfiguration.getConnection().getMode() == null) { + && datasourceConfiguration.getConnection().getMode() == null) { invalids.add(PostgresErrorMessages.DS_MISSING_CONNECTION_MODE_ERROR_MSG); } @@ -745,14 +744,14 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur * a initial value. */ if (datasourceConfiguration.getConnection() == null - || datasourceConfiguration.getConnection().getSsl() == null - || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { + || datasourceConfiguration.getConnection().getSsl() == null + || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { invalids.add(PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); } if (isSSHEnabled(datasourceConfiguration, CONNECTION_METHOD_INDEX)) { if (datasourceConfiguration.getSshProxy() == null - || isBlank(datasourceConfiguration.getSshProxy().getHost())) { + || isBlank(datasourceConfiguration.getSshProxy().getHost())) { invalids.add(DS_MISSING_SSH_HOSTNAME_ERROR_MSG); } else { String sshHost = datasourceConfiguration.getSshProxy().getHost(); @@ -766,12 +765,12 @@ public Set validateDatasource(DatasourceConfiguration datasourceConfigur } if (datasourceConfiguration.getSshProxy().getPrivateKey() == null - || datasourceConfiguration.getSshProxy().getPrivateKey().getKeyFile() == null - || isBlank(datasourceConfiguration - .getSshProxy() - .getPrivateKey() - .getKeyFile() - .getBase64Content())) { + || datasourceConfiguration.getSshProxy().getPrivateKey().getKeyFile() == null + || isBlank(datasourceConfiguration + .getSshProxy() + .getPrivateKey() + .getKeyFile() + .getBase64Content())) { invalids.add(DS_MISSING_SSH_KEY_ERROR_MSG); } } @@ -1026,13 +1025,13 @@ public Mono getStructure( @Override public Object substituteValueInInput( - int index, - String binding, - String value, - Object input, - List> insertedParams, - Object... args) - throws AppsmithPluginException { + int index, + String binding, + String value, + Object input, + List> insertedParams, + Object... args) + throws AppsmithPluginException { PreparedStatement preparedStatement = (PreparedStatement) input; HikariProxyConnection connection = (HikariProxyConnection) args[0]; @@ -1044,7 +1043,7 @@ public Object substituteValueInInput( valueType = explicitCastDataTypes.get(index - 1); } else { AppsmithType appsmithType = DataTypeServiceUtils.getAppsmithType( - param.getClientDataType(), value, PostgresSpecificDataTypes.pluginSpecificTypes); + param.getClientDataType(), value, PostgresSpecificDataTypes.pluginSpecificTypes); valueType = appsmithType.type(); } @@ -1110,7 +1109,7 @@ public Object substituteValueInInput( // Find the type of the entries in the list Object firstEntry = arrayListFromInput.get(0); AppsmithType appsmithType = DataTypeServiceUtils.getAppsmithType( - param.getDataTypesOfArrayElements().get(0), String.valueOf(firstEntry)); + param.getDataTypesOfArrayElements().get(0), String.valueOf(firstEntry)); DataType dataType = appsmithType.type(); String typeName = toPostgresqlPrimitiveTypeName(dataType); @@ -1136,9 +1135,9 @@ public Object substituteValueInInput( // the query. Ignore the exception } else { throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, - String.format(PostgresErrorMessages.QUERY_PREPARATION_FAILED_ERROR_MSG, value, binding), - e.getMessage()); + AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, + String.format(PostgresErrorMessages.QUERY_PREPARATION_FAILED_ERROR_MSG, value, binding), + e.getMessage()); } } @@ -1167,7 +1166,7 @@ private static String toPostgresqlPrimitiveTypeName(DataType type) { throw new IllegalArgumentException("Array of Array datatype is not supported."); default: throw new IllegalArgumentException( - "Unable to map the computed data type to primitive Postgresql type"); + "Unable to map the computed data type to primitive Postgresql type"); } } } @@ -1180,8 +1179,8 @@ private static String toPostgresqlPrimitiveTypeName(DataType type) { * @return connection pool */ private static HikariDataSource createConnectionPool( - DatasourceConfiguration datasourceConfiguration, Integer maximumConfigurablePoolSize) - throws AppsmithPluginException { + DatasourceConfiguration datasourceConfiguration, Integer maximumConfigurablePoolSize) + throws AppsmithPluginException { HikariConfig config = new HikariConfig(); config.setDriverClassName(JDBC_DRIVER); @@ -1218,10 +1217,10 @@ private static HikariDataSource createConnectionPool( } else { ConnectionContext connectionContext; connectionContext = getConnectionContext( - datasourceConfiguration, CONNECTION_METHOD_INDEX, DEFAULT_POSTGRES_PORT, HikariDataSource.class); + datasourceConfiguration, CONNECTION_METHOD_INDEX, DEFAULT_POSTGRES_PORT, HikariDataSource.class); hosts.add(LOCALHOST + ":" - + connectionContext.getSshTunnelContext().getServerSocket().getLocalPort()); + + connectionContext.getSshTunnelContext().getServerSocket().getLocalPort()); } urlBuilder.append(String.join(",", hosts)).append("/"); @@ -1249,17 +1248,17 @@ private static HikariDataSource createConnectionPool( * a initial value. */ if (datasourceConfiguration.getConnection() == null - || datasourceConfiguration.getConnection().getSsl() == null - || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { + || datasourceConfiguration.getConnection().getSsl() == null + || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { throw new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, PostgresErrorMessages.SSL_CONFIGURATION_ERROR_MSG); } /* * - By default, the driver configures SSL in the preferred mode. */ SSLDetails.AuthType sslAuthType = - datasourceConfiguration.getConnection().getSsl().getAuthType(); + datasourceConfiguration.getConnection().getSsl().getAuthType(); switch (sslAuthType) { case ALLOW: case PREFER: @@ -1288,39 +1287,39 @@ private static HikariDataSource createConnectionPool( // Common properties for both VERIFY_CA and VERIFY_FULL config.addDataSourceProperty("sslfactory", MutualTLSCertValidatingFactory.class.getName()); config.addDataSourceProperty( - "clientCertString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getClientCACertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "clientCertString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getClientCACertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); config.addDataSourceProperty( - "clientKeyString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getClientKeyCertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "clientKeyString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getClientKeyCertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); config.addDataSourceProperty( - "serverCACertString", - new String( - datasourceConfiguration - .getConnection() - .getSsl() - .getServerCACertificateFile() - .getDecodedContent(), - StandardCharsets.UTF_8)); + "serverCACertString", + new String( + datasourceConfiguration + .getConnection() + .getSsl() + .getServerCACertificateFile() + .getDecodedContent(), + StandardCharsets.UTF_8)); break; default: throw new AppsmithPluginException( - PostgresPluginError.POSTGRES_PLUGIN_ERROR, - String.format(PostgresErrorMessages.INVALID_SSL_OPTION_ERROR_MSG, sslAuthType)); + PostgresPluginError.POSTGRES_PLUGIN_ERROR, + String.format(PostgresErrorMessages.INVALID_SSL_OPTION_ERROR_MSG, sslAuthType)); } String url = urlBuilder.toString(); @@ -1356,16 +1355,15 @@ private static HikariDataSource createConnectionPool( String sqlState = psqlException.getSQLState(); if (PSQLState.CONNECTION_UNABLE_TO_CONNECT.getState().equals(sqlState)) { throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.DS_INVALID_HOSTNAME_AND_PORT_MSG, - psqlException.getMessage()); + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.DS_INVALID_HOSTNAME_AND_PORT_MSG, + psqlException.getMessage()); } } throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, - PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, - cause != null ? cause.getMessage() : e.getMessage()); - + AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, + PostgresErrorMessages.CONNECTION_POOL_CREATION_FAILED_ERROR_MSG, + cause != null ? cause.getMessage() : e.getMessage()); } return datasource;