-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
146daa5
commit 677bc9f
Showing
14 changed files
with
361 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataClient.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.utils.RawSettings | ||
import raw.creds.api.TeradataCredential | ||
import raw.sources.jdbc.api._ | ||
|
||
import java.net.{NoRouteToHostException, SocketTimeoutException, UnknownHostException} | ||
import java.sql.{Connection, DriverManager, ResultSetMetaData, SQLException} | ||
import scala.collection.mutable | ||
import scala.util.control.NonFatal | ||
|
||
class TeradataClient(db: TeradataCredential)(implicit settings: RawSettings) extends JdbcClient { | ||
|
||
Class.forName("com.teradata.jdbc.TeraDriver") | ||
|
||
override val vendor: String = "teradata" | ||
override val connectionString: String = { | ||
// (ctm) Received null parameters while running rawcli tests. | ||
val params: Seq[(String, String)] = Option(db.parameters).getOrElse(Map.empty).toSeq ++ | ||
db.port.map(port => Seq(("DBS_PORT", port.toString))).getOrElse(Seq.empty) | ||
if (params.nonEmpty) { | ||
s"jdbc:$vendor://${db.host}/${params.map(p => s"${p._1}=${p._2}").mkString(",")}" | ||
} else { | ||
s"jdbc:$vendor://${db.host}" | ||
} | ||
} | ||
override val username: Option[String] = db.username | ||
override val password: Option[String] = db.password | ||
|
||
override val hostname: String = db.host | ||
override val database: Option[String] = None | ||
|
||
override def getConnection: Connection = { | ||
wrapSQLException { | ||
// Teradata jdbc connections does not have the setNetworkTimeout | ||
DriverManager.getConnection(connectionString, username.orNull, password.orNull) | ||
} | ||
} | ||
|
||
override def tableMetadata(database: Option[String], maybeSchema: Option[String], table: String): TableMetadata = { | ||
val schema = maybeSchema.get | ||
val conn = getConnection | ||
try { | ||
val query = s"""select top 1 * from "$schema"."$table" ;""" | ||
val stmt = wrapSQLException(conn.prepareStatement(query)) | ||
val meta = wrapSQLException(stmt.getMetaData) | ||
wrapSQLException(stmt.cancel()) | ||
getTableTypeFromResultSetMetadata(meta) | ||
} finally { | ||
conn.close() | ||
} | ||
} | ||
|
||
private def getTableTypeFromResultSetMetadata(res: ResultSetMetaData): TableMetadata = { | ||
val columns = mutable.ListBuffer[TableColumn]() | ||
(1 to wrapSQLException(res.getColumnCount)).foreach { n => | ||
val columnName = wrapSQLException(res.getColumnName(n)) | ||
val columnType = wrapSQLException(res.getColumnType(n)) | ||
val nullability = wrapSQLException(res.isNullable(n)) | ||
columns += TableColumn(columnName, JdbcColumnType(columnType, nullability)) | ||
} | ||
TableMetadata(columns.to, None) | ||
} | ||
|
||
override def wrapSQLException[T](f: => T): T = { | ||
try { | ||
f | ||
} catch { | ||
// TODO (ctm): check Teradata exceptions | ||
case ex: SQLException => ex.getCause match { | ||
case _: UnknownHostException | _: NoRouteToHostException => | ||
// (ctm) In the Python CLI tests, the NoRouteToHostException also happens in the test with bad port | ||
// RuntimeErrorsSourceTeradataTestCase.test_register_bad_port_timeout the host is correct but the port is wrong. | ||
throw new RDBMSUnknownHostException(hostname, ex) | ||
case _: SocketTimeoutException => throw new RDBMSConnectTimeoutException(hostname, ex) | ||
case int: InterruptedException => throw int | ||
case _ => | ||
// Some more codes here (DB2 Universal Messages manual), various databases have varying degrees of compliance | ||
//https://www.ibm.com/support/knowledgecenter/en/SS6NHC/com.ibm.swg.im.dashdb.messages.doc/doc/rdb2stt.html | ||
if (ex.getSQLState != null && ex.getSQLState.startsWith("28")) { | ||
throw new AuthenticationFailedException(ex) | ||
} else if (ex.getSQLState != null && ex.getSQLState.startsWith("08")) { | ||
throw new RDBMSConnectErrorException(hostname, ex) | ||
} else if (ex.getSQLState != null && ex.getSQLState.startsWith("58")) { | ||
throw new JdbcLocationException(s"database system error: ${ex.getMessage}", ex) | ||
} else if (ex.getSQLState != null && ex.getSQLState.startsWith("0A")) { | ||
throw new JdbcLocationException(s"database feature not supported: ${ex.getMessage}", ex) | ||
} else if (ex.getSQLState != null && ex.getSQLState.startsWith("2E")) { | ||
throw new JdbcLocationException(s"database invalid connection name: ${ex.getMessage}", ex) | ||
} else { | ||
logger.warn(s"Unexpected SQL error (code: ${ex.getErrorCode}; state: ${ex.getSQLState}).", ex) | ||
throw new JdbcLocationException(ex.getMessage, ex) | ||
} | ||
} | ||
case ex: JdbcLocationException => throw ex | ||
case NonFatal(t) => | ||
logger.warn("Unexpected SQL error.", t) | ||
throw new JdbcLocationException(s"unexpected database error", t) | ||
} | ||
} | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataClients.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.creds.api.TeradataCredential | ||
import raw.sources.api.{LocationException, SourceContext} | ||
|
||
object TeradataClients { | ||
|
||
def get(dbName: String)(implicit sourceContext: SourceContext): TeradataClient = { | ||
sourceContext.credentialsService.getRDBMSServer(sourceContext.user, dbName) match { | ||
case Some(cred: TeradataCredential) => new TeradataClient(cred)(sourceContext.settings) | ||
case _ => throw new LocationException(s"no credential found for teradata: $dbName") | ||
} | ||
} | ||
|
||
} |
38 changes: 38 additions & 0 deletions
38
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataLocation.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.sources.jdbc.api.JdbcLocation | ||
|
||
import java.io.Closeable | ||
|
||
class TeradataLocation( | ||
cli: TeradataClient, | ||
dbName: String | ||
) extends JdbcLocation(cli, "teradata", dbName) { | ||
|
||
override def rawUri: String = s"teradata:$dbName" | ||
|
||
override def listSchemas(): Iterator[String] with Closeable = { | ||
new Iterator[String] with Closeable { | ||
private val it = cli.listSchemas | ||
|
||
override def hasNext: Boolean = it.hasNext | ||
|
||
override def next(): String = s"teradata:$dbName/${it.next()}" | ||
|
||
override def close(): Unit = it.close() | ||
} | ||
} | ||
|
||
} |
34 changes: 34 additions & 0 deletions
34
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataLocationBuilder.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.client.api.LocationDescription | ||
import raw.sources.api.{LocationException, SourceContext} | ||
import raw.sources.jdbc.api.{JdbcLocation, JdbcLocationBuilder} | ||
|
||
class TeradataLocationBuilder extends JdbcLocationBuilder { | ||
|
||
private val teradataDbRegex = """teradata:(?://)?([^/]+)""".r | ||
|
||
override def schemes: Seq[String] = Seq("teradata") | ||
|
||
override def build(location: LocationDescription)(implicit sourceContext: SourceContext): JdbcLocation = { | ||
location.url match { | ||
case teradataDbRegex(dbName) => | ||
val db = TeradataClients.get(dbName) | ||
new TeradataLocation(db, dbName) | ||
case _ => throw new LocationException("not a teradata database location") | ||
} | ||
} | ||
|
||
} |
40 changes: 40 additions & 0 deletions
40
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataSchema.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import java.io.Closeable | ||
import raw.sources.jdbc.api.JdbcSchemaLocation | ||
|
||
// This might be misleading, this is a Teradata database but works in a similar way to a Oracle schema | ||
// so just remember that like oracle users are also 'databases/schemas' | ||
class TeradataSchema( | ||
cli: TeradataClient, | ||
dbName: String, | ||
schema: String | ||
) extends JdbcSchemaLocation(cli, Some(schema)) { | ||
|
||
override def rawUri: String = s"teradata:$dbName/$schema" | ||
|
||
override def listTables(): Iterator[String] with Closeable = { | ||
new Iterator[String] with Closeable { | ||
private val it = cli.listTables(schema) | ||
|
||
override def hasNext: Boolean = it.hasNext | ||
|
||
override def next(): String = s"teradata:$dbName/$schema/${it.next()}" | ||
|
||
override def close(): Unit = it.close() | ||
} | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataSchemaLocationBuilder.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.client.api.LocationDescription | ||
import raw.sources.api.{LocationException, SourceContext} | ||
import raw.sources.jdbc.api.{JdbcSchemaLocation, JdbcSchemaLocationBuilder} | ||
|
||
class TeradataSchemaLocationBuilder extends JdbcSchemaLocationBuilder { | ||
|
||
private val schemaRegex = """teradata:(?://)?([^/]+)/([^/]+)""".r | ||
|
||
override def schemes: Seq[String] = Seq("teradata") | ||
|
||
override def build(location: LocationDescription)(implicit sourceContext: SourceContext): JdbcSchemaLocation = { | ||
location.url match { | ||
case schemaRegex(dbName, schema) => | ||
val db = TeradataClients.get(dbName) | ||
new TeradataSchema(db, dbName, schema) | ||
case _ => throw new LocationException("not a teradata schema location") | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
sources/src/main/scala/raw/sources/jdbc/teradata/TeradataTable.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright 2023 RAW Labs S.A. | ||
* | ||
* Use of this software is governed by the Business Source License | ||
* included in the file licenses/BSL.txt. | ||
* | ||
* As of the Change Date specified in that file, in accordance with | ||
* the Business Source License, use of this software will be governed | ||
* by the Apache License, Version 2.0, included in the file | ||
* licenses/APL.txt. | ||
*/ | ||
|
||
package raw.sources.jdbc.teradata | ||
|
||
import raw.sources.jdbc.api.JdbcTableLocation | ||
|
||
class TeradataTable( | ||
cli: TeradataClient, | ||
dbName: String, | ||
schema: String, | ||
table: String | ||
) extends JdbcTableLocation(cli, "teradata", dbName, table, Some(schema)) { | ||
|
||
override def rawUri: String = s"teradata:$dbName/$schema/$table" | ||
|
||
} |
Oops, something went wrong.