Skip to content

Commit

Permalink
SQL: Prefer resultSets over exceptions in metadata
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 committed Apr 7, 2019
1 parent 59e9721 commit b314d75
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 b314d75

Please sign in to comment.