From 7e9e50e52da13796abc2cd76145a40989b9d818b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gwendal=20Roue=CC=81?= Date: Sun, 17 Feb 2019 08:17:06 +0100 Subject: [PATCH] Up to date playgrounds --- GRDB.xcworkspace/contents.xcworkspacedata | 3 - GRDB/Core/Database+Statements.swift | 2 +- GRDB/QueryInterface/VirtualTableModule.swift | 1 - .../MyPlayground.playground/Contents.swift | 6 +- Playgrounds/Record.playground/Contents.swift | 136 ------------------ .../Record.playground/contents.xcplayground | 4 - Playgrounds/Tour.playground/Contents.swift | 63 ++++---- .../Contents.swift | 60 ++++---- 8 files changed, 64 insertions(+), 211 deletions(-) delete mode 100644 Playgrounds/Record.playground/Contents.swift delete mode 100644 Playgrounds/Record.playground/contents.xcplayground diff --git a/GRDB.xcworkspace/contents.xcworkspacedata b/GRDB.xcworkspace/contents.xcworkspacedata index 180e0c43..61082ffc 100644 --- a/GRDB.xcworkspace/contents.xcworkspacedata +++ b/GRDB.xcworkspace/contents.xcworkspacedata @@ -10,9 +10,6 @@ - - diff --git a/GRDB/Core/Database+Statements.swift b/GRDB/Core/Database+Statements.swift index c9958165..11362e4f 100644 --- a/GRDB/Core/Database+Statements.swift +++ b/GRDB/Core/Database+Statements.swift @@ -290,7 +290,7 @@ struct StatementCache { let statement: SelectStatement if #available(iOS 12.0, OSX 10.14, watchOS 5.0, *) { // SQLite 3.24.0 or more - statement = try db.makeSelectStatement(sql, prepFlags: SQLITE_PREPARE_PERSISTENT) + statement = try db.makeSelectStatement(sql: sql, prepFlags: SQLITE_PREPARE_PERSISTENT) } else { // SQLite 3.19.3 or less statement = try db.makeSelectStatement(sql: sql) diff --git a/GRDB/QueryInterface/VirtualTableModule.swift b/GRDB/QueryInterface/VirtualTableModule.swift index ae6d817d..06eaa3ea 100644 --- a/GRDB/QueryInterface/VirtualTableModule.swift +++ b/GRDB/QueryInterface/VirtualTableModule.swift @@ -55,7 +55,6 @@ extension Database { /// - module: The name of an SQLite virtual table module. /// - throws: A DatabaseError whenever an SQLite error occurs. public func create(virtualTable name: String, ifNotExists: Bool = false, using module: String) throws { - // TODO? refactor with SQLLiteral var chunks: [String] = [] chunks.append("CREATE VIRTUAL TABLE") if ifNotExists { diff --git a/Playgrounds/MyPlayground.playground/Contents.swift b/Playgrounds/MyPlayground.playground/Contents.swift index 400de1c3..067ff1a1 100644 --- a/Playgrounds/MyPlayground.playground/Contents.swift +++ b/Playgrounds/MyPlayground.playground/Contents.swift @@ -13,9 +13,9 @@ try! dbQueue.inDatabase { db in t.column("name", .text) } - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES (?)", arguments: ["Arthur"]) - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES (?)", arguments: ["Barbara"]) + try db.execute(sql: "INSERT INTO persons (name) VALUES (?)", arguments: ["Arthur"]) + try db.execute(sql: "INSERT INTO persons (name) VALUES (?)", arguments: ["Barbara"]) - let names = try String.fetchAll(db, rawSQL: "SELECT name FROM persons") + let names = try String.fetchAll(db, sql: "SELECT name FROM persons") print(names) } diff --git a/Playgrounds/Record.playground/Contents.swift b/Playgrounds/Record.playground/Contents.swift deleted file mode 100644 index bd5b3aa4..00000000 --- a/Playgrounds/Record.playground/Contents.swift +++ /dev/null @@ -1,136 +0,0 @@ -//: To run this playground: -//: -//: - Open GRDB.xcworkspace -//: - Select the GRDBOSX scheme: menu Product > Scheme > GRDBOSX -//: - Build: menu Product > Build -//: - Select the playground in the Playgrounds Group -//: - Run the playground -//: -//: Record -//: ====== -//: -//: This playground is a demo of the Record class, the type that provides fetching methods, persistence methods, and change tracking. - -import GRDB - - -//: ## Setup -//: -//: First open an in-memory database, with a trace function that prints all SQL statements. - -var configuration = Configuration() -configuration.trace = { print($0) } -let dbQueue = DatabaseQueue(configuration: configuration) - -//: Create a database table which stores persons. - -try dbQueue.inDatabase { db in - try db.execute( - "CREATE TABLE persons (" + - "id INTEGER PRIMARY KEY, " + - "firstName TEXT, " + - "lastName TEXT" + - ")") -} - - -//: ## Subclassing Record -//: -//: The Person class is a subclass of Record, with regular properties, and a regular initializer: - -class Person : Record { - var id: Int64? - var firstName: String? - var lastName: String? - - var fullName: String { - return [firstName, lastName].flatMap { $0 }.joined(separator: " ") - } - - init(firstName: String?, lastName: String?) { - self.id = nil - self.firstName = firstName - self.lastName = lastName - super.init() - } - - -//: Subclasses of Record have to override the methods that define how they interact with the database. -//: -//: 1. The table name: - - override class var databaseTableName: String { - return "persons" - } - -//: 2. How to build a Person from a database row: - - required init(row: Row) { - id = row["id"] - firstName = row["firstName"] - lastName = row["lastName"] - super.init(row: row) - } - -//: 3. The dictionary of values that are stored in the database: - - override func encode(to container: inout PersistenceContainer) { - container["id"] = id - container["firstName"] = firstName - container["lastName"] = lastName - } - -//: 4. When relevant, update the person's id after a database row has been inserted: - - override func didInsert(with rowID: Int64, for column: String?) { - id = rowID - } -} - - -//: ## Insert Records -//: -//: Persons are regular objects, that you can freely create: - -let arthur = Person(firstName: "Arthur", lastName: "Miller") -let barbra = Person(firstName: "Barbra", lastName: "Streisand") -let cinderella = Person(firstName: "Cinderella", lastName: nil) - -//: They are not stored in the database yet. Insert them: - -try dbQueue.inDatabase { db in - try arthur.insert(db) - try barbra.insert(db) - try cinderella.insert(db) -} - - -//: ## Fetching Records - -try dbQueue.inDatabase { db in - - //: Fetch records from the database: - let allPersons = try Person.fetchAll(db) - - //: Fetch record by primary key: - let person = try Person.fetchOne(db, key: arthur.id)! - person.fullName - - //: Fetch persons with an SQL query: - let millers = try Person.fetchAll(db, rawSQL: "SELECT * FROM persons WHERE lastName = ?", arguments: ["Miller"]) - millers.first!.fullName - - - //: To fetch persons using the query interface, you need some colums that can filter or sort: - - struct Col { - static let firstName = Column("firstName") - static let lastName = Column("lastName") - } - - //: Sort - let personsSortedByName = try Person.order(Col.firstName, Col.lastName).fetchAll(db) - - //: Filter - let streisands = try Person.filter(Col.lastName == "Streisand").fetchAll(db) -} diff --git a/Playgrounds/Record.playground/contents.xcplayground b/Playgrounds/Record.playground/contents.xcplayground deleted file mode 100644 index fd676d5b..00000000 --- a/Playgrounds/Record.playground/contents.xcplayground +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Playgrounds/Tour.playground/Contents.swift b/Playgrounds/Tour.playground/Contents.swift index 2f313729..32ec34a0 100644 --- a/Playgrounds/Tour.playground/Contents.swift +++ b/Playgrounds/Tour.playground/Contents.swift @@ -9,7 +9,7 @@ //: Tour //: ====== //: -//: This playground is a tour of GRDB. +//: This playground is a quick tour of GRDB. import GRDB import CoreLocation @@ -26,44 +26,45 @@ let dbQueue = DatabaseQueue(configuration: configuration) //: Execute SQL queries try dbQueue.inDatabase { db in - try db.execute( - "CREATE TABLE pointOfInterests (" + - "id INTEGER PRIMARY KEY, " + - "title TEXT, " + - "favorite BOOLEAN NOT NULL, " + - "latitude DOUBLE NOT NULL, " + - "longitude DOUBLE NOT NULL" + - ")") + try db.execute(sql: """ + CREATE TABLE place ( + id INTEGER PRIMARY KEY, + title TEXT, + favorite BOOLEAN NOT NULL, + latitude DOUBLE NOT NULL, + longitude DOUBLE NOT NULL + ) + """) - try db.execute( - "INSERT INTO pointOfInterests (title, favorite, latitude, longitude) " + - "VALUES (?, ?, ?, ?)", - arguments: ["Paris", true, 48.85341, 2.3488]) + try db.execute(sql: """ + INSERT INTO place (title, favorite, latitude, longitude) + VALUES (?, ?, ?, ?) + """, arguments: ["Paris", true, 48.85341, 2.3488]) let parisId = db.lastInsertedRowID } //: Fetch database rows and values -try dbQueue.inDatabase { db in - let rows = try Row.fetchCursor(db, rawSQL: "SELECT * FROM pointOfInterests") +try! dbQueue.inDatabase { db in + let rows = try Row.fetchCursor(db, sql: "SELECT * FROM place") while let row = try rows.next() { let title: String = row["title"] let favorite: Bool = row["favorite"] - let coordinate = CLLocationCoordinate2DMake( - row["latitude"], - row["longitude"]) + let coordinate = CLLocationCoordinate2D( + latitude: row["latitude"], + longitude: row["longitude"]) print("Fetched", title, favorite, coordinate) } - let poiCount = try Int.fetchOne(db, rawSQL: "SELECT COUNT(*) FROM pointOfInterests")! // Int - let poiTitles = try String.fetchAll(db, rawSQL: "SELECT title FROM pointOfInterests") // [String] + let placeCount = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM place")! // Int + let placeTitles = try String.fetchAll(db, sql: "SELECT title FROM place") // [String] } //: Insert and fetch records -struct PointOfInterest { +struct Place { var id: Int64? var title: String? var favorite: Bool @@ -71,7 +72,7 @@ struct PointOfInterest { } // Adopt FetchableRecord -extension PointOfInterest : FetchableRecord { +extension Place : FetchableRecord { init(row: Row) { id = row["id"] title = row["title"] @@ -83,12 +84,12 @@ extension PointOfInterest : FetchableRecord { } // Adopt TableRecord -extension PointOfInterest : TableRecord { - static let databaseTableName = "pointOfInterests" +extension Place : TableRecord { + static let databaseTableName = "place" } // Adopt MutablePersistableRecord -extension PointOfInterest : MutablePersistableRecord { +extension Place : MutablePersistableRecord { func encode(to container: inout PersistenceContainer) { container["id"] = id container["title"] = title @@ -103,7 +104,7 @@ extension PointOfInterest : MutablePersistableRecord { } try dbQueue.inDatabase { db in - var berlin = PointOfInterest( + var berlin = Place( id: nil, title: "Berlin", favorite: false, @@ -116,7 +117,7 @@ try dbQueue.inDatabase { db in try berlin.update(db) // Fetch from SQL - let pois = try PointOfInterest.fetchAll(db, rawSQL: "SELECT * FROM pointOfInterests") // [PointOfInterest] + let places = try Place.fetchAll(db, sql: "SELECT * FROM place") // [Place] //: Avoid SQL with the query interface: @@ -124,10 +125,10 @@ try dbQueue.inDatabase { db in let title = Column("title") let favorite = Column("favorite") - berlin = try PointOfInterest.filter(title == "Berlin").fetchOne(db)! // PointOfInterest - let paris = try PointOfInterest.fetchOne(db, key: 1) // PointOfInterest? - let favoritePois = try PointOfInterest // [PointOfInterest] - .filter(favorite) + berlin = try Place.filter(title == "Berlin").fetchOne(db)! // Place + let paris = try Place.fetchOne(db, key: 1) // Place? + let favoritePlaces = try Place // [Place] + .filter(favorite == true) .order(title) .fetchAll(db) } diff --git a/Playgrounds/TransactionObserver.playground/Contents.swift b/Playgrounds/TransactionObserver.playground/Contents.swift index cbffb9c6..e5aa0429 100644 --- a/Playgrounds/TransactionObserver.playground/Contents.swift +++ b/Playgrounds/TransactionObserver.playground/Contents.swift @@ -13,26 +13,24 @@ import GRDB let dbQueue = DatabaseQueue() // Memory database var migrator = DatabaseMigrator() -migrator.registerMigration("createPersons") { db in - try db.execute(rawSQL: """ - CREATE TABLE persons ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL) - """) +migrator.registerMigration("createPerson") { db in + try db.create(table: "person") { t in + t.autoIncrementedPrimaryKey("id") + t.column("name", .text).notNull() + } } -migrator.registerMigration("createPets") { db in - try db.execute(rawSQL: """ - CREATE TABLE pets ( - id INTEGER PRIMARY KEY, - name TEXT, - ownerId INTEGER NOT NULL REFERENCES persons(id) ON DELETE CASCADE) - """) +migrator.registerMigration("createPet") { db in + try db.create(table: "pet") { t in + t.autoIncrementedPrimaryKey("id") + t.column("name", .text).notNull() + t.column("ownerId", .integer).references("person", onDelete: .cascade) + } } try! migrator.migrate(dbQueue) +// -/// TableChangeObserver prints on the main thread the changes database tables. class TableChangeObserver : NSObject, TransactionObserver { private var changedTableNames: Set = [] @@ -44,9 +42,6 @@ class TableChangeObserver : NSObject, TransactionObserver { changedTableNames.insert(event.tableName) } - func databaseWillCommit() throws { - } - func databaseDidCommit(_ db: Database) { print("Changed table(s): \(changedTableNames.joined(separator: ", "))") changedTableNames = [] @@ -57,34 +52,35 @@ class TableChangeObserver : NSObject, TransactionObserver { } } - - -// Register observer - let observer = TableChangeObserver() dbQueue.add(transactionObserver: observer) // -print("-- Changes 1") -try! dbQueue.inDatabase { db in - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES (?)", arguments: ["Arthur"]) +print("-- Changes without transaction") +try dbQueue.inDatabase { db in + try db.execute(sql: "INSERT INTO person (name) VALUES (?)", arguments: ["Arthur"]) let arthurId = db.lastInsertedRowID - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES (?)", arguments: ["Barbara"]) - try db.execute(rawSQL: "INSERT INTO pets (ownerId, name) VALUES (?, ?)", arguments: [arthurId, "Barbara"]) + try db.execute(sql: "INSERT INTO person (name) VALUES (?)", arguments: ["Barbara"]) + try db.execute(sql: "INSERT INTO pet (ownerId, name) VALUES (?, ?)", arguments: [arthurId, "Barbara"]) + try db.execute(sql: "DELETE FROM person WHERE id = ?", arguments: [arthurId]) } -print("-- Changes 2") +print("-- Rollbacked changes") try dbQueue.inTransaction { db in - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES ('Arthur')") - try db.execute(rawSQL: "INSERT INTO persons (name) VALUES ('Barbara')") + try db.execute(sql: "INSERT INTO person (name) VALUES ('Arthur')") + try db.execute(sql: "INSERT INTO person (name) VALUES ('Barbara')") return .rollback } -print("-- Changes 3") +print("-- Changes wrapped in a transaction") try dbQueue.write { db in - try db.execute(rawSQL: "DELETE FROM persons") - try db.execute(rawSQL: "DELETE FROM pets") + try db.execute(sql: "DELETE FROM person") + try db.execute(sql: "INSERT INTO person (name) VALUES (?)", arguments: ["Arthur"]) + let arthurId = db.lastInsertedRowID + try db.execute(sql: "INSERT INTO person (name) VALUES (?)", arguments: ["Barbara"]) + try db.execute(sql: "INSERT INTO pet (ownerId, name) VALUES (?, ?)", arguments: [arthurId, "Barbara"]) + try db.execute(sql: "DELETE FROM person WHERE id = ?", arguments: [arthurId]) }