Up to date playgrounds

This commit is contained in:
Gwendal Roué 2019-02-17 08:17:06 +01:00
parent df05e940e8
commit 7e9e50e52d
8 changed files with 64 additions and 211 deletions

View File

@ -10,9 +10,6 @@
<FileRef
location = "group:JSONSynchronization.playground">
</FileRef>
<FileRef
location = "group:Record.playground">
</FileRef>
<FileRef
location = "group:TransactionObserver.playground">
</FileRef>

View File

@ -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)

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)
}

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='osx' display-mode='raw'>
<timeline fileName='timeline.xctimeline'/>
</playground>

View File

@ -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)
}

View File

@ -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<String> = []
@ -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])
}