Skip to content

Commit 748dcba

Browse files
committed
Support creating tables in schema changer
1 parent c7b78f2 commit 748dcba

File tree

4 files changed

+100
-2
lines changed

4 files changed

+100
-2
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ lint: $(SWIFTLINT)
3535
$< --strict
3636

3737
lint-fix: $(SWIFTLINT)
38-
$< lint fix
38+
$< --fix
3939

4040
clean:
4141
$(XCODEBUILD) $(BUILD_ARGUMENTS) clean

Sources/SQLite/Schema/SchemaChanger.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,31 @@ public class SchemaChanger: CustomStringConvertible {
4343

4444
public enum Operation {
4545
case addColumn(ColumnDefinition)
46+
case addIndex(IndexDefinition)
4647
case dropColumn(String)
4748
case renameColumn(String, String)
4849
case renameTable(String)
50+
case createTable(columns: [ColumnDefinition])
4951

5052
/// Returns non-nil if the operation can be executed with a simple SQL statement
5153
func toSQL(_ table: String, version: SQLiteVersion) -> String? {
5254
switch self {
5355
case .addColumn(let definition):
5456
return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
57+
case .addIndex(let definition):
58+
let unique = definition.unique ? "UNIQUE" : ""
59+
let columns = definition.columns.joined(separator: ", ")
60+
let `where` = definition.where.map { " WHERE " + $0 } ?? ""
61+
62+
return "CREATE \(unique) INDEX \(definition.name) ON \(definition.table) (\(columns)) \(`where`)"
5563
case .renameColumn(let from, let to) where SQLiteFeature.renameColumn.isSupported(by: version):
5664
return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
5765
case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
5866
return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
67+
case .createTable(let columns):
68+
return "CREATE TABLE \(table.quote()) (" +
69+
columns.map { $0.toSQL() }.joined(separator: ", ") +
70+
")"
5971
default: return nil
6072
}
6173
}
@@ -108,12 +120,39 @@ public class SchemaChanger: CustomStringConvertible {
108120
}
109121
}
110122

123+
public class CreateTableDefinition {
124+
fileprivate var columnDefinitions: [ColumnDefinition] = []
125+
fileprivate var indexDefinitions: [IndexDefinition] = []
126+
127+
let name: String
128+
129+
init(name: String) {
130+
self.name = name
131+
}
132+
133+
public func add(column: ColumnDefinition) {
134+
columnDefinitions.append(column)
135+
}
136+
137+
public func add(index: IndexDefinition) {
138+
indexDefinitions.append(index)
139+
}
140+
141+
var operations: [Operation] {
142+
precondition(!columnDefinitions.isEmpty)
143+
return [
144+
.createTable(columns: columnDefinitions)
145+
] + indexDefinitions.map { .addIndex($0) }
146+
}
147+
}
148+
111149
private let connection: Connection
112150
private let schemaReader: SchemaReader
113151
private let version: SQLiteVersion
114152
static let tempPrefix = "tmp_"
115153
typealias Block = () throws -> Void
116154
public typealias AlterTableDefinitionBlock = (AlterTableDefinition) -> Void
155+
public typealias CreateTableDefinitionBlock = (CreateTableDefinition) -> Void
117156

118157
struct Options: OptionSet {
119158
let rawValue: Int
@@ -141,6 +180,15 @@ public class SchemaChanger: CustomStringConvertible {
141180
}
142181
}
143182

183+
public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
184+
let createTableDefinition = CreateTableDefinition(name: table)
185+
block(createTableDefinition)
186+
187+
for operation in createTableDefinition.operations {
188+
try run(table: table, operation: operation)
189+
}
190+
}
191+
144192
public func drop(table: String, ifExists: Bool = true) throws {
145193
try dropTable(table, ifExists: ifExists)
146194
}
@@ -263,6 +311,7 @@ extension TableDefinition {
263311
func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
264312
switch operation {
265313
case .none: return self
314+
case .createTable, .addIndex: fatalError()
266315
case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
267316
case .dropColumn(let column):
268317
return TableDefinition(name: name,

Sources/SQLite/Schema/SchemaDefinitions.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,15 @@ public struct IndexDefinition: Equatable {
270270
return memo2
271271
}
272272
}
273+
274+
let orders = indexSQL.flatMap(orders)
275+
273276
self.init(table: table,
274277
name: name,
275278
unique: unique,
276279
columns: columns,
277280
where: indexSQL.flatMap(wherePart),
278-
orders: indexSQL.flatMap(orders))
281+
orders: (orders?.isEmpty ?? false) ? nil : orders)
279282
}
280283

281284
public let table: String

Tests/SQLiteTests/Schema/SchemaChangerTests.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,50 @@ class SchemaChangerTests: SQLiteTestCase {
154154
let users_new = Table("users_new")
155155
XCTAssertEqual((try db.scalar(users_new.count)) as Int, 1)
156156
}
157+
158+
func test_create_table() throws {
159+
try schemaChanger.create(table: "foo") { table in
160+
table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
161+
table.add(column: .init(name: "name", type: .TEXT, nullable: false))
162+
table.add(column: .init(name: "age", type: .INTEGER))
163+
164+
table.add(index: .init(table: table.name,
165+
name: "nameIndex",
166+
unique: true,
167+
columns: ["name"],
168+
where: nil,
169+
orders: nil))
170+
}
171+
172+
// make sure new table can be queried
173+
let foo = Table("foo")
174+
XCTAssertEqual((try db.scalar(foo.count)) as Int, 0)
175+
176+
let columns = try schema.columnDefinitions(table: "foo")
177+
XCTAssertEqual(columns, [
178+
ColumnDefinition(name: "id",
179+
primaryKey: .init(autoIncrement: true, onConflict: nil),
180+
type: .INTEGER,
181+
nullable: true,
182+
defaultValue: .NULL,
183+
references: nil),
184+
ColumnDefinition(name: "name",
185+
primaryKey: nil,
186+
type: .TEXT,
187+
nullable: false,
188+
defaultValue: .NULL,
189+
references: nil),
190+
ColumnDefinition(name: "age",
191+
primaryKey: nil,
192+
type: .INTEGER,
193+
nullable: true,
194+
defaultValue: .NULL,
195+
references: nil)
196+
])
197+
198+
let indexes = try schema.indexDefinitions(table: "foo")
199+
XCTAssertEqual(indexes, [
200+
IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil)
201+
])
202+
}
157203
}

0 commit comments

Comments
 (0)