Skip to content

Commit 27a0bcd

Browse files
authored
Use sys.all_objects for accurate function and procedure filtering (#2705)
* Updated getFunctions() and getProcedures() to use sys.all_objects to give correct results post filtering * Fixed failure * Removed AzureDW tag from tests added * Removed tests * Updated test cases to address failures in AzureDW * Addressed comments * Update DatabaseMetaDataTest.java * Addressed failures for jdk8 as Set.of is not available in Java 8 * Updated schema, proc, function name to use uuid
1 parent 8f2fde0 commit 27a0bcd

File tree

3 files changed

+415
-22
lines changed

3 files changed

+415
-22
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java

Lines changed: 152 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,38 @@ private void checkClosed() throws SQLServerException {
315315
"AND ic.key_ordinal = 0 " +
316316
"ORDER BY t.name, i.name, ic.key_ordinal";
317317

318+
private static final String GET_FUNCTIONS_QUERY_BASE = "SELECT " +
319+
"DB_NAME() AS FUNCTION_CAT, " +
320+
"SCHEMA_NAME(o.schema_id) AS FUNCTION_SCHEM, " +
321+
"o.name AS FUNCTION_NAME, " +
322+
"-1 AS NUM_INPUT_PARAMS, " +
323+
"-1 AS NUM_OUTPUT_PARAMS, " +
324+
"-1 AS NUM_RESULT_SETS, " +
325+
"CAST(NULL AS VARCHAR(254)) AS REMARKS, " +
326+
"CASE o.type " +
327+
"WHEN 'FN' THEN " + java.sql.DatabaseMetaData.functionReturnsTable + " " +
328+
"WHEN 'IF' THEN " + java.sql.DatabaseMetaData.functionReturnsTable + " " +
329+
"WHEN 'TF' THEN " + java.sql.DatabaseMetaData.functionReturnsTable + " " +
330+
"ELSE " + java.sql.DatabaseMetaData.functionNoNulls + " " +
331+
"END AS FUNCTION_TYPE " +
332+
"FROM sys.all_objects o " +
333+
"WHERE o.type IN (";
334+
335+
private static final String GET_PROCEDURES_QUERY_BASE = "SELECT " +
336+
"DB_NAME() AS PROCEDURE_CAT, " +
337+
"SCHEMA_NAME(o.schema_id) AS PROCEDURE_SCHEM, " +
338+
"o.name AS PROCEDURE_NAME, " +
339+
"-1 AS NUM_INPUT_PARAMS, " +
340+
"-1 AS NUM_OUTPUT_PARAMS, " +
341+
"-1 AS NUM_RESULT_SETS, " +
342+
"CAST(NULL AS VARCHAR(254)) AS REMARKS, " +
343+
"1 AS PROCEDURE_TYPE " +
344+
"FROM sys.all_objects o " +
345+
"WHERE o.type IN (";
346+
347+
private static final String FUNCTIONS_ORDER_BY = " ORDER BY FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_NAME";
348+
private static final String PROCEDURES_ORDER_BY = " ORDER BY PROCEDURE_CAT, PROCEDURE_SCHEM, PROCEDURE_NAME";
349+
318350
// Use LinkedHashMap to force retrieve elements in order they were inserted
319351
/** getColumns columns */
320352
private LinkedHashMap<Integer, String> getColumnsDWColumns = null;
@@ -432,6 +464,81 @@ private SQLServerResultSet getResultSetWithProvidedColumnNames(String catalog, C
432464
return rs;
433465
}
434466

467+
/*
468+
* Builds a filter string for object types based on the provided object types array.
469+
*
470+
* @param objectTypes
471+
* Array of object types to be included in the filter.
472+
*
473+
* @return A string representing the filter for the object types.
474+
*/
475+
private String buildObjectTypesFilter(String[] objectTypes) {
476+
return "'" + String.join("', '", objectTypes) + "')";
477+
}
478+
479+
/*
480+
* Helper method to append catalog, schema, and object name filters to the query
481+
*/
482+
private void appendFiltersAndOrderBy(StringBuilder queryBuilder, String catalog, String schemaPattern,
483+
String objectNamePattern, String orderByClause) {
484+
485+
// Add catalog filter
486+
if (catalog != null) {
487+
queryBuilder.append(" AND DB_NAME() = ?");
488+
}
489+
490+
// Add schema filter
491+
if (schemaPattern != null && !schemaPattern.equals("%")) {
492+
queryBuilder.append(" AND SCHEMA_NAME(o.schema_id) LIKE ?");
493+
}
494+
495+
// Add object name filter
496+
if (objectNamePattern != null && !objectNamePattern.equals("%")) {
497+
queryBuilder.append(" AND o.name LIKE ?");
498+
}
499+
500+
queryBuilder.append(orderByClause);
501+
}
502+
503+
/*
504+
* Executes a query against sys.objects to retrieve metadata about database objects.
505+
* This method is used for both functions and procedures.
506+
*/
507+
private SQLServerResultSet executeSysObjectsQuery(String catalog, String schemaPattern,
508+
String objectNamePattern, String query, String[] columnNames) throws SQLException {
509+
510+
String orgCat = switchCatalogs(catalog);
511+
512+
try {
513+
PreparedStatement pstmt = connection.prepareStatement(query);
514+
pstmt.closeOnCompletion();
515+
int paramIndex = 1;
516+
517+
if (catalog != null) {
518+
pstmt.setString(paramIndex++, escapeIDName(catalog));
519+
}
520+
if (schemaPattern != null && !schemaPattern.equals("%")) {
521+
pstmt.setString(paramIndex++, escapeIDName(schemaPattern));
522+
}
523+
if (objectNamePattern != null && !objectNamePattern.equals("%")) {
524+
pstmt.setString(paramIndex++, escapeIDName(objectNamePattern));
525+
}
526+
527+
SQLServerResultSet rs = (SQLServerResultSet) pstmt.executeQuery();
528+
529+
// Set the column names to match the expected format
530+
for (int i = 0; i < columnNames.length; i++) {
531+
rs.setColumnName(i + 1, columnNames[i]);
532+
}
533+
return rs;
534+
535+
} finally {
536+
if (null != orgCat) {
537+
connection.setCatalog(orgCat);
538+
}
539+
}
540+
}
541+
435542
/**
436543
* Switches the database catalogs.
437544
*
@@ -947,23 +1054,34 @@ public java.sql.ResultSet getFunctions(String catalog, String schemaPattern,
9471054
String functionNamePattern) throws SQLException {
9481055
checkClosed();
9491056

950-
/*
951-
* sp_stored_procedures [ [ @sp_name = ] 'name' ] [ , [ @sp_owner = ] 'schema'] [ , [ @sp_qualifier = ]
952-
* 'qualifier' ] [ , [@fUsePattern = ] 'fUsePattern' ]
953-
*/ // use default ie use pattern matching.
9541057
// catalog cannot be empty in sql server
9551058
if (null != catalog && catalog.length() == 0) {
9561059
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
957-
Object[] msgArgs = {"catalog"};
1060+
Object[] msgArgs = { "catalog" };
9581061
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false);
9591062
}
9601063

961-
String[] arguments = new String[3];
962-
arguments[0] = escapeIDName(functionNamePattern);
963-
arguments[1] = escapeIDName(schemaPattern);
964-
arguments[2] = catalog;
965-
return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments,
966-
getFunctionsColumnNames);
1064+
return getFunctionsFromSysObjects(catalog, schemaPattern, functionNamePattern, getFunctionsColumnNames);
1065+
}
1066+
1067+
/*
1068+
* Returns a ResultSet containing metadata about functions in the database.
1069+
* This method queries the sys.all_objects system view to retrieve
1070+
* information about functions, including their names, schemas, and types.
1071+
*/
1072+
private SQLServerResultSet getFunctionsFromSysObjects(String catalog, String schemaPattern,
1073+
String functionNamePattern, String[] columnNames) throws SQLException {
1074+
1075+
String[] functionTypes = { "FN", "IF", "TF" };
1076+
1077+
// Build the query
1078+
StringBuilder queryBuilder = new StringBuilder(GET_FUNCTIONS_QUERY_BASE);
1079+
queryBuilder.append(buildObjectTypesFilter(functionTypes));
1080+
1081+
appendFiltersAndOrderBy(queryBuilder, catalog, schemaPattern, functionNamePattern, FUNCTIONS_ORDER_BY);
1082+
1083+
return executeSysObjectsQuery(catalog, schemaPattern, functionNamePattern, queryBuilder.toString(),
1084+
columnNames);
9671085
}
9681086

9691087
private static final String[] getFunctionsColumnsColumnNames = { /* 1 */ FUNCTION_CAT, /* 2 */ FUNCTION_SCHEM,
@@ -1540,22 +1658,34 @@ public java.sql.ResultSet getProcedureColumns(String catalog, String schema, Str
15401658

15411659
@Override
15421660
public java.sql.ResultSet getProcedures(String catalog, String schema,
1543-
String proc) throws SQLServerException, SQLTimeoutException {
1661+
String proc) throws SQLServerException, SQLTimeoutException, SQLException {
15441662
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
15451663
loggerExternal.finer(toString() + ACTIVITY_ID + ActivityCorrelator.getCurrent().toString());
15461664
}
15471665

15481666
checkClosed();
1549-
/*
1550-
* sp_stored_procedures [ [ @sp_name = ] 'name' ] [ , [ @sp_owner = ] 'schema'] [ , [ @sp_qualifier = ]
1551-
* 'qualifier' ] [ , [@fUsePattern = ] 'fUsePattern' ]
1552-
*/
1553-
String[] arguments = new String[3];
1554-
arguments[0] = escapeIDName(proc);
1555-
arguments[1] = escapeIDName(schema);
1556-
arguments[2] = catalog;
1557-
return getResultSetWithProvidedColumnNames(catalog, CallableHandles.SP_STORED_PROCEDURES, arguments,
1558-
getProceduresColumnNames);
1667+
1668+
return getProceduresFromSysObjects(catalog, schema, proc, getProceduresColumnNames);
1669+
}
1670+
1671+
/*
1672+
* Returns a ResultSet containing metadata about stored procedures in the database.
1673+
* This method queries the sys.all_objects system view to retrieve information about
1674+
* procedures, including their names, schemas, and types.
1675+
*/
1676+
private SQLServerResultSet getProceduresFromSysObjects(String catalog, String schemaPattern,
1677+
String procedureNamePattern, String[] columnNames) throws SQLException {
1678+
1679+
String[] procedureTypes = { "P", "PC" };
1680+
1681+
// Build the query
1682+
StringBuilder queryBuilder = new StringBuilder(GET_PROCEDURES_QUERY_BASE);
1683+
queryBuilder.append(buildObjectTypesFilter(procedureTypes));
1684+
1685+
appendFiltersAndOrderBy(queryBuilder, catalog, schemaPattern, procedureNamePattern, PROCEDURES_ORDER_BY);
1686+
1687+
return executeSysObjectsQuery(catalog, schemaPattern, procedureNamePattern, queryBuilder.toString(),
1688+
columnNames);
15591689
}
15601690

15611691
@Override

src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,32 @@ public static void dropTableWithSchemaIfExists(String tableNameWithSchema,
425425
"IF OBJECT_ID('" + tableNameWithSchema + "', 'U') IS NOT NULL DROP TABLE " + tableNameWithSchema + ";");
426426
}
427427

428+
/**
429+
* Drops a procedure with schema if it exists.
430+
*
431+
* @param procedureWithSchema
432+
* @param stmt
433+
* @throws SQLException
434+
*/
428435
public static void dropProcedureWithSchemaIfExists(String procedureWithSchema,
429436
java.sql.Statement stmt) throws SQLException {
430437
stmt.execute("IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" + procedureWithSchema
431438
+ "') AND type in (N'P', N'PC')) DROP PROCEDURE " + procedureWithSchema + ";");
432439
}
433440

441+
/**
442+
* Drops a function with schema if it exists.
443+
*
444+
* @param functionWithSchema
445+
* @param stmt
446+
* @throws SQLException
447+
*/
448+
public static void dropFunctionWithSchemaIfExists(String functionWithSchema,
449+
java.sql.Statement stmt) throws SQLException {
450+
stmt.execute("IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" + functionWithSchema
451+
+ "') AND type in (N'FN', N'IF', N'TF')) DROP FUNCTION " + functionWithSchema + ";");
452+
}
453+
434454
/**
435455
* Deletes the contents of a table.
436456
*

0 commit comments

Comments
 (0)