Skip to content

Commit

Permalink
SQL: Prefer resultSets over exceptions in metadata (#40641)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
costin authored Apr 8, 2019
1 parent 02c3b1d commit ef2d252
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ private JdbcConfiguration(URI baseURI, String u, Properties props) throws JdbcSQ
}

@Override
protected Collection<? extends String> extraOptions() {
protected Collection<String> extraOptions() {
return OPTION_NAMES;
}

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

public DriverPropertyInfo[] driverPropertyInfo() {
List<DriverPropertyInfo> info = new ArrayList<>();
for (String option : OPTION_NAMES) {
String value = null;
DriverPropertyInfo prop = new DriverPropertyInfo(option, value);
for (String option : optionNames()) {
DriverPropertyInfo prop = new DriverPropertyInfo(option, null);
info.add(prop);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ class JdbcConnection implements Connection, JdbcWrapper {
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
*/
JdbcConnection(JdbcConfiguration connectionInfo) throws SQLException {
cfg = connectionInfo;
client = new JdbcHttpClient(connectionInfo);
this(connectionInfo, true);
}

JdbcConnection(JdbcConfiguration connectionInfo, boolean checkServer) throws SQLException {
cfg = connectionInfo;
client = new JdbcHttpClient(connectionInfo, checkServer);
url = connectionInfo.connectionString();
userName = connectionInfo.authUser();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverPropertyInfo;
import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;

import static java.sql.JDBCType.BIGINT;
import static java.sql.JDBCType.BOOLEAN;
import static java.sql.JDBCType.INTEGER;
import static java.sql.JDBCType.SMALLINT;
import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY;
Expand Down Expand Up @@ -209,7 +211,7 @@ public String getStringFunctions() throws SQLException {
@Override
public String getSystemFunctions() throws SQLException {
// https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/system-functions?view=sql-server-2017
return "DATABASE, IFNULL, USER";
return "DATABASE,IFNULL,USER";
}

@Override
Expand Down Expand Up @@ -663,8 +665,7 @@ public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
// https://www.postgresql.org/docs/9.0/static/infoschema-routines.html
@Override
public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException {
return emptySet(con.cfg,
"ROUTINES",
return emptySet(con.cfg, "ROUTINES",
"PROCEDURE_CAT",
"PROCEDURE_SCHEM",
"PROCEDURE_NAME",
Expand All @@ -679,8 +680,7 @@ public ResultSet getProcedures(String catalog, String schemaPattern, String proc
@Override
public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern)
throws SQLException {
return emptySet(con.cfg,
"PARAMETERS",
return emptySet(con.cfg, "ROUTINES_COLUMNS",
"PROCEDURE_CAT",
"PROCEDURE_SCHEM",
"PROCEDURE_NAME",
Expand Down Expand Up @@ -774,14 +774,14 @@ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLExce
public ResultSet getCatalogs() throws SQLException {
// TABLE_CAT is the first column
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%' LIKE ''", 1);
return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data);
return memorySet(con.cfg, columnInfo("CATALOGS", "TABLE_CAT"), data);
}

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

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

@Override
public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException {
throw new SQLFeatureNotSupportedException("Privileges not supported");
return emptySet(con.cfg, "",
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"COLUMN_NAME",
"GRANTOR",
"GRANTEE",
"PRIVILEGE",
"IS_GRANTABLE");
}

@Override
public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
throw new SQLFeatureNotSupportedException("Privileges not supported");
return emptySet(con.cfg, "",
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"GRANTOR",
"GRANTEE",
"PRIVILEGE",
"IS_GRANTABLE");
}

@Override
public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException {
throw new SQLFeatureNotSupportedException("Row identifiers not supported");
return emptySet(con.cfg, "",
"SCOPE", SMALLINT,
"COLUMN_NAME",
"DATA_TYPE", INTEGER,
"TYPE_NAME",
"COLUMN_SIZE", INTEGER,
"BUFFER_LENGTH", INTEGER,
"DECIMAL_DIGITS", SMALLINT,
"PSEUDO_COLUMN", SMALLINT);
}

@Override
public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException {
throw new SQLFeatureNotSupportedException("Version column not supported yet");
return emptySet(con.cfg, "",
"SCOPE", SMALLINT,
"COLUMN_NAME",
"DATA_TYPE", INTEGER,
"TYPE_NAME",
"COLUMN_SIZE", INTEGER,
"BUFFER_LENGTH", INTEGER,
"DECIMAL_DIGITS", SMALLINT,
"PSEUDO_COLUMN", SMALLINT);
}

@Override
public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException {
throw new SQLFeatureNotSupportedException("Primary keys not supported");
return emptySet(con.cfg, "",
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"COLUMN_NAME",
"KEY_SEQ", SMALLINT,
"PK_NAME");
}

@Override
public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException {
throw new SQLFeatureNotSupportedException("Imported keys not supported");
return emptySet(con.cfg, "",
"PKTABLE_CAT",
"PKTABLE_SCHEM",
"PKTABLE_NAME",
"PKCOLUMN_NAME",
"FKTABLE_CAT",
"FKTABLE_SCHEM",
"FKTABLE_NAME",
"FKCOLUMN_NAME",
"KEY_SEQ", SMALLINT,
"UPDATE_RULE ", SMALLINT,
"DELETE_RULE ", SMALLINT,
"FK_NAME",
"PK_NAME ",
"DEFERRABILITY", SMALLINT,
"IS_NULLABLE"
);
}

@Override
public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException {
throw new SQLFeatureNotSupportedException("Exported keys not supported");
return emptySet(con.cfg, "",
"PKTABLE_CAT",
"PKTABLE_SCHEM",
"PKTABLE_NAME",
"PKCOLUMN_NAME",
"FKTABLE_CAT",
"FKTABLE_SCHEM",
"FKTABLE_NAME",
"FKCOLUMN_NAME",
"KEY_SEQ", SMALLINT,
"UPDATE_RULE ", SMALLINT,
"DELETE_RULE ", SMALLINT,
"FK_NAME",
"PK_NAME ",
"DEFERRABILITY", SMALLINT,
"IS_NULLABLE"
);
}

@Override
public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog,
String foreignSchema, String foreignTable) throws SQLException {
throw new SQLFeatureNotSupportedException("Cross reference not supported");
return emptySet(con.cfg, "",
"PKTABLE_CAT",
"PKTABLE_SCHEM",
"PKTABLE_NAME",
"PKCOLUMN_NAME",
"FKTABLE_CAT",
"FKTABLE_SCHEM",
"FKTABLE_NAME",
"FKCOLUMN_NAME",
"KEY_SEQ", SMALLINT,
"UPDATE_RULE ", SMALLINT,
"DELETE_RULE ", SMALLINT,
"FK_NAME",
"PK_NAME ",
"DEFERRABILITY", SMALLINT,
"IS_NULLABLE"
);
}

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

@Override
public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException {
throw new SQLFeatureNotSupportedException("Indicies not supported");
return emptySet(con.cfg, "",
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"NON_UNIQUE", BOOLEAN,
"INDEX_QUALIFIER",
"INDEX_NAME",
"TYPE", SMALLINT,
"ORDINAL_POSITION", SMALLINT,
"COLUMN_NAME",
"ASC_OR_DESC",
"CARDINALITY", BIGINT,
"PAGES", BIGINT,
"FILTER_CONDITION",
"TYPE_NAME"
);
}

@Override
Expand Down Expand Up @@ -908,7 +1008,7 @@ public boolean supportsBatchUpdates() throws SQLException {

@Override
public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"USER_DEFINED_TYPES",
"TYPE_CAT",
"TYPE_SCHEM",
Expand Down Expand Up @@ -946,7 +1046,7 @@ public boolean supportsGetGeneratedKeys() throws SQLException {

@Override
public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"SUPER_TYPES",
"TYPE_CAT",
"TYPE_SCHEM",
Expand All @@ -959,7 +1059,7 @@ public ResultSet getSuperTypes(String catalog, String schemaPattern, String type

@Override
public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
return emptySet(con.cfg, "SUPER_TABLES",
return emptySet(con.cfg, "",
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
Expand All @@ -969,7 +1069,7 @@ public ResultSet getSuperTables(String catalog, String schemaPattern, String tab
@Override
public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern)
throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"ATTRIBUTES",
"TYPE_CAT",
"TYPE_SCHEM",
Expand Down Expand Up @@ -1056,12 +1156,27 @@ public boolean autoCommitFailureClosesAllResultSets() throws SQLException {

@Override
public ResultSet getClientInfoProperties() throws SQLException {
throw new SQLException("Client info not implemented yet");
DriverPropertyInfo[] info = con.cfg.driverPropertyInfo();
Object[][] data = new Object[info.length][];

for (int i = 0; i < data.length; i++) {
data[i] = new Object[4];
data[i][0] = info[i].name;
data[i][1] = Integer.valueOf(-1);
data[i][2] = EMPTY;
data[i][3] = EMPTY;
}

return memorySet(con.cfg, columnInfo("",
"NAME",
"MAX_LEN", INTEGER,
"DEFAULT_VALUE",
"DESCRIPTION"), data);
}

@Override
public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"FUNCTIONS",
"FUNCTION_CAT",
"FUNCTION_SCHEM",
Expand All @@ -1074,7 +1189,7 @@ public ResultSet getFunctions(String catalog, String schemaPattern, String funct
@Override
public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern)
throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"FUNCTION_COLUMNS",
"FUNCTION_CAT",
"FUNCTION_SCHEM",
Expand All @@ -1097,7 +1212,7 @@ public ResultSet getFunctionColumns(String catalog, String schemaPattern, String
@Override
public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {
return emptySet(con.cfg,
return emptySet(con.cfg, "",
"PSEUDO_COLUMNS",
"TABLE_CAT",
"TABLE_SCHEM",
Expand Down Expand Up @@ -1212,7 +1327,7 @@ public Object column(int column) {

@Override
public int batchSize() {
return data.length;
return ObjectUtils.isEmpty(data) ? 0 : data.length;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,23 @@
class JdbcHttpClient {
private final HttpClient httpClient;
private final JdbcConfiguration conCfg;
private final InfoResponse serverInfo;
private InfoResponse serverInfo;

/**
* The SQLException is the only type of Exception the JDBC API can throw (and that the user expects).
* If we remove it, we need to make sure no other types of Exceptions (runtime or otherwise) are thrown
*/
JdbcHttpClient(JdbcConfiguration conCfg) throws SQLException {
this(conCfg, true);
}

JdbcHttpClient(JdbcConfiguration conCfg, boolean checkServer) throws SQLException {
httpClient = new HttpClient(conCfg);
this.conCfg = conCfg;
this.serverInfo = fetchServerInfo();
checkServerVersion();
if (checkServer) {
this.serverInfo = fetchServerInfo();
checkServerVersion();
}
}

boolean ping(long timeoutInMs) throws SQLException {
Expand Down Expand Up @@ -78,6 +84,9 @@ boolean queryClose(String cursor) throws SQLException {
}

InfoResponse serverInfo() throws SQLException {
if (serverInfo == null) {
serverInfo = fetchServerInfo();
}
return serverInfo;
}

Expand Down
Loading

0 comments on commit ef2d252

Please sign in to comment.