Skip to content

Commit

Permalink
Merge pull request #689 from groue/rename-column
Browse files Browse the repository at this point in the history
Add support for renaming columns within a table
  • Loading branch information
groue authored Feb 14, 2020
2 parents a3c43ef + 38a161f commit a5b2757
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 15 deletions.
84 changes: 69 additions & 15 deletions GRDB/QueryInterface/Schema/TableDefinition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,13 @@ public final class TableDefinition {
/// See https://www.sqlite.org/lang_altertable.html
public final class TableAlteration {
private let name: String
private var addedColumns: [ColumnDefinition] = []

private enum TableAlterationKind {
case add(ColumnDefinition)
case rename(old: String, new: String)
}

private var alterations: [TableAlterationKind] = []

init(name: String) {
self.name = name
Expand All @@ -677,27 +683,75 @@ public final class TableAlteration {
@discardableResult
public func add(column name: String, _ type: Database.ColumnType? = nil) -> ColumnDefinition {
let column = ColumnDefinition(name: name, type: type)
addedColumns.append(column)
alterations.append(.add(column))
return column
}

#if GRDBCUSTOMSQLITE || GRDBCipher
/// Renames a column in a table.
///
/// try db.alter(table: "player") { t in
/// t.rename(column: "url", to: "home_url")
/// }
///
/// See https://www.sqlite.org/lang_altertable.html
///
/// - parameter name: the column name to rename.
/// - parameter newName: the new name of the column.
public func rename(column name: String, to newName: String) {
_rename(column: name, to: newName)
}
#else
/// Renames a column in a table.
///
/// try db.alter(table: "player") { t in
/// t.rename(column: "url", to: "home_url")
/// }
///
/// See https://www.sqlite.org/lang_altertable.html
///
/// - parameter name: the column name to rename.
/// - parameter newName: the new name of the column.
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public func rename(column name: String, to newName: String) {
_rename(column: name, to: newName)
}
#endif

private func _rename(column name: String, to newName: String) {
alterations.append(.rename(old: name, new: newName))
}

fileprivate func sql(_ db: Database) throws -> String {
var statements: [String] = []

for column in addedColumns {
var chunks: [String] = []
chunks.append("ALTER TABLE")
chunks.append(name.quotedDatabaseIdentifier)
chunks.append("ADD COLUMN")
try chunks.append(column.sql(db, tableName: name, primaryKeyColumns: nil))
let statement = chunks.joined(separator: " ")
statements.append(statement)

if let indexDefinition = column.indexDefinition(in: name) {
statements.append(indexDefinition.sql())

for alteration in alterations {
switch alteration {
case let .add(column):
var chunks: [String] = []
chunks.append("ALTER TABLE")
chunks.append(name.quotedDatabaseIdentifier)
chunks.append("ADD COLUMN")
try chunks.append(column.sql(db, tableName: name, primaryKeyColumns: nil))
let statement = chunks.joined(separator: " ")
statements.append(statement)

if let indexDefinition = column.indexDefinition(in: name) {
statements.append(indexDefinition.sql())
}
case let .rename(oldName, newName):
var chunks: [String] = []
chunks.append("ALTER TABLE")
chunks.append(name.quotedDatabaseIdentifier)
chunks.append("RENAME COLUMN")
chunks.append(oldName.quotedDatabaseIdentifier)
chunks.append("TO")
chunks.append(newName.quotedDatabaseIdentifier)
let statement = chunks.joined(separator: " ")
statements.append(statement)
}
}

return statements.joined(separator: "; ")
}
}
Expand Down
35 changes: 35 additions & 0 deletions Tests/GRDBTests/TableDefinitionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import XCTest
#if GRDBCUSTOMSQLITE
import GRDBCustomSQLite
#else
#if GRDBCIPHER
import SQLCipher
#elseif SWIFT_PACKAGE
import CSQLite
#else
import SQLite3
#endif
import GRDB
#endif

Expand Down Expand Up @@ -495,6 +502,34 @@ class TableDefinitionTests: GRDBTestCase {
assertEqualSQL(sqlQueries[sqlQueries.count - 1], "ALTER TABLE \"test\" ADD COLUMN \"e\"")
}
}

func testAlterTableRenameColumn() throws {
guard sqlite3_libversion_number() >= 3025000 else {
return
}
#if !GRDBCUSTOMSQLITE && !GRDBCIPHER
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) else {
return
}
#endif
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.create(table: "test") { t in
t.column("a", .text)
}

sqlQueries.removeAll()
try db.alter(table: "test") { t in
t.rename(column: "a", to: "b")
t.add(column: "c")
t.rename(column: "c", to: "d")
}

assertEqualSQL(sqlQueries[sqlQueries.count - 3], "ALTER TABLE \"test\" RENAME COLUMN \"a\" TO \"b\"")
assertEqualSQL(sqlQueries[sqlQueries.count - 2], "ALTER TABLE \"test\" ADD COLUMN \"c\"")
assertEqualSQL(sqlQueries[sqlQueries.count - 1], "ALTER TABLE \"test\" RENAME COLUMN \"c\" TO \"d\"")
}
}

func testDropTable() throws {
let dbQueue = try makeDatabaseQueue()
Expand Down

0 comments on commit a5b2757

Please sign in to comment.