Skip to content

Commit

Permalink
[KYUUBI #6815] JDBC Engine supports Oracle
Browse files Browse the repository at this point in the history
# Description

Currently, Kyuubi supports JDBC engines with limited dialects, and I extend the dialects to support Oracle.
* Introduce Oracle support in JDBC Engine
* Adding dialects and tests for Oracle

## Types of changes 🔖

- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Test Plan 🧪

Add tests of `OperationWithOracleEngineSuite`, `OracleOperationSuite`, `OracleSessionSuite` and `OracleStatementSuite`.

---

# Checklist 📝

- [x] This patch was not authored or co-authored using [Generative Tooling](https://www.apache.org/legal/generative-tooling.html)

**Be nice. Be informative.**

Closes #6815 from naive-zhang/jdbc-oracle.

Closes #6815

0ffad5b [native-zhang] add some brief comments on the caller side for the implementation of Oracle JDBC engine
6f469a1 [naive-zhang] Merge branch 'apache:master' into jdbc-oracle
ae70710 [Cheng Pan] Update externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/dialect/OracleSQLDialect.scala
171d06b [native-zhang] use another implementation of transform decimal into int, in engine instead of KyuubiBaseResultSet
7cb74d2 [naive-zhang] Merge branch 'apache:master' into jdbc-oracle
ccd7cae [naive-zhang] remove redundant override methods in OracleSQLDialect.scala
a7da4a6 [naive-zhang] remove redundant impl of getTableTypesOperation in OracleSQLDialect.scala
70b49fc [naive-zhang] Use the single line string if SQL fits in one line, otherwise  write it in a pretty style
e583484 [naive-zhang] Update externals/kyuubi-jdbc-engine/src/main/scala/org/apache/kyuubi/engine/jdbc/dialect/OracleSQLDialect.scala
b33e97a [naive-zhang] remove redundant testcontainers-scala-oracle-xe dependency in pom.xml
4c967b9 [naive-zhang] use gvenzl/oracle-free:23.5-slim with docker-compose for test case
0215e6d [naive-zhang] Merge branch 'apache:master' into jdbc-oracle
d688b47 [naive-zhang] change oracle image into gvenzl/oracle-free:23.5-slim
abf9837 [naive-zhang] fix code style checking error in KyuubiConf.scala
d1e82ed [naive-zhang] fix code style checking error in settings.md
aa2e2e9 [naive-zhang] adjust wired space in OracleSQLDialect
b43cea4 [naive-zhang] add oracle configuration for kyuubi.engine.jdbc.connection.provider
397c1cf [naive-zhang] Merge branch 'apache:master' into jdbc-oracle
2f1b5ed [naive-zhang] add jdbc support for Oracle

Lead-authored-by: naive-zhang <[email protected]>
Co-authored-by: native-zhang <[email protected]>
Co-authored-by: Cheng Pan <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
  • Loading branch information
naive-zhang and pan3793 committed Dec 2, 2024
1 parent 68a6f48 commit eb1b599
Show file tree
Hide file tree
Showing 18 changed files with 664 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/configuration/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ You can configure the Kyuubi properties in `$KYUUBI_HOME/conf/kyuubi-defaults.co
| kyuubi.engine.jdbc.connection.password | &lt;undefined&gt; | The password is used for connecting to server | string | 1.6.0 |
| kyuubi.engine.jdbc.connection.propagateCredential | false | Whether to use the session's user and password to connect to database | boolean | 1.8.0 |
| kyuubi.engine.jdbc.connection.properties || The additional properties are used for connecting to server | seq | 1.6.0 |
| kyuubi.engine.jdbc.connection.provider | &lt;undefined&gt; | A JDBC connection provider plugin for the Kyuubi Server to establish a connection to the JDBC URL. The configuration value should be a subclass of `org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider`. Kyuubi provides the following built-in implementations: <li>doris: For establishing Doris connections.</li> <li>mysql: For establishing MySQL connections.</li> <li>phoenix: For establishing Phoenix connections.</li> <li>postgresql: For establishing PostgreSQL connections.</li><li>starrocks: For establishing StarRocks connections.</li><li>impala: For establishing Impala connections.</li><li>clickhouse: For establishing clickhouse connections.</li> | string | 1.6.0 |
| kyuubi.engine.jdbc.connection.provider | &lt;undefined&gt; | A JDBC connection provider plugin for the Kyuubi Server to establish a connection to the JDBC URL. The configuration value should be a subclass of `org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider`. Kyuubi provides the following built-in implementations: <li>doris: For establishing Doris connections.</li> <li>mysql: For establishing MySQL connections.</li> <li>phoenix: For establishing Phoenix connections.</li> <li>postgresql: For establishing PostgreSQL connections.</li><li>starrocks: For establishing StarRocks connections.</li><li>impala: For establishing Impala connections.</li><li>clickhouse: For establishing clickhouse connections.</li><li>oracle: For establishing oracle connections.</li> | string | 1.6.0 |
| kyuubi.engine.jdbc.connection.url | &lt;undefined&gt; | The server url that engine will connect to | string | 1.6.0 |
| kyuubi.engine.jdbc.connection.user | &lt;undefined&gt; | The user is used for connecting to server | string | 1.6.0 |
| kyuubi.engine.jdbc.deploy.mode | LOCAL | Configures the jdbc engine deploy mode, The value can be 'local', 'yarn'. In local mode, the engine operates on the same node as the KyuubiServer. In YARN mode, the engine runs within the Application Master (AM) container of YARN. | string | 1.10.0 |
Expand Down
6 changes: 6 additions & 0 deletions externals/kyuubi-jdbc-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@
<classifier>http</classifier>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ org.apache.kyuubi.engine.jdbc.clickhouse.ClickHouseConnectionProvider
org.apache.kyuubi.engine.jdbc.doris.DorisConnectionProvider
org.apache.kyuubi.engine.jdbc.impala.ImpalaConnectionProvider
org.apache.kyuubi.engine.jdbc.mysql.MySQLConnectionProvider
org.apache.kyuubi.engine.jdbc.oracle.OracleConnectionProvider
org.apache.kyuubi.engine.jdbc.phoenix.PhoenixConnectionProvider
org.apache.kyuubi.engine.jdbc.postgresql.PostgreSQLConnectionProvider
org.apache.kyuubi.engine.jdbc.starrocks.StarRocksConnectionProvider
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ org.apache.kyuubi.engine.jdbc.dialect.ClickHouseDialect
org.apache.kyuubi.engine.jdbc.dialect.DorisDialect
org.apache.kyuubi.engine.jdbc.dialect.ImpalaDialect
org.apache.kyuubi.engine.jdbc.dialect.MySQLDialect
org.apache.kyuubi.engine.jdbc.dialect.OracleSQLDialect
org.apache.kyuubi.engine.jdbc.dialect.PhoenixDialect
org.apache.kyuubi.engine.jdbc.dialect.PostgreSQLDialect
org.apache.kyuubi.engine.jdbc.dialect.StarRocksDialect
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* 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.kyuubi.engine.jdbc.dialect

import java.sql.{Connection, ResultSet, Statement}
import java.util

import scala.collection.JavaConverters._
import scala.collection.mutable.ArrayBuffer

import org.apache.commons.lang3.StringUtils

import org.apache.kyuubi.engine.jdbc.oracle.{OracleSchemaHelper, OracleTRowSetGenerator}
import org.apache.kyuubi.engine.jdbc.schema.{JdbcTRowSetGenerator, SchemaHelper}
import org.apache.kyuubi.operation.meta.ResultSetSchemaConstant._
import org.apache.kyuubi.session.Session

class OracleSQLDialect extends JdbcDialect {

override def createStatement(connection: Connection, fetchSize: Int): Statement = {
val statement =
connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)
if (connection.getAutoCommit) {
statement.setFetchSize(fetchSize)
}
statement
}

override def getTablesQuery(
catalog: String,
schema: String,
tableName: String,
tableTypes: util.List[String]): String = {
val tTypes =
if (tableTypes == null || tableTypes.isEmpty) {
Set()
} else {
tableTypes.asScala.toSet
}
val query = new StringBuilder(
"SELECT OWNER AS TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE AS TABLE_TYPE FROM ALL_CATALOG")

val filters = ArrayBuffer[String]()
if (StringUtils.isNotBlank(schema)) {
filters += s"OWNER LIKE '$schema'"
}

if (StringUtils.isNotBlank(tableName)) {
filters += s"$TABLE_NAME LIKE '$tableName'"
}

if (tTypes.nonEmpty) {
filters += s"(${
tTypes.map { tableType => s"$TABLE_TYPE = '$tableType'" }
.mkString(" OR ")
})"
}

if (filters.nonEmpty) {
query.append(" WHERE ")
query.append(filters.mkString(" AND "))
}

query.toString()
}

override def getColumnsQuery(
session: Session,
catalogName: String,
schemaName: String,
tableName: String,
columnName: String): String = {
val query = new StringBuilder(
"SELECT OWNER AS TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME FROM ALL_TAB_COLUMNS")

val filters = ArrayBuffer[String]()
if (StringUtils.isNotEmpty(schemaName)) {
filters += s"OWNER LIKE '$schemaName'"
}
if (StringUtils.isNotEmpty(tableName)) {
filters += s"$TABLE_NAME LIKE '$tableName'"
}
if (StringUtils.isNotEmpty(columnName)) {
filters += s"$COLUMN_NAME LIKE '$columnName'"
}

if (filters.nonEmpty) {
query.append(" WHERE ")
query.append(filters.mkString(" AND "))
}

query.toString()
}

override def getTRowSetGenerator(): JdbcTRowSetGenerator = new OracleTRowSetGenerator

override def getSchemaHelper(): SchemaHelper = new OracleSchemaHelper

override def name(): String = "oracle"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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.kyuubi.engine.jdbc.oracle

import org.apache.kyuubi.engine.jdbc.connection.JdbcConnectionProvider

class OracleConnectionProvider extends JdbcConnectionProvider {
override val name: String = classOf[OracleConnectionProvider].getName
// use oracle jdbc class for connection
override val driverClass: String = "oracle.jdbc.OracleDriver"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.kyuubi.engine.jdbc.oracle

import java.sql.Types

import org.apache.kyuubi.engine.jdbc.schema.SchemaHelper
import org.apache.kyuubi.shaded.hive.service.rpc.thrift.TTypeDesc

class OracleSchemaHelper extends SchemaHelper {
override protected def toTTypeDesc(sqlType: Int, precision: Int, scale: Int): TTypeDesc = {
sqlType match {
// case for int, returns NUMERIC type in Oracle JDBC
case Types.NUMERIC if scale == 0 =>
super.toTTypeDesc(Types.INTEGER, precision, scale)
// except for int
case Types.NUMERIC =>
super.toTTypeDesc(Types.DECIMAL, precision, scale)
case _ => super.toTTypeDesc(sqlType, precision, scale)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.kyuubi.engine.jdbc.oracle

import java.sql.Types

import org.apache.kyuubi.engine.jdbc.schema.{Column, DefaultJdbcTRowSetGenerator}
import org.apache.kyuubi.shaded.hive.service.rpc.thrift.{TColumn, TColumnValue}

class OracleTRowSetGenerator extends DefaultJdbcTRowSetGenerator {

override def toIntegerTColumn(rows: Seq[Seq[_]], ordinal: Int): TColumn = {
// define convertFunc in asIntegerTColumn for int type
asIntegerTColumn(rows, ordinal, (rows, ordinal) => Integer.parseInt(rows(ordinal).toString))
}

override def toIntegerTColumnValue(row: Seq[_], ordinal: Int): TColumnValue = {
asIntegerTColumnValue(row, ordinal, x => Integer.parseInt(x.toString))
super.toIntegerTColumnValue(row, ordinal)
}

override def getColumnType(schema: Seq[Column], ordinal: Int): Int = {
schema(ordinal).sqlType match {
// case for int, returns NUMERIC type in Oracle JDBC
case Types.NUMERIC if schema(ordinal).scale == 0 =>
Types.INTEGER
case Types.NUMERIC =>
Types.DECIMAL
case _ => super.getColumnType(schema, ordinal)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ abstract class SchemaHelper {
tColumnDesc
}

private def toTTypeDesc(sqlType: Int, precision: Int, scale: Int): TTypeDesc = {
protected def toTTypeDesc(sqlType: Int, precision: Int, scale: Int): TTypeDesc = {
val typeEntry = new TPrimitiveTypeEntry(toTTypeId(sqlType))
typeEntry.setTypeQualifiers(toTTypeQualifiers(sqlType, precision, scale))
val tTypeDesc = new TTypeDesc()
Expand Down
Loading

0 comments on commit eb1b599

Please sign in to comment.