diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index 563b7cb6c..3311cbc6e 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -46,7 +46,27 @@ jobs: # macos-26 lazy-loads simulator runtimes; -downloadPlatform pulls the runtime # matching Xcode's SDK and is a no-op when it is already present. - name: Install iOS simulator runtime - run: sudo xcodebuild -downloadPlatform iOS + run: | + if sudo xcodebuild -downloadPlatform iOS; then + exit 0 + fi + + echo "::warning::xcodebuild -downloadPlatform iOS failed; checking existing simulator destinations" + destinations="$( + xcodebuild -showdestinations \ + -project "$XCODE_PROJECT" \ + -scheme "$XCODE_SCHEME" \ + -skipPackagePluginValidation 2>&1 || true + )" + echo "$destinations" + + if echo "$destinations" | grep -q "platform:iOS Simulator.*name:iPhone 17 Pro"; then + echo "iPhone 17 Pro simulator destination is already available; continuing." + exit 0 + fi + + echo "::error::No iPhone 17 Pro simulator destination is available after downloadPlatform failed." + exit 70 # Secrets.xcconfig is gitignored. Tests do not need analytics keys, so the # checked-in example template is enough for the project to resolve. diff --git a/CHANGELOG.md b/CHANGELOG.md index fb87083e0..72bbcd6f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- SQL Server table browsing and edits now use the active schema when opening objects outside `dbo`. (#1774) + ## [0.54.0] - 2026-06-30 ### Added diff --git a/Packages/TableProCore/Sources/TableProMSSQLCore/MSSQLSchemaQueries.swift b/Packages/TableProCore/Sources/TableProMSSQLCore/MSSQLSchemaQueries.swift index 2224683bd..a46fdbb33 100644 --- a/Packages/TableProCore/Sources/TableProMSSQLCore/MSSQLSchemaQueries.swift +++ b/Packages/TableProCore/Sources/TableProMSSQLCore/MSSQLSchemaQueries.swift @@ -20,6 +20,16 @@ public enum MSSQLSchemaQueries { return bracketed(schema: schema, table: table) } + public static func resolvedObjectSchema(_ schema: String?, currentSchema: String?) -> String? { + if let schema, !schema.isEmpty { + return schema + } + guard let currentSchema, !currentSchema.isEmpty else { + return nil + } + return currentSchema + } + public static func browse( schema: String?, table: String, diff --git a/Packages/TableProCore/Tests/TableProMSSQLCoreTests/MSSQLSchemaQueriesTests.swift b/Packages/TableProCore/Tests/TableProMSSQLCoreTests/MSSQLSchemaQueriesTests.swift index 43c552ccb..c857b9eff 100644 --- a/Packages/TableProCore/Tests/TableProMSSQLCoreTests/MSSQLSchemaQueriesTests.swift +++ b/Packages/TableProCore/Tests/TableProMSSQLCoreTests/MSSQLSchemaQueriesTests.swift @@ -132,6 +132,20 @@ final class MSSQLSchemaQueriesTests: XCTestCase { XCTAssertEqual(MSSQLSchemaQueries.qualifiedName(schema: "", table: "routeCache"), "[routeCache]") } + func testResolvedObjectSchemaPrefersExplicitSchema() { + XCTAssertEqual(MSSQLSchemaQueries.resolvedObjectSchema("audit", currentSchema: "sales"), "audit") + } + + func testResolvedObjectSchemaFallsBackToCurrentSchema() { + XCTAssertEqual(MSSQLSchemaQueries.resolvedObjectSchema(nil, currentSchema: "sales"), "sales") + XCTAssertEqual(MSSQLSchemaQueries.resolvedObjectSchema("", currentSchema: "sales"), "sales") + } + + func testResolvedObjectSchemaReturnsNilWhenBothSchemasAreBlank() { + XCTAssertNil(MSSQLSchemaQueries.resolvedObjectSchema(nil, currentSchema: nil)) + XCTAssertNil(MSSQLSchemaQueries.resolvedObjectSchema("", currentSchema: "")) + } + func testBrowseQualifiesNonDefaultSchema() { let sql = MSSQLSchemaQueries.browse( schema: "sales", table: "routeCache", @@ -143,6 +157,18 @@ final class MSSQLSchemaQueriesTests: XCTestCase { ) } + func testBrowseUsesResolvedCurrentSchema() { + let schema = MSSQLSchemaQueries.resolvedObjectSchema(nil, currentSchema: "sales") + let sql = MSSQLSchemaQueries.browse( + schema: schema, table: "routeCache", + orderByClause: "ORDER BY (SELECT NULL)", offset: 0, limit: 200 + ) + XCTAssertEqual( + sql, + "SELECT * FROM [sales].[routeCache] ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 200 ROWS ONLY" + ) + } + func testBrowseWithoutSchemaStaysUnqualified() { let sql = MSSQLSchemaQueries.browse( schema: nil, table: "routeCache", diff --git a/Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift b/Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift index 1fcdfdc5a..b84250ece 100644 --- a/Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift +++ b/Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift @@ -330,7 +330,7 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { deletedRowIndices: Set, insertedRowIndices: Set ) -> [(statement: String, parameters: [PluginCellValue])]? { - let qualifiedTable = MSSQLSchemaQueries.qualifiedName(schema: schema, table: table) + let qualifiedTable = MSSQLSchemaQueries.qualifiedName(schema: resolvedObjectSchema(schema), table: table) var statements: [(statement: String, parameters: [PluginCellValue])] = [] var deleteChanges: [PluginRowChange] = [] @@ -598,7 +598,7 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { sortColumns: sortColumns, columns: columns, quoteIdentifier: mssqlQuoteIdentifier ) ?? "ORDER BY (SELECT NULL)" return MSSQLSchemaQueries.browse( - schema: schema, table: table, orderByClause: orderBy, offset: offset, limit: limit + schema: resolvedObjectSchema(schema), table: table, orderByClause: orderBy, offset: offset, limit: limit ) } @@ -640,7 +640,7 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { sortColumns: sortColumns, columns: columns, quoteIdentifier: mssqlQuoteIdentifier ) ?? "ORDER BY (SELECT NULL)" return MSSQLSchemaQueries.filtered( - schema: schema, table: table, whereClause: whereClause, + schema: resolvedObjectSchema(schema), table: table, whereClause: whereClause, orderByClause: orderBy, offset: offset, limit: limit ) } @@ -651,6 +651,10 @@ final class MSSQLPluginDriver: PluginDatabaseDriver, @unchecked Sendable { quoteIdentifier(identifier) } + private func resolvedObjectSchema(_ schema: String?) -> String? { + MSSQLSchemaQueries.resolvedObjectSchema(schema, currentSchema: _currentSchema) + } + private func mssqlEscapeValue(_ value: String) -> String { let trimmed = value.trimmingCharacters(in: .whitespaces) if trimmed.caseInsensitiveCompare("NULL") == .orderedSame { return "NULL" }