Skip to content

Commit ef2d252

Browse files
authored
SQL: Prefer resultSets over exceptions in metadata (#40641)
Changed the JDBC metadata to return empty results sets instead of throwing SQLFeatureNotSupported as it seems a more safer/compatible approach for consumers. Fix #40533
1 parent 02c3b1d commit ef2d252

File tree

7 files changed

+273
-42
lines changed

7 files changed

+273
-42
lines changed

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcConfiguration.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private JdbcConfiguration(URI baseURI, String u, Properties props) throws JdbcSQ
162162
}
163163

164164
@Override
165-
protected Collection<? extends String> extraOptions() {
165+
protected Collection<String> extraOptions() {
166166
return OPTION_NAMES;
167167
}
168168

@@ -192,9 +192,8 @@ public static boolean canAccept(String url) {
192192

193193
public DriverPropertyInfo[] driverPropertyInfo() {
194194
List<DriverPropertyInfo> info = new ArrayList<>();
195-
for (String option : OPTION_NAMES) {
196-
String value = null;
197-
DriverPropertyInfo prop = new DriverPropertyInfo(option, value);
195+
for (String option : optionNames()) {
196+
DriverPropertyInfo prop = new DriverPropertyInfo(option, null);
198197
info.add(prop);
199198
}
200199

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcConnection.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ class JdbcConnection implements Connection, JdbcWrapper {
4545
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
4646
*/
4747
JdbcConnection(JdbcConfiguration connectionInfo) throws SQLException {
48-
cfg = connectionInfo;
49-
client = new JdbcHttpClient(connectionInfo);
48+
this(connectionInfo, true);
49+
}
5050

51+
JdbcConnection(JdbcConfiguration connectionInfo, boolean checkServer) throws SQLException {
52+
cfg = connectionInfo;
53+
client = new JdbcHttpClient(connectionInfo, checkServer);
5154
url = connectionInfo.connectionString();
5255
userName = connectionInfo.authUser();
5356
}

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java

Lines changed: 141 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010

1111
import java.sql.Connection;
1212
import java.sql.DatabaseMetaData;
13+
import java.sql.DriverPropertyInfo;
1314
import java.sql.JDBCType;
1415
import java.sql.PreparedStatement;
1516
import java.sql.ResultSet;
1617
import java.sql.RowIdLifetime;
1718
import java.sql.SQLException;
18-
import java.sql.SQLFeatureNotSupportedException;
1919
import java.util.ArrayList;
2020
import java.util.List;
2121

22+
import static java.sql.JDBCType.BIGINT;
23+
import static java.sql.JDBCType.BOOLEAN;
2224
import static java.sql.JDBCType.INTEGER;
2325
import static java.sql.JDBCType.SMALLINT;
2426
import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY;
@@ -209,7 +211,7 @@ public String getStringFunctions() throws SQLException {
209211
@Override
210212
public String getSystemFunctions() throws SQLException {
211213
// https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/system-functions?view=sql-server-2017
212-
return "DATABASE, IFNULL, USER";
214+
return "DATABASE,IFNULL,USER";
213215
}
214216

215217
@Override
@@ -663,8 +665,7 @@ public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
663665
// https://www.postgresql.org/docs/9.0/static/infoschema-routines.html
664666
@Override
665667
public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException {
666-
return emptySet(con.cfg,
667-
"ROUTINES",
668+
return emptySet(con.cfg, "ROUTINES",
668669
"PROCEDURE_CAT",
669670
"PROCEDURE_SCHEM",
670671
"PROCEDURE_NAME",
@@ -679,8 +680,7 @@ public ResultSet getProcedures(String catalog, String schemaPattern, String proc
679680
@Override
680681
public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern)
681682
throws SQLException {
682-
return emptySet(con.cfg,
683-
"PARAMETERS",
683+
return emptySet(con.cfg, "ROUTINES_COLUMNS",
684684
"PROCEDURE_CAT",
685685
"PROCEDURE_SCHEM",
686686
"PROCEDURE_NAME",
@@ -774,14 +774,14 @@ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLExce
774774
public ResultSet getCatalogs() throws SQLException {
775775
// TABLE_CAT is the first column
776776
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%' LIKE ''", 1);
777-
return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data);
777+
return memorySet(con.cfg, columnInfo("CATALOGS", "TABLE_CAT"), data);
778778
}
779779

780780
@Override
781781
public ResultSet getTableTypes() throws SQLException {
782782
// TABLE_TYPE (4)
783783
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", 4);
784-
return memorySet(con.cfg, columnInfo("", "TABLE_TYPE"), data);
784+
return memorySet(con.cfg, columnInfo("TABLE_TYPES", "TABLE_TYPE"), data);
785785
}
786786

787787
@Override
@@ -797,43 +797,128 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa
797797

798798
@Override
799799
public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException {
800-
throw new SQLFeatureNotSupportedException("Privileges not supported");
800+
return emptySet(con.cfg, "",
801+
"TABLE_CAT",
802+
"TABLE_SCHEM",
803+
"TABLE_NAME",
804+
"COLUMN_NAME",
805+
"GRANTOR",
806+
"GRANTEE",
807+
"PRIVILEGE",
808+
"IS_GRANTABLE");
801809
}
802810

803811
@Override
804812
public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
805-
throw new SQLFeatureNotSupportedException("Privileges not supported");
813+
return emptySet(con.cfg, "",
814+
"TABLE_CAT",
815+
"TABLE_SCHEM",
816+
"TABLE_NAME",
817+
"GRANTOR",
818+
"GRANTEE",
819+
"PRIVILEGE",
820+
"IS_GRANTABLE");
806821
}
807822

808823
@Override
809824
public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException {
810-
throw new SQLFeatureNotSupportedException("Row identifiers not supported");
825+
return emptySet(con.cfg, "",
826+
"SCOPE", SMALLINT,
827+
"COLUMN_NAME",
828+
"DATA_TYPE", INTEGER,
829+
"TYPE_NAME",
830+
"COLUMN_SIZE", INTEGER,
831+
"BUFFER_LENGTH", INTEGER,
832+
"DECIMAL_DIGITS", SMALLINT,
833+
"PSEUDO_COLUMN", SMALLINT);
811834
}
812835

813836
@Override
814837
public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException {
815-
throw new SQLFeatureNotSupportedException("Version column not supported yet");
838+
return emptySet(con.cfg, "",
839+
"SCOPE", SMALLINT,
840+
"COLUMN_NAME",
841+
"DATA_TYPE", INTEGER,
842+
"TYPE_NAME",
843+
"COLUMN_SIZE", INTEGER,
844+
"BUFFER_LENGTH", INTEGER,
845+
"DECIMAL_DIGITS", SMALLINT,
846+
"PSEUDO_COLUMN", SMALLINT);
816847
}
817848

818849
@Override
819850
public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException {
820-
throw new SQLFeatureNotSupportedException("Primary keys not supported");
851+
return emptySet(con.cfg, "",
852+
"TABLE_CAT",
853+
"TABLE_SCHEM",
854+
"TABLE_NAME",
855+
"COLUMN_NAME",
856+
"KEY_SEQ", SMALLINT,
857+
"PK_NAME");
821858
}
822859

823860
@Override
824861
public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException {
825-
throw new SQLFeatureNotSupportedException("Imported keys not supported");
862+
return emptySet(con.cfg, "",
863+
"PKTABLE_CAT",
864+
"PKTABLE_SCHEM",
865+
"PKTABLE_NAME",
866+
"PKCOLUMN_NAME",
867+
"FKTABLE_CAT",
868+
"FKTABLE_SCHEM",
869+
"FKTABLE_NAME",
870+
"FKCOLUMN_NAME",
871+
"KEY_SEQ", SMALLINT,
872+
"UPDATE_RULE ", SMALLINT,
873+
"DELETE_RULE ", SMALLINT,
874+
"FK_NAME",
875+
"PK_NAME ",
876+
"DEFERRABILITY", SMALLINT,
877+
"IS_NULLABLE"
878+
);
826879
}
827880

828881
@Override
829882
public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException {
830-
throw new SQLFeatureNotSupportedException("Exported keys not supported");
883+
return emptySet(con.cfg, "",
884+
"PKTABLE_CAT",
885+
"PKTABLE_SCHEM",
886+
"PKTABLE_NAME",
887+
"PKCOLUMN_NAME",
888+
"FKTABLE_CAT",
889+
"FKTABLE_SCHEM",
890+
"FKTABLE_NAME",
891+
"FKCOLUMN_NAME",
892+
"KEY_SEQ", SMALLINT,
893+
"UPDATE_RULE ", SMALLINT,
894+
"DELETE_RULE ", SMALLINT,
895+
"FK_NAME",
896+
"PK_NAME ",
897+
"DEFERRABILITY", SMALLINT,
898+
"IS_NULLABLE"
899+
);
831900
}
832901

833902
@Override
834903
public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog,
835904
String foreignSchema, String foreignTable) throws SQLException {
836-
throw new SQLFeatureNotSupportedException("Cross reference not supported");
905+
return emptySet(con.cfg, "",
906+
"PKTABLE_CAT",
907+
"PKTABLE_SCHEM",
908+
"PKTABLE_NAME",
909+
"PKCOLUMN_NAME",
910+
"FKTABLE_CAT",
911+
"FKTABLE_SCHEM",
912+
"FKTABLE_NAME",
913+
"FKCOLUMN_NAME",
914+
"KEY_SEQ", SMALLINT,
915+
"UPDATE_RULE ", SMALLINT,
916+
"DELETE_RULE ", SMALLINT,
917+
"FK_NAME",
918+
"PK_NAME ",
919+
"DEFERRABILITY", SMALLINT,
920+
"IS_NULLABLE"
921+
);
837922
}
838923

839924
@Override
@@ -843,7 +928,22 @@ public ResultSet getTypeInfo() throws SQLException {
843928

844929
@Override
845930
public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException {
846-
throw new SQLFeatureNotSupportedException("Indicies not supported");
931+
return emptySet(con.cfg, "",
932+
"TABLE_CAT",
933+
"TABLE_SCHEM",
934+
"TABLE_NAME",
935+
"NON_UNIQUE", BOOLEAN,
936+
"INDEX_QUALIFIER",
937+
"INDEX_NAME",
938+
"TYPE", SMALLINT,
939+
"ORDINAL_POSITION", SMALLINT,
940+
"COLUMN_NAME",
941+
"ASC_OR_DESC",
942+
"CARDINALITY", BIGINT,
943+
"PAGES", BIGINT,
944+
"FILTER_CONDITION",
945+
"TYPE_NAME"
946+
);
847947
}
848948

849949
@Override
@@ -908,7 +1008,7 @@ public boolean supportsBatchUpdates() throws SQLException {
9081008

9091009
@Override
9101010
public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException {
911-
return emptySet(con.cfg,
1011+
return emptySet(con.cfg, "",
9121012
"USER_DEFINED_TYPES",
9131013
"TYPE_CAT",
9141014
"TYPE_SCHEM",
@@ -946,7 +1046,7 @@ public boolean supportsGetGeneratedKeys() throws SQLException {
9461046

9471047
@Override
9481048
public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException {
949-
return emptySet(con.cfg,
1049+
return emptySet(con.cfg, "",
9501050
"SUPER_TYPES",
9511051
"TYPE_CAT",
9521052
"TYPE_SCHEM",
@@ -959,7 +1059,7 @@ public ResultSet getSuperTypes(String catalog, String schemaPattern, String type
9591059

9601060
@Override
9611061
public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
962-
return emptySet(con.cfg, "SUPER_TABLES",
1062+
return emptySet(con.cfg, "",
9631063
"TABLE_CAT",
9641064
"TABLE_SCHEM",
9651065
"TABLE_NAME",
@@ -969,7 +1069,7 @@ public ResultSet getSuperTables(String catalog, String schemaPattern, String tab
9691069
@Override
9701070
public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern)
9711071
throws SQLException {
972-
return emptySet(con.cfg,
1072+
return emptySet(con.cfg, "",
9731073
"ATTRIBUTES",
9741074
"TYPE_CAT",
9751075
"TYPE_SCHEM",
@@ -1056,12 +1156,27 @@ public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
10561156

10571157
@Override
10581158
public ResultSet getClientInfoProperties() throws SQLException {
1059-
throw new SQLException("Client info not implemented yet");
1159+
DriverPropertyInfo[] info = con.cfg.driverPropertyInfo();
1160+
Object[][] data = new Object[info.length][];
1161+
1162+
for (int i = 0; i < data.length; i++) {
1163+
data[i] = new Object[4];
1164+
data[i][0] = info[i].name;
1165+
data[i][1] = Integer.valueOf(-1);
1166+
data[i][2] = EMPTY;
1167+
data[i][3] = EMPTY;
1168+
}
1169+
1170+
return memorySet(con.cfg, columnInfo("",
1171+
"NAME",
1172+
"MAX_LEN", INTEGER,
1173+
"DEFAULT_VALUE",
1174+
"DESCRIPTION"), data);
10601175
}
10611176

10621177
@Override
10631178
public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException {
1064-
return emptySet(con.cfg,
1179+
return emptySet(con.cfg, "",
10651180
"FUNCTIONS",
10661181
"FUNCTION_CAT",
10671182
"FUNCTION_SCHEM",
@@ -1074,7 +1189,7 @@ public ResultSet getFunctions(String catalog, String schemaPattern, String funct
10741189
@Override
10751190
public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern)
10761191
throws SQLException {
1077-
return emptySet(con.cfg,
1192+
return emptySet(con.cfg, "",
10781193
"FUNCTION_COLUMNS",
10791194
"FUNCTION_CAT",
10801195
"FUNCTION_SCHEM",
@@ -1097,7 +1212,7 @@ public ResultSet getFunctionColumns(String catalog, String schemaPattern, String
10971212
@Override
10981213
public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
10991214
throws SQLException {
1100-
return emptySet(con.cfg,
1215+
return emptySet(con.cfg, "",
11011216
"PSEUDO_COLUMNS",
11021217
"TABLE_CAT",
11031218
"TABLE_SCHEM",
@@ -1212,7 +1327,7 @@ public Object column(int column) {
12121327

12131328
@Override
12141329
public int batchSize() {
1215-
return data.length;
1330+
return ObjectUtils.isEmpty(data) ? 0 : data.length;
12161331
}
12171332

12181333
@Override

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcHttpClient.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,23 @@
3030
class JdbcHttpClient {
3131
private final HttpClient httpClient;
3232
private final JdbcConfiguration conCfg;
33-
private final InfoResponse serverInfo;
33+
private InfoResponse serverInfo;
3434

3535
/**
3636
* The SQLException is the only type of Exception the JDBC API can throw (and that the user expects).
3737
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
3838
*/
3939
JdbcHttpClient(JdbcConfiguration conCfg) throws SQLException {
40+
this(conCfg, true);
41+
}
42+
43+
JdbcHttpClient(JdbcConfiguration conCfg, boolean checkServer) throws SQLException {
4044
httpClient = new HttpClient(conCfg);
4145
this.conCfg = conCfg;
42-
this.serverInfo = fetchServerInfo();
43-
checkServerVersion();
46+
if (checkServer) {
47+
this.serverInfo = fetchServerInfo();
48+
checkServerVersion();
49+
}
4450
}
4551

4652
boolean ping(long timeoutInMs) throws SQLException {
@@ -78,6 +84,9 @@ boolean queryClose(String cursor) throws SQLException {
7884
}
7985

8086
InfoResponse serverInfo() throws SQLException {
87+
if (serverInfo == null) {
88+
serverInfo = fetchServerInfo();
89+
}
8190
return serverInfo;
8291
}
8392

0 commit comments

Comments
 (0)