Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Oct 13, 2024
2 parents d6b50b2 + 73083c4 commit c396fc2
Show file tree
Hide file tree
Showing 18 changed files with 913 additions and 465 deletions.
13 changes: 10 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:

#### 7.x Releases

- `7.0.0` Betas - [7.0.0-beta](#700-beta) - [7.0.0-beta.2](#700-beta2) - [7.0.0-beta.3](#700-beta3) - [7.0.0-beta.4](#700-beta4)
- `7.0.0` Betas - [7.0.0-beta](#700-beta) - [7.0.0-beta.2](#700-beta2) - [7.0.0-beta.3](#700-beta3) - [7.0.0-beta.4](#700-beta4) - [7.0.0-beta.5](#700-beta5)

#### 6.x Releases

Expand Down Expand Up @@ -131,11 +131,18 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:

---

## 7.0.0-beta.5

Released October 13, 2024

- **Fixed**: Fix DatabaseMigrator.hasSchemaChanges failing for readonly connections by [@Jnosh](https://github.com/Jnosh) in [#1653](https://github.com/groue/GRDB.swift/pull/1653)
- **Documentation Update**: Enhance the database schema documentation by [@groue](https://github.com/groue) in [#1652](https://github.com/groue/GRDB.swift/pull/1652)

## 7.0.0-beta.4

Released October 12, 2024

- **New**: Allow applications to handle DatabaseMigrator schema changes [@groue](https://github.com/groue) in [#1651](https://github.com/groue/GRDB.swift/pull/1651)
- **New**: Allow applications to handle DatabaseMigrator schema changes by [@groue](https://github.com/groue) in [#1651](https://github.com/groue/GRDB.swift/pull/1651)

## 7.0.0-beta.3

Expand All @@ -159,7 +166,7 @@ Released September 29, 2024

[Migrating From GRDB 6 to GRDB 7](Documentation/GRDB7MigrationGuide.md) describes in detail how to bump the GRDB version in your application.

The new [Swift Concurrency and GRDB](https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.4/documentation/grdb/swiftconcurrency) guide explains how to best integrate GRDB and Swift Concurrency.
The new [Swift Concurrency and GRDB](https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.5/documentation/grdb/swiftconcurrency) guide explains how to best integrate GRDB and Swift Concurrency.

The [demo app](Documentation/DemoApps/) was rewritten from scratch in a brand new Xcode 16 project.

Expand Down
8 changes: 4 additions & 4 deletions Documentation/GRDB7MigrationGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Do not miss [Swift Concurrency and GRDB], for more recommendations regarding non
- The async sequence returned by [`ValueObservation.values`](https://swiftpackageindex.com/groue/grdb.swiftdocumentation/grdb/valueobservation/values(in:scheduling:bufferingpolicy:)) now iterates on the cooperative thread pool by default. Use .mainActor as the scheduler if you need the previous behavior.

[Migrating to Swift 6]: https://www.swift.org/migration/documentation/migrationguide
[Sharing a Database]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.4/documentation/grdb/databasesharing
[Transaction Kinds]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.4/documentation/grdb/transactions#Transaction-Kinds
[Swift Concurrency and GRDB]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.4/documentation/grdb/swiftconcurrency
[Record]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.4/documentation/grdb/record
[Sharing a Database]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.5/documentation/grdb/databasesharing
[Transaction Kinds]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.5/documentation/grdb/transactions#Transaction-Kinds
[Swift Concurrency and GRDB]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.5/documentation/grdb/swiftconcurrency
[Record]: https://swiftpackageindex.com/groue/grdb.swift/v7.0.0-beta.5/documentation/grdb/record
2 changes: 1 addition & 1 deletion GRDB.swift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'GRDB.swift'
s.version = '7.0.0-beta.4'
s.version = '7.0.0-beta.5'

s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'
Expand Down
159 changes: 138 additions & 21 deletions GRDB/Core/Database+Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ extension Database {

// MARK: - Database Schema

/// Returns the current schema version.
/// Returns the current schema version (`PRAGMA schema_version`).
///
/// For example:
///
/// ```swift
/// let version = try dbQueue.read { db in
/// try db.schemaVersion()
/// }
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/pragma.html#pragma_schema_version>
public func schemaVersion() throws -> Int32 {
Expand Down Expand Up @@ -234,8 +242,19 @@ extension Database {

/// Returns whether a table exists
///
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
/// When `schemaName` is not specified, the result is true if any known
/// schema contains the table.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// if try db.tableExists("player") { ... }
/// if try db.tableExists("player", in: "main") { ... }
/// if try db.tableExists("player", in: "temp") { ... }
/// if try db.tableExists("player", in: "attached") { ... }
/// }
/// ```
///
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs, or
/// if the specified schema does not exist
Expand Down Expand Up @@ -278,8 +297,19 @@ extension Database {
/// Returns whether a view exists, in the main or temp schema, or in an
/// attached database.
///
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
/// When `schemaName` is not specified, the result is true if any known
/// schema contains the table.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// if try db.viewExists("player") { ... }
/// if try db.viewExists("player", in: "main") { ... }
/// if try db.viewExists("player", in: "temp") { ... }
/// if try db.viewExists("player", in: "attached") { ... }
/// }
/// ```
///
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs, or
/// if the specified schema does not exist
Expand All @@ -296,8 +326,19 @@ extension Database {
/// Returns whether a trigger exists, in the main or temp schema, or in an
/// attached database.
///
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
/// When `schemaName` is not specified, the result is true if any known
/// schema contains the table.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// if try db.triggerExists("on_player_update") { ... }
/// if try db.triggerExists("on_player_update", in: "main") { ... }
/// if try db.triggerExists("on_player_update", in: "temp") { ... }
/// if try db.triggerExists("on_player_update", in: "attached") { ... }
/// }
/// ```
///
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs, or
/// if the specified schema does not exist
Expand Down Expand Up @@ -340,6 +381,15 @@ extension Database {
/// table has no explicit primary key, the result is the hidden
/// "rowid" column.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let primaryKey = try db.primaryKey("player")
/// print(primaryKey.columns)
/// }
/// ```
///
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
///
Expand All @@ -359,6 +409,36 @@ extension Database {
throw DatabaseError.noSuchTable(tableName)
}

/// Returns the name of the single-column primary key.
///
/// A fatal error is raised if the primary key has several columns, or
/// if `tableName` is the name of a database view.
func filteringPrimaryKeyColumn(_ tableName: String) throws -> String {
do {
let primaryKey = try primaryKey(tableName)
GRDBPrecondition(
primaryKey.columns.count == 1,
"Filtering by primary key requires a single-column primary key in the table '\(tableName)'")
return primaryKey.columns[0]
} catch let error as DatabaseError {
// Maybe the user tries to filter a view by primary key,
// as in <https://github.com/groue/GRDB.swift/issues/1648>.
// In this case, raise a fatalError because this is a
// programmer error which is very likely to be detected
// during development.
if case .SQLITE_ERROR = error.resultCode,
(try? viewExists(tableName)) == true
{
fatalError("""
Filtering by primary key is not available on the database view '\(tableName)'. \
Use `filter(Column("...") == value)` instead.
""")
} else {
throw error
}
}
}

/// Returns nil if table does not exist
private func primaryKey(_ table: TableIdentifier) throws -> PrimaryKeyInfo? {
SchedulingWatchdog.preconditionValidQueue(self)
Expand Down Expand Up @@ -499,14 +579,26 @@ extension Database {

/// The indexes on table named `tableName`.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let indexes = db.indexes(in: "player")
/// for index in indexes {
/// print(index.columns)
/// }
/// }
/// ```
///
/// Only indexes on columns are returned. Indexes on expressions are
/// not returned.
///
/// SQLite does not define any index for INTEGER PRIMARY KEY columns: this
/// method does not return any index that represents the primary key.
/// SQLite does not define any index for INTEGER PRIMARY KEY columns:
/// this method does not return any index that represents the
/// primary key.
///
/// If you want to know if a set of columns uniquely identifies a row, because
/// the columns contain the primary key or a unique index, use
/// If you want to know if a set of columns uniquely identifies a row,
/// because the columns contain the primary key or a unique index, use
/// ``table(_:hasUniqueKey:)``.
///
/// When `schemaName` is not specified, known schemas are iterated in
Expand Down Expand Up @@ -587,16 +679,19 @@ extension Database {
/// For example:
///
/// ```swift
/// // One table with one primary key (id), and a unique index (a, b):
/// //
/// // > CREATE TABLE t(id INTEGER PRIMARY KEY, a, b, c);
/// // > CREATE UNIQUE INDEX i ON t(a, b);
/// try db.table("t", hasUniqueKey: ["id"]) // true
/// try db.table("t", hasUniqueKey: ["a", "b"]) // true
/// try db.table("t", hasUniqueKey: ["b", "a"]) // true
/// try db.table("t", hasUniqueKey: ["c"]) // false
/// try db.table("t", hasUniqueKey: ["id", "a"]) // true
/// try db.table("t", hasUniqueKey: ["id", "a", "b", "c"]) // true
/// try dbQueue.read { db in
/// // One table with one primary key (id)
/// // and a unique index (a, b):
/// //
/// // > CREATE TABLE t(id INTEGER PRIMARY KEY, a, b, c);
/// // > CREATE UNIQUE INDEX i ON t(a, b);
/// try db.table("t", hasUniqueKey: ["id"]) // true
/// try db.table("t", hasUniqueKey: ["a", "b"]) // true
/// try db.table("t", hasUniqueKey: ["b", "a"]) // true
/// try db.table("t", hasUniqueKey: ["c"]) // false
/// try db.table("t", hasUniqueKey: ["id", "a"]) // true
/// try db.table("t", hasUniqueKey: ["id", "a", "b", "c"]) // true
/// }
/// ```
public func table(
_ tableName: String,
Expand All @@ -610,6 +705,17 @@ extension Database {
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let foreignKeys = try db.foreignKeys(in: "player")
/// for foreignKey in foreignKeys {
/// print(foreignKey.destinationTable)
/// }
/// }
/// ```
///
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs, if
/// the specified schema does not exist, or if no such table or view
/// with this name exists in the main or temp schema, or in an attached
Expand Down Expand Up @@ -831,6 +937,17 @@ extension Database {
/// When `schemaName` is not specified, known schemas are iterated in
/// SQLite resolution order and the first matching result is returned.
///
/// For example:
///
/// ```swift
/// try dbQueue.read { db in
/// let columns = try db.columns(in: "player")
/// for column in columns {
/// print(column.name)
/// }
/// }
/// ```
///
/// - throws: A ``DatabaseError`` whenever an SQLite error occurs, if
/// the specified schema does not exist,or if no such table or view
/// with this name exists in the main or temp schema, or in an attached
Expand Down
Loading

0 comments on commit c396fc2

Please sign in to comment.