From 19cc2393dbe38ebe076b72d4158546e7fd8ba218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 29 May 2026 17:02:40 +0700 Subject: [PATCH 1/3] feat(plugin-oracle): native network encryption, redirect following, clearer listener errors (#483) --- CHANGELOG.md | 9 +++++++++ .../OracleDriverPlugin/OracleConnection.swift | 20 ++++++++++++++++++- Plugins/OracleDriverPlugin/OraclePlugin.swift | 7 +++---- TablePro.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 11 +++++++++- docs/databases/oracle.mdx | 4 +++- 6 files changed, 45 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d643cac0e..576af6419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Oracle connections negotiate Native Network Encryption when the server asks for it, so servers with `SQLNET.ENCRYPTION_SERVER` or `SQLNET.CRYPTO_CHECKSUM_SERVER` set to REQUIRED now connect (AES with a SHA crypto-checksum), matching what SQL Developer and DBeaver do. (#483) +- Oracle connections follow listener redirects, so RAC SCAN listeners, shared server, and load-balanced setups now connect instead of failing during the handshake. (#483) + +### Fixed + +- Oracle connection failures show the listener's actual reason (such as an unknown service name) instead of a generic "server closed the connection" message. (#483) + ## [0.46.0] - 2026-05-28 ### Added diff --git a/Plugins/OracleDriverPlugin/OracleConnection.swift b/Plugins/OracleDriverPlugin/OracleConnection.swift index 943810cfa..38636ad1d 100644 --- a/Plugins/OracleDriverPlugin/OracleConnection.swift +++ b/Plugins/OracleDriverPlugin/OracleConnection.swift @@ -189,7 +189,7 @@ final class OracleConnectionWrapper: @unchecked Sendable { let target = useSID ? "\(self.host):\(self.port):\(identifier)" : "\(self.host):\(self.port)/\(identifier)" osLogger.debug("Connected to Oracle \(target)") } catch let sqlError as OracleSQLError { - let detail = sqlError.serverInfo?.message ?? sqlError.description + let detail = Self.connectFailureDetail(sqlError) osLogger.error("Oracle connection failed: \(detail)") if let sslError = Self.classifySSLError(detail) { throw sslError @@ -236,6 +236,11 @@ final class OracleConnectionWrapper: @unchecked Sendable { if codeDescription.hasPrefix("unsupportedVerifierType") { return .authVerifierUnsupported(flag: codeDescription) } + // A listener that refuses the connection (wrong service name, SID, or listener + // policy) sends its own error text, which is more useful than a generic message. + if error.underlying is OracleListenerRefusedError { + return .connectionFailed + } switch codeDescription { case "uncleanShutdown": return .authConnectionDropped @@ -246,6 +251,19 @@ final class OracleConnectionWrapper: @unchecked Sendable { } } + private static func connectFailureDetail(_ error: OracleSQLError) -> String { + if let refused = error.underlying as? OracleListenerRefusedError { + if let code = refused.code { + return String( + format: String(localized: "The Oracle listener refused the connection (ORA-%d)."), + code + ) + } + return String(localized: "The Oracle listener refused the connection.") + } + return error.serverInfo?.message ?? error.description + } + private static func connectErrorMessage( for category: OracleError.Category, serverDetail: String diff --git a/Plugins/OracleDriverPlugin/OraclePlugin.swift b/Plugins/OracleDriverPlugin/OraclePlugin.swift index a1d47de61..ac5d82727 100644 --- a/Plugins/OracleDriverPlugin/OraclePlugin.swift +++ b/Plugins/OracleDriverPlugin/OraclePlugin.swift @@ -143,10 +143,9 @@ final class OraclePlugin: NSObject, TableProPlugin, DriverPlugin, PluginDiagnost title: String(localized: "Connection Dropped During Handshake"), message: oracleError.message, suggestedActions: [ - String(localized: "The server may require Native Network Encryption, which the pure-Swift driver cannot negotiate."), - String(localized: "Configure the listener for TLS, or set SQLNET.ENCRYPTION_SERVER to ACCEPTED instead of REQUIRED."), - String(localized: "If the same connection works in DBeaver or SQL Developer, they use Oracle's OCI client, which supports Native Network Encryption."), - String(localized: "Check for a firewall or load balancer between the client and server that closes connections mid-handshake.") + String(localized: "Check for a firewall, VPN, or load balancer between you and the server that closes connections mid-handshake."), + String(localized: "If the listener endpoint is TLS-only (TCPS), set the SSL mode in the connection's SSL settings."), + String(localized: "Confirm the host and port reach the database listener directly, not a proxy that resets unknown traffic.") ], supportURL: URL(string: "https://github.com/TableProApp/TablePro/issues/483") ) diff --git a/TablePro.xcodeproj/project.pbxproj b/TablePro.xcodeproj/project.pbxproj index 9c9b74a42..d3fcaddf8 100644 --- a/TablePro.xcodeproj/project.pbxproj +++ b/TablePro.xcodeproj/project.pbxproj @@ -4191,7 +4191,7 @@ repositoryURL = "https://github.com/TableProApp/oracle-nio"; requirement = { kind = revision; - revision = 254b72adfb6b527ac45895b42a38e60ba6c77a1f; + revision = 18ee714233c64cf3442d1f85d61ee55b9ec4e8ab; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4387a72dc..b8fac3f85 100644 --- a/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { "originHash" : "475d5c8ee85aa1d1e11facad23a0ba0c8149e7ea855d30de709ab6e3dd78d3be", "pins" : [ + { + "identity" : "bigint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/attaswift/BigInt.git", + "state" : { + "revision" : "e07e00fa1fd435143a2dcf8b7eec9a7710b2fdfe", + "version" : "5.7.0" + } + }, { "identity" : "codeeditsymbols", "kind" : "remoteSourceControl", @@ -15,7 +24,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/TableProApp/oracle-nio", "state" : { - "revision" : "254b72adfb6b527ac45895b42a38e60ba6c77a1f" + "revision" : "18ee714233c64cf3442d1f85d61ee55b9ec4e8ab" } }, { diff --git a/docs/databases/oracle.mdx b/docs/databases/oracle.mdx index 5222687b4..36eeab2b7 100644 --- a/docs/databases/oracle.mdx +++ b/docs/databases/oracle.mdx @@ -8,9 +8,11 @@ description: Connect to Oracle Database with TablePro TablePro supports Oracle Database 11.1 and later. It speaks the Oracle TNS wire protocol directly in Swift, so no Oracle Instant Client or other external library is required. This covers Oracle Database instances running on-premises, in Docker, or Oracle Cloud. -Oracle 10g and earlier are not supported: they use the older O3LOGON handshake that the pure-Swift driver does not implement. Servers that require Native Network Encryption also need Oracle's OCI client (use TLS instead). +Oracle 10g and earlier are not supported: they use the older O3LOGON handshake that the pure-Swift driver does not implement. +Servers behind a RAC SCAN listener, shared server, or a load balancer are supported: TablePro follows the listener's redirect to the instance that serves the session. Servers that require Native Network Encryption (`SQLNET.ENCRYPTION_SERVER` or `SQLNET.CRYPTO_CHECKSUM_SERVER` set to REQUIRED) are also supported; TablePro negotiates AES encryption with a SHA crypto-checksum, the same as SQL Developer and DBeaver. + ## Install Plugin The Oracle driver is available as a downloadable plugin. When you select Oracle in the connection form, TablePro will prompt you to install it automatically. You can also install it manually: From a4350fd9a21aa756b0d22658355d9a63e6d4d17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 29 May 2026 17:05:17 +0700 Subject: [PATCH 2/3] refactor(plugin-oracle): drop redundant listener-refused branch in connect error classification --- Plugins/OracleDriverPlugin/OracleConnection.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Plugins/OracleDriverPlugin/OracleConnection.swift b/Plugins/OracleDriverPlugin/OracleConnection.swift index 38636ad1d..53379e965 100644 --- a/Plugins/OracleDriverPlugin/OracleConnection.swift +++ b/Plugins/OracleDriverPlugin/OracleConnection.swift @@ -236,11 +236,6 @@ final class OracleConnectionWrapper: @unchecked Sendable { if codeDescription.hasPrefix("unsupportedVerifierType") { return .authVerifierUnsupported(flag: codeDescription) } - // A listener that refuses the connection (wrong service name, SID, or listener - // policy) sends its own error text, which is more useful than a generic message. - if error.underlying is OracleListenerRefusedError { - return .connectionFailed - } switch codeDescription { case "uncleanShutdown": return .authConnectionDropped From 9a5c816788f0a5b17f33e031700160f76bc1f62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 29 May 2026 17:15:15 +0700 Subject: [PATCH 3/3] build(plugin-oracle): bump oracle-nio pin to include native encryption cleanup --- TablePro.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TablePro.xcodeproj/project.pbxproj b/TablePro.xcodeproj/project.pbxproj index d3fcaddf8..d0d3650b7 100644 --- a/TablePro.xcodeproj/project.pbxproj +++ b/TablePro.xcodeproj/project.pbxproj @@ -4191,7 +4191,7 @@ repositoryURL = "https://github.com/TableProApp/oracle-nio"; requirement = { kind = revision; - revision = 18ee714233c64cf3442d1f85d61ee55b9ec4e8ab; + revision = 04a4e5967bf4d96cadf66081ea5a22133fe403ea; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b8fac3f85..82e54457b 100644 --- a/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,7 +24,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/TableProApp/oracle-nio", "state" : { - "revision" : "18ee714233c64cf3442d1f85d61ee55b9ec4e8ab" + "revision" : "04a4e5967bf4d96cadf66081ea5a22133fe403ea" } }, {