diff --git a/.travis.yml b/.travis.yml index d5cabb5..930dd00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: objective-c os: osx -osx_image: xcode11 +osx_image: xcode11.6 xcode_project: Restructure.xcodeproj @@ -11,22 +11,22 @@ xcode_scheme: - Restructure tvOS xcode_sdk: - - iphonesimulator13.0 + - iphonesimulator13.6 - macosx10.15 - - appletvsimulator13.0 + - appletvsimulator13.4 matrix: exclude: - xcode_scheme: Restructure iOS xcode_sdk: macosx10.15 - xcode_scheme: Restructure iOS - xcode_sdk: appletvsimulator13.0 + xcode_sdk: appletvsimulator13.4 - xcode_scheme: Restructure macOS - xcode_sdk: iphonesimulator13.0 + xcode_sdk: iphonesimulator13.6 - xcode_scheme: Restructure macOS - xcode_sdk: appletvsimulator13.0 + xcode_sdk: appletvsimulator13.4 - xcode_scheme: Restructure tvOS - xcode_sdk: iphonesimulator13.0 + xcode_sdk: iphonesimulator13.6 - xcode_scheme: Restructure tvOS xcode_sdk: macosx10.15 diff --git a/CHANGELOG.md b/CHANGELOG.md index d9ea57a..68042f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.1.0 2020-08-16 +### Added +- `sqliteVersion` fetches the underlying SQLite version string. +- Dynamic member lookup is enabled for `Row`, allowing for direct access to values via property notation. + +### Removed +- `JournalMode.off` has been removed because of defensive configs. + ## 2.0.0 - 2019-09-12 ### Added - `AutoVacuum` dictates the automatic vacuuming mode. diff --git a/LICENSE b/LICENSE index d69332f..d571c84 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019 Stephen H. Gerstacker +Copyright (c) 2020 Stephen H. Gerstacker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d91c929..e18bdfe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Restructure -[![Build Status](https://travis-ci.org/stack/Restructure.svg?branch=swift-5.1)](https://travis-ci.org/stack/Restructure) -![Swift 5.1](https://img.shields.io/badge/Swift-5.0-orange.svg) +[![Build Status](https://travis-ci.org/stack/Restructure.svg)](https://travis-ci.org/stack/Restructure) +![Swift 5.1](https://img.shields.io/badge/Swift-5.1-orange.svg) Restructure is a wrapper library for [SQLite](https://sqlite.org/index.html) for iOS, macOS, and tvOS. It's fairly opinionated, as in, it does exactly what I @@ -185,6 +185,23 @@ for row in statement } } ``` + +### Rows support Dynamic Member Lookup + +You can extract data from a row with direct property access using Dynamic Member Lookup. + +```swift +let statement = try! restructure.prepare(query: "SELECT a, b, c, d, e FROM foo LIMIT 1") + +guard case let .row(row) = statement.step() else { + /// Handle error +} + +let a: Int = row.a +let b: String = row.b +let c: Double = row.c +``` + ### Migrations The Restructure object has a `userVersion` property to track the version of a diff --git a/Sources/Restructure/ArrayStrategy.swift b/Sources/Restructure/ArrayStrategy.swift index 4834236..8742dd8 100644 --- a/Sources/Restructure/ArrayStrategy.swift +++ b/Sources/Restructure/ArrayStrategy.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/10/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/AutoVacuum.swift b/Sources/Restructure/AutoVacuum.swift index e3d58f9..e90adaf 100644 --- a/Sources/Restructure/AutoVacuum.swift +++ b/Sources/Restructure/AutoVacuum.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 8/12/19. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/Date+Julian.swift b/Sources/Restructure/Date+Julian.swift index 5c016c2..3eb89fc 100644 --- a/Sources/Restructure/Date+Julian.swift +++ b/Sources/Restructure/Date+Julian.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/DateStrategy.swift b/Sources/Restructure/DateStrategy.swift index a951b2e..ff7f4c1 100644 --- a/Sources/Restructure/DateStrategy.swift +++ b/Sources/Restructure/DateStrategy.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/JournalMode.swift b/Sources/Restructure/JournalMode.swift index ed749ef..30cab54 100644 --- a/Sources/Restructure/JournalMode.swift +++ b/Sources/Restructure/JournalMode.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 8/12/19. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation @@ -20,8 +20,6 @@ public enum JournalMode: CaseIterable, PragmaRepresentable { case memory /// A write-ahead log is used as opposed to a rollback journal. This is the default for a file-backed database. case wal - /// No rollback journal is used. - case off /// Create a `JournalMode` from a SQlite representation static func from(value: String) -> JournalMode { @@ -36,8 +34,6 @@ public enum JournalMode: CaseIterable, PragmaRepresentable { return .memory case "WAL": return .wal - case "OFF": - return .off default: fatalError("Unsupported JournalMode string: \(value)") } @@ -56,8 +52,6 @@ public enum JournalMode: CaseIterable, PragmaRepresentable { return "MEMORY" case .wal: return "WAL" - case .off: - return "OFF" } } } diff --git a/Sources/Restructure/PragmaRepresentable.swift b/Sources/Restructure/PragmaRepresentable.swift index bfdea6b..c12aac0 100644 --- a/Sources/Restructure/PragmaRepresentable.swift +++ b/Sources/Restructure/PragmaRepresentable.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 8/12/19. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/Restructure.swift b/Sources/Restructure/Restructure.swift index f5fce80..c87554a 100644 --- a/Sources/Restructure/Restructure.swift +++ b/Sources/Restructure/Restructure.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation @@ -67,6 +67,23 @@ public class Restructure { get { get(pragma: "user_version") } set { set(pragma: "user_version", value: newValue) } } + + /// The underlying SQLite version { + public var sqliteVersion: String { + do { + let statement = try prepare(query: "SELECT sqlite_version()") + let result = statement.step() + + switch result { + case let .row(row): + return row[0] + default: + fatalError("Failed to fetch sqlite_version from a result: \(result)") + } + } catch { + fatalError("Failed to fetch sqlite_version: \(error)") + } + } // MARK: - Initialization diff --git a/Sources/Restructure/RestructureError.swift b/Sources/Restructure/RestructureError.swift index 812d483..0a23e03 100644 --- a/Sources/Restructure/RestructureError.swift +++ b/Sources/Restructure/RestructureError.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/Row.swift b/Sources/Restructure/Row.swift index 0318708..179e89d 100644 --- a/Sources/Restructure/Row.swift +++ b/Sources/Restructure/Row.swift @@ -3,13 +3,14 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation import SQLite3 /// A row result from a `Statement`. +@dynamicMemberLookup public class Row { // MARK: - Properties @@ -78,6 +79,15 @@ public class Row { return self[Int(index)] } + + /// Returns the non-null `Structurable` value via dynamic look up + /// + /// - Parameter dynamicMember: The dynamic member to look for. + /// + /// - Returns: The `Structurable` value associated with the key, transformated by the underlying SQLite API if necessary. + public subscript(dynamicMember key: String) -> T { + return self[key] + } ///Returns the nullable `Structurable` value for the given index value. /// @@ -106,4 +116,13 @@ public class Row { return self[Int(index)] } + + /// Returns the nullable `Structurable` value via dynamic look up + /// + /// - Parameter dynamicMember: The dynamic member to look for. + /// + /// - Returns: The `Structurable` value associated with the key, transformated by the underlying SQLite API if necessary. + public subscript(dynamicMember key: String) -> T? { + return self[key] + } } diff --git a/Sources/Restructure/RowDecoder.swift b/Sources/Restructure/RowDecoder.swift index a69c716..65bf663 100644 --- a/Sources/Restructure/RowDecoder.swift +++ b/Sources/Restructure/RowDecoder.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/15/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/SecureDelete.swift b/Sources/Restructure/SecureDelete.swift index e7a11ee..af0472e 100644 --- a/Sources/Restructure/SecureDelete.swift +++ b/Sources/Restructure/SecureDelete.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 8/12/19. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/Statement.swift b/Sources/Restructure/Statement.swift index 1b49283..ffe5d86 100644 --- a/Sources/Restructure/Statement.swift +++ b/Sources/Restructure/Statement.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/StatementEncoder.swift b/Sources/Restructure/StatementEncoder.swift index aba167f..6c02ee3 100644 --- a/Sources/Restructure/StatementEncoder.swift +++ b/Sources/Restructure/StatementEncoder.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/10/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/StepResult.swift b/Sources/Restructure/StepResult.swift index 34ade3c..3755aa3 100644 --- a/Sources/Restructure/StepResult.swift +++ b/Sources/Restructure/StepResult.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/String+SQLite3.swift b/Sources/Restructure/String+SQLite3.swift index daef1e9..734e261 100644 --- a/Sources/Restructure/String+SQLite3.swift +++ b/Sources/Restructure/String+SQLite3.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Sources/Restructure/Structurable.swift b/Sources/Restructure/Structurable.swift index bced671..7f7c1a6 100644 --- a/Sources/Restructure/Structurable.swift +++ b/Sources/Restructure/Structurable.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import Foundation diff --git a/Tests/RestructureTests/RestructureInitializationTests.swift b/Tests/RestructureTests/RestructureInitializationTests.swift index f91f69d..974969d 100644 --- a/Tests/RestructureTests/RestructureInitializationTests.swift +++ b/Tests/RestructureTests/RestructureInitializationTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest diff --git a/Tests/RestructureTests/RestructureTests.swift b/Tests/RestructureTests/RestructureTests.swift index 6e7034f..6e285fb 100644 --- a/Tests/RestructureTests/RestructureTests.swift +++ b/Tests/RestructureTests/RestructureTests.swift @@ -3,7 +3,7 @@ // Restructure macOS Tests // // Created by Stephen H. Gerstacker on 11/3/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest @@ -71,6 +71,13 @@ class RestructureTests: XCTestCase { XCTAssertNoThrow(try restructure.execute(query: "CREATE TABLE foo (a INTEGER PRIMARY KEY AUTOINCREMENT, b INT); INSERT INTO foo (b) VALUES(42);")) XCTAssertGreaterThan(restructure.lastInsertedId, 0) } + + // MARK: - SQLite Version Tests + + func testSQLiteVersionExists() { + let version = restructure.sqliteVersion + XCTAssertFalse(version.isEmpty) + } // MARK: - Migration Tests diff --git a/Tests/RestructureTests/RowDecoderTests.swift b/Tests/RestructureTests/RowDecoderTests.swift index c37bfa3..b9c0db8 100644 --- a/Tests/RestructureTests/RowDecoderTests.swift +++ b/Tests/RestructureTests/RowDecoderTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/30/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest diff --git a/Tests/RestructureTests/RowTests.swift b/Tests/RestructureTests/RowTests.swift index b703730..fcc2ffa 100644 --- a/Tests/RestructureTests/RowTests.swift +++ b/Tests/RestructureTests/RowTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest @@ -819,4 +819,63 @@ class RowTests: XCTestCase { XCTAssertNotNil(data2) XCTAssertEqual(data1, data2!) } + + + // MARK: - Dynamic Member Tests + + func testDynamicMembers() { + try! restructure.execute(query: "CREATE TABLE foo (a INT, t TEXT)") + let insertStatement = try! restructure.prepare(query: "INSERT INTO foo (a, t) VALUES (:a, :t)") + + insertStatement.bind(value: 0, for: "a") + insertStatement.bind(value: "Text 0", for: "t") + _ = insertStatement.step() + + insertStatement.reset() + + insertStatement.bind(value: 1, for: "a") + insertStatement.bind(value: "Text 1", for: "t") + _ = insertStatement.step() + + insertStatement.reset() + + insertStatement.bind(value: 2, for: "a") + insertStatement.bind(value: "Text 2", for: "t") + _ = insertStatement.step() + + let selectStatement = try! restructure.prepare(query: "SELECT a, t FROM foo ORDER BY t") + + guard case let .row(row1) = selectStatement.step() else { + XCTFail("Failed to fetch row") + return + } + + let row1Int: Int = row1.a + let row1String: String = row1.t + + XCTAssertEqual(row1Int, 0) + XCTAssertEqual(row1String, "Text 0") + + guard case let .row(row2) = selectStatement.step() else { + XCTFail("Failed to fetch row") + return + } + + let row2Int: Int = row2.a + let row2String: String = row2.t + + XCTAssertEqual(row2Int, 1) + XCTAssertEqual(row2String, "Text 1") + + guard case let .row(row3) = selectStatement.step() else { + XCTFail("Failed to fetch row") + return + } + + let row3Int: Int = row3.a + let row3String: String = row3.t + + XCTAssertEqual(row3Int, 2) + XCTAssertEqual(row3String, "Text 2") + } } diff --git a/Tests/RestructureTests/StatementEncoderTests.swift b/Tests/RestructureTests/StatementEncoderTests.swift index 4712a55..08aa426 100644 --- a/Tests/RestructureTests/StatementEncoderTests.swift +++ b/Tests/RestructureTests/StatementEncoderTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/10/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest diff --git a/Tests/RestructureTests/StatementSequenceTests.swift b/Tests/RestructureTests/StatementSequenceTests.swift index 6562c5f..d078e29 100644 --- a/Tests/RestructureTests/StatementSequenceTests.swift +++ b/Tests/RestructureTests/StatementSequenceTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/6/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest diff --git a/Tests/RestructureTests/StatementTests.swift b/Tests/RestructureTests/StatementTests.swift index 3d40496..5949ef0 100644 --- a/Tests/RestructureTests/StatementTests.swift +++ b/Tests/RestructureTests/StatementTests.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest @@ -70,7 +70,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 42.1, for: "C") insertStatement.bind(value: 42, for: "D") - let data = Data(bytes: UnsafePointer([ 0x41, 0x42, 0x43 ] as [UInt8]), count: 3) + let data = Data(bytes: [0x41, 0x42, 0x43], count: 3) insertStatement.bind(value: data, for: "E") XCTAssertNoThrow(try insertStatement.perform()) @@ -103,7 +103,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 42.1, for: "C") insertStatement.bind(value: 42, for: "D") - let data = Data(bytes: UnsafePointer([ 0x41, 0x42, 0x43 ] as [UInt8]), count: 3) + let data = Data(bytes: [0x41, 0x42, 0x43 ], count: 3) insertStatement.bind(value: data, for: "E") XCTAssertNoThrow(try insertStatement.perform()) @@ -210,7 +210,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 42.1, for: "C") insertStatement.bind(value: 42, for: "D") - let data = Data(bytes: UnsafePointer([ 0x41, 0x42, 0x43 ] as [UInt8]), count: 3) + let data = Data(bytes: [0x41, 0x42, 0x43], count: 3) insertStatement.bind(value: data, for: "E") XCTAssertNoThrow(try insertStatement.perform()) @@ -231,7 +231,7 @@ class StatementTests: XCTestCase { updateStatement.bind(value: 2, for: "D") updateStatement.bind(value: lastId, for: "A") - let data2 = Data(bytes: UnsafePointer([ 0x44, 0x45, 0x46 ] as [UInt8]), count: 3) + let data2 = Data(bytes: [0x44, 0x45, 0x46], count: 3) updateStatement.bind(value: data2, for: "E") XCTAssertNoThrow(try updateStatement.perform()) @@ -287,7 +287,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 1.1, for: "C") insertStatement.bind(value: 1, for: "D") - let data1 = Data(bytes: UnsafePointer([ 0x01 ] as [UInt8]), count: 1) + let data1 = Data(bytes: [0x01], count: 1) insertStatement.bind(value: data1, for: "E") XCTAssertNoThrow(try insertStatement.perform()) @@ -299,7 +299,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 2.2, for: "C") insertStatement.bind(value: 2, for: "D") - let data2 = Data(bytes: UnsafePointer([ 0x02 ] as [UInt8]), count: 1) + let data2 = Data(bytes: [0x02], count: 1) insertStatement.bind(value: data2, for: "E") XCTAssertNoThrow(try insertStatement.perform()) @@ -312,7 +312,7 @@ class StatementTests: XCTestCase { insertStatement.bind(value: 3.3, for: "C") insertStatement.bind(value: 3, for: "D") - let data3 = Data(bytes: UnsafePointer([ 0x03 ] as [UInt8]), count: 1) + let data3 = Data(bytes: [0x03], count: 1) insertStatement.bind(value: data3, for: "E") XCTAssertNoThrow(try insertStatement.perform()) diff --git a/Tests/RestructureTests/TestHelpers.swift b/Tests/RestructureTests/TestHelpers.swift index 3e166f5..3b6ef23 100644 --- a/Tests/RestructureTests/TestHelpers.swift +++ b/Tests/RestructureTests/TestHelpers.swift @@ -3,7 +3,7 @@ // Restructure // // Created by Stephen H. Gerstacker on 11/4/18. -// Copyright @ 2019 Stephen H. Gerstacker. All rights reserved. +// Copyright @ 2020 Stephen H. Gerstacker. All rights reserved. // import XCTest