Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Mar 21, 2020
2 parents 6a58286 + 50f64dc commit e831c64
Show file tree
Hide file tree
Showing 21 changed files with 768 additions and 259 deletions.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:

#### 4.x Releases

- `4.12.x` Releases - [4.12.0](#4120)
- `4.11.x` Releases - [4.11.0](#4110)
- `4.10.x` Releases - [4.10.0](#4100)
- `4.9.x` Releases - [4.9.0](#490)
Expand Down Expand Up @@ -68,6 +69,37 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
## Next Release
-->

## 4.12.0

Released March 21, 2020 • [diff](https://github.com/groue/GRDB.swift/compare/v4.11.0...v4.12.0)

**New**

- Batch updates now accept nil assignments:

```swift
// UPDATE player SET score = NULL
try Player.updateAll(db, scoreColumn <- nil)
```

- DatabaseMigrator can now recreate the database if a migration has been removed, or renamed (addresses [#725](https://github.com/groue/GRDB.swift/issues/725)).

- DatabaseMigrator querying methods have been enhanced:

```swift
// New
dbQueue.read(migrator.hasCompletedMigrations)
dbQueue.read(migrator.completedMigrations).contains("v2")
dbQueue.read(migrator.completedMigrations).last == "v2"
dbQueue.read(migrator.appliedMigrations)

// Deprecated
migrator.hasCompletedMigrations(in: dbQueue)
migrator.hasCompletedMigrations(in: dbQueue, through: "v2")
migrator.lastCompletedMigration(in: dbQueue) == "v2"
migrator.appliedMigrations(in: dbQueue)
```

## 4.11.0

Released March 2, 2020 &bull; [diff](https://github.com/groue/GRDB.swift/compare/v4.10.0...v4.11.0)
Expand Down
2 changes: 1 addition & 1 deletion Documentation/AppGroupContainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Since several processes may open the database at the same time, protect the crea

// Check here if the database schema is correct, for example
// with a DatabaseMigrator.
if try migrator.hasCompletedMigrations(in: dbPool) {
if try dbPool.read(migrator.hasCompletedMigrations) {
return dbPool
} else {
return nil
Expand Down
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 = '4.11.0'
s.version = '4.12.0'

s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'
Expand Down
10 changes: 10 additions & 0 deletions GRDB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,10 @@
56B964BF1DA51D0A0002DA19 /* FTS5Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B964B81DA51D0A0002DA19 /* FTS5Pattern.swift */; };
56BB6EA91D3009B100A1CA52 /* SchedulingWatchdog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BB6EA81D3009B100A1CA52 /* SchedulingWatchdog.swift */; };
56BB6EAC1D3009B100A1CA52 /* SchedulingWatchdog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BB6EA81D3009B100A1CA52 /* SchedulingWatchdog.swift */; };
56BF2282241781C5003D86EB /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF2281241781C5003D86EB /* UtilsTests.swift */; };
56BF2283241781C5003D86EB /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF2281241781C5003D86EB /* UtilsTests.swift */; };
56BF2284241781C5003D86EB /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF2281241781C5003D86EB /* UtilsTests.swift */; };
56BF2285241781C5003D86EB /* UtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BF2281241781C5003D86EB /* UtilsTests.swift */; };
56C3F7561CF9F12400F6A361 /* DatabaseSavepointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C3F7521CF9F12400F6A361 /* DatabaseSavepointTests.swift */; };
56CC922C201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CC922B201DFFB900CB597E /* DropWhileCursorTests.swift */; };
56CC922D201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56CC922B201DFFB900CB597E /* DropWhileCursorTests.swift */; };
Expand Down Expand Up @@ -1475,6 +1479,7 @@
56B964C11DA521450002DA19 /* FTS5RecordTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5RecordTests.swift; sourceTree = "<group>"; };
56B964C21DA521450002DA19 /* FTS5TableBuilderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5TableBuilderTests.swift; sourceTree = "<group>"; };
56BB6EA81D3009B100A1CA52 /* SchedulingWatchdog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulingWatchdog.swift; sourceTree = "<group>"; };
56BF2281241781C5003D86EB /* UtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UtilsTests.swift; sourceTree = "<group>"; };
56C3F7521CF9F12400F6A361 /* DatabaseSavepointTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseSavepointTests.swift; sourceTree = "<group>"; };
56C48E731C9A9923005DF1D9 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
56C494401ED7255500CC72AF /* GRDBDeploymentTarget.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = GRDBDeploymentTarget.xcconfig; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2039,6 +2044,7 @@
56915781231BF28B00E1D237 /* PoolTests.swift */,
56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */,
56A4CDAF1D4234B200B1A9B9 /* SQLExpressionLiteralTests.swift */,
56BF2281241781C5003D86EB /* UtilsTests.swift */,
);
name = Private;
sourceTree = "<group>";
Expand Down Expand Up @@ -2870,6 +2876,7 @@
5695310C1C9067DC00CF1A2B /* MigrationCrashTests.swift in Sources */,
569531091C9067DC00CF1A2B /* DatabaseQueueCrashTests.swift in Sources */,
5695310D1C9067DC00CF1A2B /* RecordCrashTests.swift in Sources */,
56BF2283241781C5003D86EB /* UtilsTests.swift in Sources */,
5695310E1C9067DC00CF1A2B /* StatementColumnConvertibleCrashTests.swift in Sources */,
5695310A1C9067DC00CF1A2B /* DatabaseValueConvertibleCrashTests.swift in Sources */,
5695310F1C9067DC00CF1A2B /* StatementCrashTests.swift in Sources */,
Expand Down Expand Up @@ -3040,6 +3047,7 @@
56A2384C1B9C74A90082EB20 /* UpdateStatementTests.swift in Sources */,
56A2384E1B9C74A90082EB20 /* DatabaseMigratorTests.swift in Sources */,
563DE4F4231A91E2005081B7 /* DatabaseConfigurationTests.swift in Sources */,
56BF2284241781C5003D86EB /* UtilsTests.swift in Sources */,
56CC9244201E034D00CB597E /* PrefixWhileCursorTests.swift in Sources */,
560714E4227DD0810091BB10 /* AssociationPrefetchingSQLTests.swift in Sources */,
5657AB4A1D108BA9006283EF /* FoundationNSNullTests.swift in Sources */,
Expand Down Expand Up @@ -3246,6 +3254,7 @@
562206061E420EA4005860AC /* DatabasePoolBackupTests.swift in Sources */,
56D496B51D813413008276D7 /* DatabaseCollationTests.swift in Sources */,
563DE4F3231A91E2005081B7 /* DatabaseConfigurationTests.swift in Sources */,
56BF2282241781C5003D86EB /* UtilsTests.swift in Sources */,
56CC9243201E034D00CB597E /* PrefixWhileCursorTests.swift in Sources */,
560714E3227DD0810091BB10 /* AssociationPrefetchingSQLTests.swift in Sources */,
56D496841D813147008276D7 /* SelectStatementTests.swift in Sources */,
Expand Down Expand Up @@ -3586,6 +3595,7 @@
AAA4DD1F230F262000C74B15 /* UpdateStatementTests.swift in Sources */,
AAA4DD20230F262000C74B15 /* DatabaseMigratorTests.swift in Sources */,
563DE4F5231A91E2005081B7 /* DatabaseConfigurationTests.swift in Sources */,
56BF2285241781C5003D86EB /* UtilsTests.swift in Sources */,
AAA4DD21230F262000C74B15 /* PrefixWhileCursorTests.swift in Sources */,
AAA4DD22230F262000C74B15 /* AssociationPrefetchingSQLTests.swift in Sources */,
AAA4DD23230F262000C74B15 /* FoundationNSNullTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion GRDB/Core/Database+Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ extension Database {
// Extract statement arguments
let bindings = try arguments.extractBindings(forStatement: statement, allowingRemainingValues: true)
// unsafe is OK because we just extracted the correct number of arguments
statement.unsafeSetArguments(StatementArguments(bindings))
statement.setUncheckedArguments(StatementArguments(bindings))

// Execute
try statement.execute()
Expand Down
2 changes: 1 addition & 1 deletion GRDB/Core/SQLRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public struct SQLRequest<T>: FetchRequest {
case .internal?:
statement = try db.internalCachedSelectStatement(sql: sql)
}
try statement.setArgumentsWithValidation(context.arguments)
try statement.setArguments(context.arguments)
return PreparedRequest(statement: statement, adapter: adapter)
}
}
Expand Down
117 changes: 107 additions & 10 deletions GRDB/Core/Statement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,19 +128,74 @@ public class Statement {
set {
// Force arguments validity: it is a programmer error to provide
// arguments that do not match the statement.
try! setArgumentsWithValidation(newValue)
try! setArguments(newValue)
}
}

/// Throws a DatabaseError of code SQLITE_ERROR if arguments don't fill all
/// statement arguments.
public func validate(arguments: StatementArguments) throws {
///
/// For example:
///
/// let statement = try db.makeUpdateArgument(sql: """
/// INSERT INTO player (id, name) VALUES (?, ?)
/// """)
///
/// // OK
/// statement.validateArguments([1, "Arthur"])
///
/// // Throws
/// statement.validateArguments([1])
///
/// See also setArguments(_:)
public func validateArguments(_ arguments: StatementArguments) throws {
var arguments = arguments
_ = try arguments.extractBindings(forStatement: self, allowingRemainingValues: false)
}

/// Throws a DatabaseError of code SQLITE_ERROR if arguments don't fill all
/// statement arguments.
///
/// For example:
///
/// let statement = try db.makeUpdateArgument(sql: """
/// INSERT INTO player (id, name) VALUES (?, ?)
/// """)
///
/// // OK
/// statement.validate([1, "Arthur"])
///
/// // Throws
/// statement.validate([1])
///
/// See also setArguments(_:)
@available(*, deprecated, renamed: "validateArguments(_:)")
public func validate(arguments: StatementArguments) throws {
try validateArguments(arguments)
}

/// Set arguments without any validation. Trades safety for performance.
public func unsafeSetArguments(_ arguments: StatementArguments) {
///
/// Only call this method if you are sure input arguments match all expected
/// arguments of the statement.
///
/// For example:
///
/// let statement = try db.makeUpdateArgument(sql: """
/// INSERT INTO player (id, name) VALUES (?, ?)
/// """)
///
/// // OK
/// statement.setUncheckedArguments([1, "Arthur"])
///
/// // OK
/// let arguments: StatementArguments = ... // some untrusted arguments
/// try statement.validateArguments(arguments)
/// statement.setUncheckedArguments(arguments)
///
/// // NOT OK
/// statement.setUncheckedArguments([1])
public func setUncheckedArguments(_ arguments: StatementArguments) {
_arguments = arguments
argumentsNeedValidation = false

Expand All @@ -159,14 +214,56 @@ public class Statement {
}
}

func setArgumentsWithValidation(_ arguments: StatementArguments) throws {
/// Set arguments without any validation. Trades safety for performance.
///
/// Only call this method if you are sure input arguments match all expected
/// arguments of the statement.
///
/// For example:
///
/// let statement = try db.makeUpdateArgument(sql: """
/// INSERT INTO player (id, name) VALUES (?, ?)
/// """)
///
/// // OK
/// statement.unsafeSetArguments([1, "Arthur"])
///
/// // OK
/// let arguments: StatementArguments = ... // some untrusted arguments
/// try statement.validateArguments(arguments)
/// statement.unsafeSetArguments(arguments)
///
/// // NOT OK
/// statement.unsafeSetArguments([1])
///
/// See also setArguments(_:)
@available(*, deprecated, renamed: "setUncheckedArguments(_:)")
public func unsafeSetArguments(_ arguments: StatementArguments) {
setUncheckedArguments(arguments)
}

/// Set the statement arguments, or throws a DatabaseError of code
/// SQLITE_ERROR if arguments don't fill all statement arguments.
///
/// For example:
///
/// let statement = try db.makeUpdateArgument(sql: """
/// INSERT INTO player (id, name) VALUES (?, ?)
/// """)
///
/// // OK
/// try statement.setArguments([1, "Arthur"])
///
/// // Throws an error
/// try statement.setArguments([1])
public func setArguments(_ arguments: StatementArguments) throws {
// Validate
_arguments = arguments
var arguments = arguments
let bindings = try arguments.extractBindings(forStatement: self, allowingRemainingValues: false)
argumentsNeedValidation = false
var consumedArguments = arguments
let bindings = try consumedArguments.extractBindings(forStatement: self, allowingRemainingValues: false)

// Apply
_arguments = arguments
argumentsNeedValidation = false
try reset()
clearBindings()
for (index, dbValue) in zip(Int32(1)..., bindings) {
Expand Down Expand Up @@ -220,9 +317,9 @@ public class Statement {
// Force arguments validity: it is a programmer error to provide
// arguments that do not match the statement.
if let arguments = arguments {
try! setArgumentsWithValidation(arguments)
try! setArguments(arguments)
} else if argumentsNeedValidation {
try! validate(arguments: self.arguments)
try! validateArguments(self.arguments)
}
}
}
Expand Down
Loading

0 comments on commit e831c64

Please sign in to comment.