@@ -43,19 +43,30 @@ public class SchemaChanger: CustomStringConvertible {
43
43
44
44
public enum Operation {
45
45
case addColumn( ColumnDefinition )
46
+ case addIndex( IndexDefinition , ifNotExists: Bool )
46
47
case dropColumn( String )
48
+ case dropIndex( String , ifExists: Bool )
47
49
case renameColumn( String , String )
48
50
case renameTable( String )
51
+ case createTable( columns: [ ColumnDefinition ] , ifNotExists: Bool )
49
52
50
53
/// Returns non-nil if the operation can be executed with a simple SQL statement
51
54
func toSQL( _ table: String , version: SQLiteVersion ) -> String ? {
52
55
switch self {
53
56
case . addColumn( let definition) :
54
57
return " ALTER TABLE \( table. quote ( ) ) ADD COLUMN \( definition. toSQL ( ) ) "
58
+ case . addIndex( let definition, let ifNotExists) :
59
+ return definition. toSQL ( ifNotExists: ifNotExists)
55
60
case . renameColumn( let from, let to) where SQLiteFeature . renameColumn. isSupported ( by: version) :
56
61
return " ALTER TABLE \( table. quote ( ) ) RENAME COLUMN \( from. quote ( ) ) TO \( to. quote ( ) ) "
57
62
case . dropColumn( let column) where SQLiteFeature . dropColumn. isSupported ( by: version) :
58
63
return " ALTER TABLE \( table. quote ( ) ) DROP COLUMN \( column. quote ( ) ) "
64
+ case . dropIndex( let name, let ifExists) :
65
+ return " DROP INDEX \( ifExists ? " IF EXISTS " : " " ) \( name. quote ( ) ) "
66
+ case . createTable( let columns, let ifNotExists) :
67
+ return " CREATE TABLE \( ifNotExists ? " IF NOT EXISTS " : " " ) \( table. quote ( ) ) ( " +
68
+ columns. map { $0. toSQL ( ) } . joined ( separator: " , " ) +
69
+ " ) "
59
70
default : return nil
60
71
}
61
72
}
@@ -89,7 +100,7 @@ public class SchemaChanger: CustomStringConvertible {
89
100
public class AlterTableDefinition {
90
101
fileprivate var operations : [ Operation ] = [ ]
91
102
92
- let name : String
103
+ public let name : String
93
104
94
105
init ( name: String ) {
95
106
self . name = name
@@ -99,21 +110,73 @@ public class SchemaChanger: CustomStringConvertible {
99
110
operations. append ( . addColumn( column) )
100
111
}
101
112
113
+ public func add( index: IndexDefinition , ifNotExists: Bool = false ) {
114
+ operations. append ( . addIndex( index, ifNotExists: ifNotExists) )
115
+ }
116
+
102
117
public func drop( column: String ) {
103
118
operations. append ( . dropColumn( column) )
104
119
}
105
120
121
+ public func drop( index: String , ifExists: Bool = false ) {
122
+ operations. append ( . dropIndex( index, ifExists: ifExists) )
123
+ }
124
+
106
125
public func rename( column: String , to: String ) {
107
126
operations. append ( . renameColumn( column, to) )
108
127
}
109
128
}
110
129
130
+ public class CreateTableDefinition {
131
+ fileprivate var columnDefinitions : [ ColumnDefinition ] = [ ]
132
+ fileprivate var indexDefinitions : [ IndexDefinition ] = [ ]
133
+
134
+ let name : String
135
+ let ifNotExists : Bool
136
+
137
+ init ( name: String , ifNotExists: Bool ) {
138
+ self . name = name
139
+ self . ifNotExists = ifNotExists
140
+ }
141
+
142
+ public func add( column: ColumnDefinition ) {
143
+ columnDefinitions. append ( column)
144
+ }
145
+
146
+ public func add< T> ( expression: Expression < T > ) where T: Value {
147
+ add ( column: . init( name: columnName ( for: expression) , type: . init( expression: expression) , nullable: false ) )
148
+ }
149
+
150
+ public func add< T> ( expression: Expression < T ? > ) where T: Value {
151
+ add ( column: . init( name: columnName ( for: expression) , type: . init( expression: expression) , nullable: true ) )
152
+ }
153
+
154
+ public func add( index: IndexDefinition ) {
155
+ indexDefinitions. append ( index)
156
+ }
157
+
158
+ var operations : [ Operation ] {
159
+ precondition ( !columnDefinitions. isEmpty)
160
+ return [
161
+ . createTable( columns: columnDefinitions, ifNotExists: ifNotExists)
162
+ ] + indexDefinitions. map { . addIndex( $0, ifNotExists: ifNotExists) }
163
+ }
164
+
165
+ private func columnName< T> ( for expression: Expression < T > ) -> String {
166
+ switch LiteralValue ( expression. template) {
167
+ case . stringLiteral( let string) : return string
168
+ default : fatalError ( " expression is not a literal string value " )
169
+ }
170
+ }
171
+ }
172
+
111
173
private let connection : Connection
112
174
private let schemaReader : SchemaReader
113
175
private let version : SQLiteVersion
114
176
static let tempPrefix = " tmp_ "
115
177
typealias Block = ( ) throws -> Void
116
178
public typealias AlterTableDefinitionBlock = ( AlterTableDefinition ) -> Void
179
+ public typealias CreateTableDefinitionBlock = ( CreateTableDefinition ) -> Void
117
180
118
181
struct Options : OptionSet {
119
182
let rawValue : Int
@@ -141,6 +204,15 @@ public class SchemaChanger: CustomStringConvertible {
141
204
}
142
205
}
143
206
207
+ public func create( table: String , ifNotExists: Bool = false , block: CreateTableDefinitionBlock ) throws {
208
+ let createTableDefinition = CreateTableDefinition ( name: table, ifNotExists: ifNotExists)
209
+ block ( createTableDefinition)
210
+
211
+ for operation in createTableDefinition. operations {
212
+ try run ( table: table, operation: operation)
213
+ }
214
+ }
215
+
144
216
public func drop( table: String , ifExists: Bool = true ) throws {
145
217
try dropTable ( table, ifExists: ifExists)
146
218
}
@@ -151,6 +223,12 @@ public class SchemaChanger: CustomStringConvertible {
151
223
try connection. run ( " ALTER TABLE \( table. quote ( ) ) RENAME TO \( to. quote ( ) ) " )
152
224
}
153
225
226
+ // Runs arbitrary SQL. Should only be used if no predefined operations exist.
227
+ @discardableResult
228
+ public func run( _ sql: String , _ bindings: Binding ? ... ) throws -> Statement {
229
+ return try connection. run ( sql, bindings)
230
+ }
231
+
154
232
private func run( table: String , operation: Operation ) throws {
155
233
try operation. validate ( )
156
234
@@ -263,7 +341,9 @@ extension TableDefinition {
263
341
func apply( _ operation: SchemaChanger . Operation ? ) -> TableDefinition {
264
342
switch operation {
265
343
case . none: return self
344
+ case . createTable, . addIndex, . dropIndex: fatalError ( )
266
345
case . addColumn: fatalError ( " Use 'ALTER TABLE ADD COLUMN (...)' " )
346
+
267
347
case . dropColumn( let column) :
268
348
return TableDefinition ( name: name,
269
349
columns: columns. filter { $0. name != column } ,
@@ -280,3 +360,13 @@ extension TableDefinition {
280
360
}
281
361
}
282
362
}
363
+
364
+ extension ColumnDefinition . Affinity {
365
+ init < T> ( expression: Expression < T > ) where T: Value {
366
+ self . init ( T . declaredDatatype)
367
+ }
368
+
369
+ init < T> ( expression: Expression < T ? > ) where T: Value {
370
+ self . init ( T . declaredDatatype)
371
+ }
372
+ }
0 commit comments