diff --git a/sql/core/pom.xml b/sql/core/pom.xml
index 0855fa13fa79a..c2ed4c079d3cf 100644
--- a/sql/core/pom.xml
+++ b/sql/core/pom.xml
@@ -150,6 +150,11 @@
mssql-jdbc
test
+
+ com.oracle.database.jdbc
+ ojdbc8
+ test
+
org.apache.parquet
parquet-avro
diff --git a/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/ConnectionProvider.scala b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/ConnectionProvider.scala
index 6c310ced37883..ce45be442ccc3 100644
--- a/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/ConnectionProvider.scala
+++ b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/ConnectionProvider.scala
@@ -64,6 +64,10 @@ private[jdbc] object ConnectionProvider extends Logging {
logDebug("MS SQL connection provider found")
new MSSQLConnectionProvider(driver, options)
+ case OracleConnectionProvider.driverClass =>
+ logDebug("Oracle connection provider found")
+ new OracleConnectionProvider(driver, options)
+
case _ =>
throw new IllegalArgumentException(s"Driver ${options.driverClass} does not support " +
"Kerberos authentication")
diff --git a/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProvider.scala b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProvider.scala
new file mode 100644
index 0000000000000..c2b71b35b8128
--- /dev/null
+++ b/sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProvider.scala
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.execution.datasources.jdbc.connection
+
+import java.security.PrivilegedExceptionAction
+import java.sql.{Connection, Driver}
+import java.util.Properties
+
+import org.apache.hadoop.security.UserGroupInformation
+
+import org.apache.spark.sql.execution.datasources.jdbc.JDBCOptions
+
+private[sql] class OracleConnectionProvider(driver: Driver, options: JDBCOptions)
+ extends SecureConnectionProvider(driver, options) {
+ override val appEntry: String = "kprb5module"
+
+ override def getConnection(): Connection = {
+ setAuthenticationConfigIfNeeded()
+ UserGroupInformation.loginUserFromKeytabAndReturnUGI(options.principal, options.keytab).doAs(
+ new PrivilegedExceptionAction[Connection]() {
+ override def run(): Connection = {
+ OracleConnectionProvider.super.getConnection()
+ }
+ }
+ )
+ }
+
+ override def getAdditionalProperties(): Properties = {
+ val result = new Properties()
+ // This prop is needed to turn on kerberos authentication in the JDBC driver.
+ // The possible values can be found in AnoServices public interface
+ // The value is coming from AUTHENTICATION_KERBEROS5 final String in driver version 19.6.0.0
+ result.put("oracle.net.authentication_services", "(KERBEROS5)");
+ result
+ }
+
+ override def setAuthenticationConfigIfNeeded(): Unit = SecurityConfigurationLock.synchronized {
+ val (parent, configEntry) = getConfigWithAppEntry()
+ if (configEntry == null || configEntry.isEmpty) {
+ setAuthenticationConfig(parent)
+ }
+ }
+}
+
+private[sql] object OracleConnectionProvider {
+ val driverClass = "oracle.jdbc.OracleDriver"
+}
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProviderSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProviderSuite.scala
new file mode 100644
index 0000000000000..13cde32ddbe4e
--- /dev/null
+++ b/sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/jdbc/connection/OracleConnectionProviderSuite.scala
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.sql.execution.datasources.jdbc.connection
+
+class OracleConnectionProviderSuite extends ConnectionProviderSuiteBase {
+ test("setAuthenticationConfigIfNeeded must set authentication if not set") {
+ val driver = registerDriver(OracleConnectionProvider.driverClass)
+ val provider = new OracleConnectionProvider(driver,
+ options("jdbc:oracle:thin:@//localhost/xe"))
+
+ testSecureConnectionProvider(provider)
+ }
+}