@@ -206,58 +206,56 @@ class OracleIntegrationSuite extends DockerJDBCIntegrationV2Suite with V2JDBCTes
206206 }
207207 }
208208
209- test(" SPARK-57778: objects that are not selectable as tables are handled gracefully" ) {
210- // Each case: (description, setup DDL, object name in SYSTEM, teardown DDL). Oracle's table
211- // listing surfaces these objects, but they cannot be read as tables.
212- val cases = Seq (
213- (
214- " synonym to a procedure (ORA-04044)" ,
215- Seq (
216- " CREATE PROCEDURE test_proc AS BEGIN NULL; END;" ,
217- " CREATE SYNONYM proc_synonym FOR test_proc" ),
218- " PROC_SYNONYM" ,
219- Seq (" DROP SYNONYM proc_synonym" , " DROP PROCEDURE test_proc" )),
220- (
221- " synonym to a broken function (ORA-04044)" ,
222- Seq (
223- """ CREATE FUNCTION broken_func RETURN NUMBER AS
224- | x NUMBER;
225- |BEGIN
226- | SELECT col INTO x FROM non_existent_table_xyz;
227- | RETURN x;
228- |END;""" .stripMargin,
229- " CREATE SYNONYM func_synonym FOR broken_func" ),
230- " FUNC_SYNONYM" ,
231- Seq (" DROP SYNONYM func_synonym" , " DROP FUNCTION broken_func" )),
232- (
233- " invalid view (ORA-04063)" ,
234- Seq (" CREATE FORCE VIEW invalid_view AS SELECT * FROM non_existent_table_xyz" ),
235- " INVALID_VIEW" ,
236- Seq (" DROP VIEW invalid_view" )))
237-
238- val tableCatalog =
239- spark.sessionState.catalogManager.catalog(catalogName).asInstanceOf [TableCatalog ]
240-
241- cases.foreach { case (desc, setup, objectName, teardown) =>
242- withClue(s " [ $desc] " ) {
243- Using .resource(getConnection()) { conn =>
244- setup.foreach(conn.prepareStatement(_).executeUpdate())
245- }
246- try {
247- // tableExists treats a non-selectable object as non-existent instead of throwing.
248- assert(! tableCatalog.tableExists(Identifier .of(Array (" SYSTEM" ), objectName)))
249-
250- // Reading it surfaces a dedicated, clear error instead of a raw JDBC failure.
251- val e = intercept[AnalysisException ] {
252- sql(s " SELECT * FROM $catalogName.SYSTEM. $objectName" ).collect()
253- }
254- assert(e.getCondition == " JDBC_OBJECT_NOT_SELECTABLE" )
255- assert(e.getMessageParameters.get(" objectName" ).contains(objectName))
256- } finally {
257- Using .resource(getConnection()) { conn =>
258- teardown.foreach(conn.prepareStatement(_).executeUpdate())
259- }
260- }
209+ // An object that Oracle's table listing surfaces but that cannot be read as a table.
210+ // `setup`/`teardown` are the DDL to create/drop it; `objectName` is its name in SYSTEM.
211+ case class NonSelectableObjectCase (setup : Seq [String ], objectName : String , teardown : Seq [String ])
212+
213+ private val nonSelectableObjectCases = Map (
214+ " synonym to a procedure (ORA-04044)" -> NonSelectableObjectCase (
215+ setup = Seq (
216+ " CREATE PROCEDURE test_proc AS BEGIN NULL; END;" ,
217+ " CREATE SYNONYM proc_synonym FOR test_proc" ),
218+ objectName = " PROC_SYNONYM" ,
219+ teardown = Seq (" DROP SYNONYM proc_synonym" , " DROP PROCEDURE test_proc" )),
220+ " synonym to a broken function (ORA-04044)" -> NonSelectableObjectCase (
221+ // Function referencing a missing object -> created INVALID.
222+ setup = Seq (
223+ """ CREATE FUNCTION broken_func RETURN NUMBER AS
224+ | x NUMBER;
225+ |BEGIN
226+ | SELECT col INTO x FROM non_existent_table_xyz;
227+ | RETURN x;
228+ |END;""" .stripMargin,
229+ " CREATE SYNONYM func_synonym FOR broken_func" ),
230+ objectName = " FUNC_SYNONYM" ,
231+ teardown = Seq (" DROP SYNONYM func_synonym" , " DROP FUNCTION broken_func" )),
232+ " invalid view (ORA-04063)" -> NonSelectableObjectCase (
233+ // FORCE-create a view over a missing table -> view is INVALID; SELECT raises ORA-04063.
234+ setup = Seq (" CREATE FORCE VIEW invalid_view AS SELECT * FROM non_existent_table_xyz" ),
235+ objectName = " INVALID_VIEW" ,
236+ teardown = Seq (" DROP VIEW invalid_view" )))
237+
238+ namedGridTest(" SPARK-57778: non-selectable object is handled gracefully" )(
239+ nonSelectableObjectCases) { testCase =>
240+ Using .resource(getConnection()) { conn =>
241+ testCase.setup.foreach(conn.prepareStatement(_).executeUpdate())
242+ }
243+ try {
244+ val tableCatalog =
245+ spark.sessionState.catalogManager.catalog(catalogName).asInstanceOf [TableCatalog ]
246+
247+ // tableExists treats a non-selectable object as non-existent instead of throwing.
248+ assert(! tableCatalog.tableExists(Identifier .of(Array (" SYSTEM" ), testCase.objectName)))
249+
250+ // Reading it surfaces a dedicated, clear error instead of a raw JDBC failure.
251+ val e = intercept[AnalysisException ] {
252+ sql(s " SELECT * FROM $catalogName.SYSTEM. ${testCase.objectName}" ).collect()
253+ }
254+ assert(e.getCondition == " JDBC_OBJECT_NOT_SELECTABLE" )
255+ assert(e.getMessageParameters.get(" objectName" ).contains(testCase.objectName))
256+ } finally {
257+ Using .resource(getConnection()) { conn =>
258+ testCase.teardown.foreach(conn.prepareStatement(_).executeUpdate())
261259 }
262260 }
263261 }
0 commit comments