Skip to content

Commit

Permalink
Enum Support (#85)
Browse files Browse the repository at this point in the history
* Enum (take 2) (#83)

* add enum type

* Add test, fix bug with using raw rather than string literal for enum values.

* annotation in test case

* take 2. protocol based from the start. no direct support on SQLDatabaseType.

* enum updates

* enum support + benchmark

* fix tests

* as sqlexpression

* fail with warning if enum types not supported when using builders

* test enum branches

* revert branch

Co-authored-by: Mathew Polzin <[email protected]>
  • Loading branch information
tanner0101 and mattpolzin authored Jan 22, 2020
1 parent d09b552 commit 371bdf2
Show file tree
Hide file tree
Showing 22 changed files with 651 additions and 109 deletions.
39 changes: 39 additions & 0 deletions Sources/SQLKit/Builders/SQLAlterEnumBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
extension SQLDatabase {
public func alter(enum name: String) -> SQLAlterEnumBuilder {
self.alter(enum: SQLIdentifier(name))
}

public func alter(enum name: SQLExpression) -> SQLAlterEnumBuilder {
.init(database: self, name: name)
}
}

public final class SQLAlterEnumBuilder: SQLQueryBuilder {
public var database: SQLDatabase
public var alterEnum: SQLAlterEnum
public var query: SQLExpression {
self.alterEnum
}

init(database: SQLDatabase, name: SQLExpression) {
self.database = database
self.alterEnum = .init(name: name, value: nil)
}

public func add(value: String) -> Self {
self.add(value: SQLLiteral.string(value))
}

public func add(value: SQLExpression) -> Self {
self.alterEnum.value = value
return self
}

public func run() -> EventLoopFuture<Void> {
guard self.database.dialect.enumSyntax == .typeName else {
self.database.logger.warning("Database does not support enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
78 changes: 75 additions & 3 deletions Sources/SQLKit/Builders/SQLAlterTableBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public final class SQLAlterTableBuilder: SQLQueryBuilder, SQLColumnBuilder {
public final class SQLAlterTableBuilder: SQLQueryBuilder {
/// `SQLAlterTable` query being built.
public var alterTable: SQLAlterTable

Expand All @@ -12,8 +12,8 @@ public final class SQLAlterTableBuilder: SQLQueryBuilder, SQLColumnBuilder {

/// See `SQLColumnBuilder`.
public var columns: [SQLExpression] {
get { return alterTable.columns }
set { alterTable.columns = newValue }
get { return alterTable.addColumns }
set { alterTable.addColumns = newValue }
}

/// Creates a new `SQLAlterTableBuilder`.
Expand All @@ -25,6 +25,78 @@ public final class SQLAlterTableBuilder: SQLQueryBuilder, SQLColumnBuilder {
self.alterTable = alterTable
self.database = database
}

public func column(
_ column: String,
type dataType: SQLDataType,
_ constraints: SQLColumnConstraintAlgorithm...
) -> Self {
return self.addColumn(SQLColumnDefinition(
column: SQLIdentifier(column),
dataType: dataType,
constraints: constraints
))
}

public func column(
_ column: SQLExpression,
type dataType: SQLExpression,
_ constraints: SQLExpression...
) -> Self {
return self.addColumn(SQLColumnDefinition(
column: column,
dataType: dataType,
constraints: constraints
))
}

public func addColumn(_ columnDefinition: SQLExpression) -> Self {
self.alterTable.addColumns.append(columnDefinition)
return self
}

public func modifyColumn(
_ column: String,
type dataType: SQLDataType,
_ constraints: SQLColumnConstraintAlgorithm...
) -> Self {
return self.modifyColumn(SQLColumnDefinition(
column: SQLIdentifier(column),
dataType: dataType,
constraints: constraints
))
}

public func modifyColumn(
_ column: SQLExpression,
type dataType: SQLExpression,
_ constraints: SQLExpression...
) -> Self {
return self.modifyColumn(SQLColumnDefinition(
column: column,
dataType: dataType,
constraints: constraints
))
}

public func modifyColumn(_ columnDefinition: SQLExpression) -> Self {
self.alterTable.modifyColumns.append(columnDefinition)
return self
}

public func dropColumn(
_ column: String
) -> Self {
return self.dropColumn(SQLIdentifier(column))
}

public func dropColumn(
_ column: SQLExpression
) -> Self {
self.alterTable.dropColumns.append(column)
return self
}

}

// MARK: Connection
Expand Down
42 changes: 0 additions & 42 deletions Sources/SQLKit/Builders/SQLColumnBuilder.swift

This file was deleted.

69 changes: 69 additions & 0 deletions Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// MARK: Connection

extension SQLDatabase {
/// Creates a new `SQLCreateEnumBuilder`.
///
/// conn.create(enum: "meal", cases: "breakfast", "lunch", "dinner")...
///
/// - parameters:
/// - name: Name of ENUM type to create.
/// - cases: The cases of the ENUM type.
/// - returns: `SQLCreateEnumBuilder`.
public func create(enum name: String) -> SQLCreateEnumBuilder {
return self.create(enum: SQLIdentifier(name))
}

/// Creates a new `SQLCreateEnumBuilder`.
///
/// conn.create(enum: SQLIdentifier("meal"), cases: "breakfast", "lunch", "dinner")...
///
/// - parameters:
/// - name: Name of ENUM type to create.
/// - cases: The cases of the ENUM type.
/// - returns: `SQLCreateEnumBuilder`.
public func create(enum name: SQLExpression) -> SQLCreateEnumBuilder {
return .init(name: name, on: self)
}
}

/// Builds `SQLCreateEnum` queries.
///
/// conn.create(enum: "meal", cases: "breakfast", "lunch", "dinner")
/// .run()
///
/// See `SQLColumnBuilder` and `SQLQueryBuilder` for more information.
public final class SQLCreateEnumBuilder: SQLQueryBuilder {
/// `CreateType` query being built.
public var createEnum: SQLCreateEnum

/// See `SQLQueryBuilder`.
public var database: SQLDatabase

/// See `SQLQueryBuilder`.
public var query: SQLExpression {
return self.createEnum
}

/// Creates a new `SQLCreateEnumBuilder`.
init(name: SQLExpression, on database: SQLDatabase) {
self.createEnum = .init(name: name, values: [])
self.database = database
}

public func value(_ value: String) -> Self {
self.value(SQLLiteral.string(value))
}

public func value(_ value: SQLExpression) -> Self {
self.createEnum.values.append(value)
return self
}

public func run() -> EventLoopFuture<Void> {
guard self.database.dialect.enumSyntax == .typeName else {
self.database.logger.warning("Database does not support enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
32 changes: 30 additions & 2 deletions Sources/SQLKit/Builders/SQLCreateTableBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// .run()
///
/// See `SQLColumnBuilder` and `SQLQueryBuilder` for more information.
public final class SQLCreateTableBuilder: SQLQueryBuilder, SQLColumnBuilder {
public final class SQLCreateTableBuilder: SQLQueryBuilder {
/// `CreateTable` query being built.
public var createTable: SQLCreateTable

Expand All @@ -29,7 +29,35 @@ public final class SQLCreateTableBuilder: SQLQueryBuilder, SQLColumnBuilder {
self.createTable = createTable
self.database = database
}


public func column(
_ column: String,
type dataType: SQLDataType,
_ constraints: SQLColumnConstraintAlgorithm...
) -> Self {
return self.column(SQLColumnDefinition(
column: SQLIdentifier(column),
dataType: dataType,
constraints: constraints
))
}

public func column(
_ column: SQLExpression,
type dataType: SQLExpression,
_ constraints: SQLExpression...
) -> Self {
return self.column(SQLColumnDefinition(
column: column,
dataType: dataType,
constraints: constraints
))
}

public func column(_ columnDefinition: SQLExpression) -> Self {
self.columns.append(columnDefinition)
return self
}

/// If the "TEMP" or "TEMPORARY" keyword occurs between the "CREATE" and "TABLE" then the new table is created in the temp database.
public func temporary() -> Self {
Expand Down
70 changes: 70 additions & 0 deletions Sources/SQLKit/Builders/SQLDropEnumBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
extension SQLDatabase {
/// Creates a new `SQLDropEnumBuilder`.
///
/// sql.drop(enum: "meal").run()
///
/// - parameters:
/// - type: Name of type to drop.
/// - returns: `SQLDropEnumBuilder`.
public func drop(enum name: String) -> SQLDropEnumBuilder {
self.drop(enum: SQLIdentifier(name))
}

/// Creates a new `SQLDropEnumBuilder`.
///
/// sql.drop(enum: "meal").run()
///
/// - parameters:
/// - type: Name of type to drop.
/// - returns: `SQLDropEnumBuilder`.
public func drop(enum name: SQLExpression) -> SQLDropEnumBuilder {
.init(name: name, on: self)
}
}

/// Builds `SQLDropEnumBuilder` queries.
///
/// conn.drop(type: "meal").run()
///
/// See `SQLQueryBuilder` for more information.
public final class SQLDropEnumBuilder: SQLQueryBuilder {
/// `DropType` query being built.
public var dropEnum: SQLDropEnum

/// See `SQLQueryBuilder`.
public var database: SQLDatabase

/// See `SQLQueryBuilder`.
public var query: SQLExpression {
return self.dropEnum
}

/// Creates a new `SQLDropEnumBuilder`.
init(name: SQLExpression, on database: SQLDatabase) {
self.dropEnum = .init(name: name)
self.database = database
}

/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the type does not exist.
public func ifExists() -> Self {
self.dropEnum.ifExists = true
return self
}

/// The optional `CASCADE` clause drops other objects that depend on this type
/// (such as table columns, functions, and operators), and in turn all objects
/// that depend on those objects.
public func cascade() -> Self {
self.dropEnum.cascade = true
return self
}

public func run() -> EventLoopFuture<Void> {
guard self.database.dialect.enumSyntax == .typeName else {
self.database.logger.warning("Database does not support enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
4 changes: 2 additions & 2 deletions Sources/SQLKit/Builders/SQLInsertBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ public final class SQLInsertBuilder: SQLQueryBuilder {
{
let row = try SQLQueryEncoder().encode(model)
if self.insert.columns.isEmpty {
self.insert.columns += row.keys.map { SQLColumn($0, table: nil) }
self.insert.columns += row.map { $0.0 }.map { SQLColumn($0, table: nil) }
} else {
assert(
self.insert.columns.count == row.count,
"Column count (\(self.insert.columns.count)) did not equal value count (\(row.count)): \(model)."
)
}
self.insert.values.append(.init(row.values))
self.insert.values.append(.init(row.map { $0.1 }))
return self
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/SQLKit/Builders/SQLQueryBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public protocol SQLQueryBuilder: class {

/// Connection to execute query on.
var database: SQLDatabase { get }

func run() -> EventLoopFuture<Void>
}

extension SQLQueryBuilder {
Expand Down
Loading

0 comments on commit 371bdf2

Please sign in to comment.