Skip to content

Commit cbb1229

Browse files
committed
Merge branch 'main' into feat/1220-in-window-tabs
2 parents 8b1ed2a + cc8662b commit cbb1229

33 files changed

Lines changed: 759 additions & 203 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727

2828
### Fixed
2929

30+
- ClickHouse, BigQuery, CloudflareD1, LibSQL, Etcd, and DynamoDB: long-running queries no longer fail at 30 seconds when Settings > Query timeout is set higher. The HTTP transport now uses the configured query timeout plus a 30-second grace, so the server's `max_execution_time` (or equivalent) fires before the client gives up. Setting "No limit" raises the transport ceiling to 1 hour. (#1267)
31+
- AI Chat: DeepSeek V4 thinking content (`reasoning_content`) is now captured during streaming and passed back in subsequent turns, fixing 400 errors when using deepseek-v4-pro or deepseek-v4-flash.
32+
- MongoDB: the connection form now shows a Username field. It was hidden for databases where authentication is optional, so connections to auth-enabled servers saved with no credentials and every query failed with "requires authentication" even though the connection looked healthy.
33+
- SQL import dropped statements when the database executed them slower than the file was parsed, so a re-imported export could fail with errors like "relation does not exist". The parser now waits for each statement to be consumed before reading more. (#1264)
34+
- SQL import ignored the database dialect, so PostgreSQL dumps with dollar-quoted function bodies were split at semicolons inside the body. (#1264)
35+
- SQL export emitted `DROP TABLE` for views, materialized views, and foreign tables, so re-importing failed with "is not a table". It now emits `DROP VIEW`, `DROP MATERIALIZED VIEW`, or `DROP FOREIGN TABLE` to match the object. (#1264)
3036
- iOS: connections, groups, and tags no longer silently disappear after a TestFlight or App Store update. Persistence files are now stored with `.completeFileProtectionUntilFirstUserAuthentication` so they stay readable across background sync runs, load failures are no longer swallowed, and the sync engine refuses to overwrite local data when the load was not actually empty.
3137

3238
### Removed

Plugins/BigQueryDriverPlugin/BigQueryConnection.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ internal final class BigQueryConnection: @unchecked Sendable {
284284
private var _currentJobId: String?
285285
private var _currentJobLocation: String?
286286
private var _queryTimeoutSeconds: Int = 300
287+
private let _queryTimeout = HttpQueryTimeoutBox()
287288
private let location: String?
288289
private static let logger = Logger(subsystem: "com.TablePro", category: "BigQueryConnection")
289290
private static let baseUrl = "https://bigquery.googleapis.com/bigquery/v2"
@@ -294,6 +295,7 @@ internal final class BigQueryConnection: @unchecked Sendable {
294295

295296
func setQueryTimeout(_ seconds: Int) {
296297
lock.withLock { _queryTimeoutSeconds = max(seconds, 30) }
298+
_queryTimeout.set(serverTimeoutSeconds: seconds)
297299
}
298300

299301
init(config: DriverConnectionConfig) {
@@ -306,8 +308,8 @@ internal final class BigQueryConnection: @unchecked Sendable {
306308
let authProvider = try createAuthProvider()
307309

308310
let sessionConfig = URLSessionConfiguration.default
309-
sessionConfig.timeoutIntervalForRequest = 60
310-
sessionConfig.timeoutIntervalForResource = 300
311+
sessionConfig.timeoutIntervalForRequest = HttpQueryTimeout.sessionBootstrapRequestTimeout
312+
sessionConfig.timeoutIntervalForResource = HttpQueryTimeout.sessionResourceTimeout
311313
let urlSession = URLSession(configuration: sessionConfig)
312314

313315
lock.withLock {
@@ -836,8 +838,10 @@ internal final class BigQueryConnection: @unchecked Sendable {
836838
_ request: URLRequest,
837839
session: URLSession
838840
) async throws -> (Data, URLResponse) {
839-
try await withCheckedThrowingContinuation { continuation in
840-
let task = session.dataTask(with: request) { [weak self] data, response, error in
841+
var timedRequest = request
842+
timedRequest.timeoutInterval = _queryTimeout.requestTimeoutInterval
843+
return try await withCheckedThrowingContinuation { continuation in
844+
let task = session.dataTask(with: timedRequest) { [weak self] data, response, error in
841845
self?.lock.withLock { self?._currentTask = nil }
842846
if let error {
843847
if (error as? URLError)?.code == .cancelled {

Plugins/BigQueryDriverPlugin/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
<dict>
55
<key>TableProPluginKitVersion</key>
66
<integer>12</integer>
7+
<key>TableProMinAppVersion</key>
8+
<string>0.42.0</string>
79
</dict>
810
</plist>

Plugins/ClickHouseDriverPlugin/ClickHousePlugin.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
145145
private var currentTask: URLSessionDataTask?
146146
private var _currentDatabase: String
147147
private var _lastQueryId: String?
148+
private let _queryTimeout = HttpQueryTimeoutBox()
148149

149150
private static let logger = Logger(subsystem: "com.TablePro", category: "ClickHousePluginDriver")
150151

@@ -196,8 +197,8 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
196197

197198
func connect() async throws {
198199
let urlConfig = URLSessionConfiguration.default
199-
urlConfig.timeoutIntervalForRequest = 30
200-
urlConfig.timeoutIntervalForResource = 300
200+
urlConfig.timeoutIntervalForRequest = HttpQueryTimeout.sessionBootstrapRequestTimeout
201+
urlConfig.timeoutIntervalForResource = HttpQueryTimeout.sessionResourceTimeout
201202

202203
lock.lock()
203204
if let delegate = ClickHouseTLSDelegate.make(for: config.ssl) {
@@ -732,6 +733,7 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
732733
}
733734

734735
func applyQueryTimeout(_ seconds: Int) async throws {
736+
_queryTimeout.set(serverTimeoutSeconds: seconds)
735737
guard seconds > 0 else { return }
736738
_ = try await execute(query: "SET max_execution_time = \(seconds)")
737739
}
@@ -802,7 +804,8 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
802804
}
803805
lock.unlock()
804806

805-
let request = try buildRequest(query: query, database: database, queryId: queryId)
807+
var request = try buildRequest(query: query, database: database, queryId: queryId)
808+
request.timeoutInterval = _queryTimeout.requestTimeoutInterval
806809
let isSelect = Self.isSelectLikeQuery(query)
807810

808811
let (data, response) = try await withTaskCancellationHandler {
@@ -861,7 +864,8 @@ final class ClickHousePluginDriver: PluginDatabaseDriver, @unchecked Sendable {
861864
}
862865
lock.unlock()
863866

864-
let request = try buildRequest(query: query, database: database, queryId: queryId, params: params)
867+
var request = try buildRequest(query: query, database: database, queryId: queryId, params: params)
868+
request.timeoutInterval = _queryTimeout.requestTimeoutInterval
865869
let isSelect = Self.isSelectLikeQuery(query)
866870

867871
let (data, response) = try await withTaskCancellationHandler {

Plugins/CloudflareD1DriverPlugin/CloudflareD1PluginDriver.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ final class CloudflareD1PluginDriver: PluginDatabaseDriver, @unchecked Sendable
182182
lock.unlock()
183183
}
184184

185+
func applyQueryTimeout(_ seconds: Int) async throws {
186+
lock.lock()
187+
let client = httpClient
188+
lock.unlock()
189+
client?.setQueryTimeout(seconds)
190+
}
191+
185192
// MARK: - Streaming
186193

187194
func streamRows(query: String) -> AsyncThrowingStream<PluginStreamElement, Error> {

Plugins/CloudflareD1DriverPlugin/D1HttpClient.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import Foundation
77
import os
8+
import TableProPluginKit
89

910
// MARK: - API Response Types
1011

@@ -136,6 +137,7 @@ final class D1HttpClient: @unchecked Sendable {
136137
private var _databaseId: String
137138
private var session: URLSession?
138139
private var currentTask: URLSessionDataTask?
140+
private let queryTimeout = HttpQueryTimeoutBox()
139141

140142
var databaseId: String {
141143
get {
@@ -156,10 +158,14 @@ final class D1HttpClient: @unchecked Sendable {
156158
self._databaseId = databaseId
157159
}
158160

161+
func setQueryTimeout(_ seconds: Int) {
162+
queryTimeout.set(serverTimeoutSeconds: seconds)
163+
}
164+
159165
func createSession() {
160166
let config = URLSessionConfiguration.default
161-
config.timeoutIntervalForRequest = 30
162-
config.timeoutIntervalForResource = 300
167+
config.timeoutIntervalForRequest = HttpQueryTimeout.sessionBootstrapRequestTimeout
168+
config.timeoutIntervalForResource = HttpQueryTimeout.sessionResourceTimeout
163169

164170
lock.lock()
165171
session = URLSession(configuration: config)
@@ -307,6 +313,7 @@ final class D1HttpClient: @unchecked Sendable {
307313

308314
var request = URLRequest(url: url)
309315
request.httpMethod = method
316+
request.timeoutInterval = queryTimeout.requestTimeoutInterval
310317
request.setValue("Bearer \(apiToken)", forHTTPHeaderField: "Authorization")
311318
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
312319
request.httpBody = body

Plugins/CloudflareD1DriverPlugin/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
<dict>
55
<key>TableProPluginKitVersion</key>
66
<integer>12</integer>
7+
<key>TableProMinAppVersion</key>
8+
<string>0.42.0</string>
79
</dict>
810
</plist>

Plugins/DynamoDBDriverPlugin/DynamoDBConnection.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ internal final class DynamoDBConnection: @unchecked Sendable {
261261
private var _session: URLSession?
262262
private var _credentials: AWSCredentials?
263263
private var _currentTask: URLSessionDataTask?
264+
private let _queryTimeout = HttpQueryTimeoutBox()
264265
private let region: String
265266
private let endpointUrl: String
266267
private static let logger = Logger(subsystem: "com.TablePro", category: "DynamoDBConnection")
@@ -270,6 +271,10 @@ internal final class DynamoDBConnection: @unchecked Sendable {
270271
lock.withLock { _session }
271272
}
272273

274+
func setQueryTimeout(_ seconds: Int) {
275+
_queryTimeout.set(serverTimeoutSeconds: seconds)
276+
}
277+
273278
init(config: DriverConnectionConfig) {
274279
self.config = config
275280
self.region = config.additionalFields["awsRegion"] ?? "us-east-1"
@@ -298,8 +303,8 @@ internal final class DynamoDBConnection: @unchecked Sendable {
298303
func connect() async throws {
299304
let credentials = try resolveCredentials()
300305
let sessionConfig = URLSessionConfiguration.default
301-
sessionConfig.timeoutIntervalForRequest = 30
302-
sessionConfig.timeoutIntervalForResource = 60
306+
sessionConfig.timeoutIntervalForRequest = HttpQueryTimeout.sessionBootstrapRequestTimeout
307+
sessionConfig.timeoutIntervalForResource = HttpQueryTimeout.sessionResourceTimeout
303308
let urlSession = URLSession(configuration: sessionConfig)
304309

305310
lock.withLock {
@@ -442,6 +447,7 @@ internal final class DynamoDBConnection: @unchecked Sendable {
442447
urlRequest.setValue(hostHeader, forHTTPHeaderField: "Host")
443448

444449
signRequest(&urlRequest, body: bodyData, credentials: credentials)
450+
urlRequest.timeoutInterval = _queryTimeout.requestTimeoutInterval
445451

446452
let (data, response) = try await withCheckedThrowingContinuation {
447453
(continuation: CheckedContinuation<(Data, URLResponse), Error>) in

Plugins/DynamoDBDriverPlugin/DynamoDBPluginDriver.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ internal final class DynamoDBPluginDriver: PluginDatabaseDriver, @unchecked Send
197197
connection?.cancelCurrentRequest()
198198
}
199199

200-
func applyQueryTimeout(_ seconds: Int) async throws {}
200+
func applyQueryTimeout(_ seconds: Int) async throws {
201+
connection?.setQueryTimeout(seconds)
202+
}
201203

202204
// MARK: - Schema Operations
203205

Plugins/DynamoDBDriverPlugin/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
<dict>
55
<key>TableProPluginKitVersion</key>
66
<integer>12</integer>
7+
<key>TableProMinAppVersion</key>
8+
<string>0.42.0</string>
79
</dict>
810
</plist>

0 commit comments

Comments
 (0)