Skip to content

Commit

Permalink
cleanup (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntVil authored Sep 26, 2024
1 parent e7b1b81 commit 8e44041
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 101 deletions.
17 changes: 9 additions & 8 deletions Sources/ClickHouseVapor/Application+ClickHouseNIO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Created by Patrick Zippenfenig on 2020-11-10.
//

import Vapor
import ClickHouseNIO
import Vapor

@_exported import struct NIO.TimeAmount

Expand Down Expand Up @@ -34,13 +34,14 @@ public struct ClickHousePoolConfiguration {
public let maxConnectionsPerEventLoop: Int
public let requestTimeout: TimeAmount

public init(hostname: String = "localhost",
port: Int = ClickHouseConnection.defaultPort,
user: String? = nil,
password: String? = nil,
database: String? = nil,
maxConnectionsPerEventLoop: Int = 1,
requestTimeout: TimeAmount = .seconds(10)
public init(
hostname: String = "localhost",
port: Int = ClickHouseConnection.defaultPort,
user: String? = nil,
password: String? = nil,
database: String? = nil,
maxConnectionsPerEventLoop: Int = 1,
requestTimeout: TimeAmount = .seconds(10)
) throws {
self.configuration = try ClickHouseConfiguration(
hostname: hostname,
Expand Down
6 changes: 1 addition & 5 deletions Sources/ClickHouseVapor/ClickHouseColumnConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

import ClickHouseNIO



/// Define how a colun can be converted into a clickhose datatype
public protocol ClickHouseColumnConvertible: AnyObject {
var key: String { get }
Expand All @@ -33,7 +31,6 @@ public protocol ClickHouseColumnConvertibleTyped: ClickHouseColumnConvertible {
associatedtype Value: ClickHouseDataType
var wrappedValue: [Value] { get set }
var columnMetadata: ClickHouseColumnMetadata? { get }

}

extension ClickHouseColumnConvertibleTyped {
Expand Down Expand Up @@ -89,7 +86,6 @@ public final class Field<Value: ClickHouseDataType>: ClickHouseColumnConvertible
self
}


fileprivate init(
key: String,
isPrimary: Bool = false,
Expand Down Expand Up @@ -209,7 +205,7 @@ extension Array {
/// Only include column rows where the isIncluded array is true
func filtered(_ isIncluded: [Bool]) -> Self {
precondition(count == isIncluded.count)
var arr = Self.init()
var arr = Self()
let count = isIncluded.reduce(0, { $0 + ($1 ? 1 : 0) })
arr.reserveCapacity(count)
for (i, include) in isIncluded.enumerated() where include {
Expand Down
66 changes: 16 additions & 50 deletions Sources/ClickHouseVapor/ClickHouseEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import Foundation
import enum ClickHouseNIO.ClickHouseTypeName

/// Abstract a clickhouse storage engine. For example the `ReplacingMergeTree` requires special CREATE syntax.
/// Abstract a clickHouse storage engine. For example the `ReplacingMergeTree` requires special CREATE syntax.
/// This could also be used to
public protocol ClickHouseEngine {
public protocol ClickHouseEngine: Sendable {
/// Generate a SQL query to create the table using the defined model columns
func createTableQuery(columns: [ClickHouseColumnConvertible]) -> String

Expand All @@ -26,23 +26,23 @@ public protocol ClickHouseEngine {

extension ClickHouseEngine {
public var isUsingCluster: Bool {
cluster != nil
self.cluster != nil
}

/// Returns the tablename and database name encoded with a dot
/// Returns the table name and database name encoded with a dot
public var tableWithDatabase: String {
if let database = database {
return "`\(database)`.`\(table)`"
if let database = self.database {
return "`\(database)`.`\(self.table)`"
}
return "`\(table)`"
return "`\(self.table)`"
}
}

public struct ClickHouseEngineReplacingMergeTree: ClickHouseEngine {
public var table: String
public var database: String?
public var cluster: String?
public var partitionBy: String?
public let table: String
public let database: String?
public let cluster: String?
public let partitionBy: String?

public init(table: String, database: String?, cluster: String?, partitionBy: String?) {
self.table = table
Expand Down Expand Up @@ -77,7 +77,7 @@ public struct ClickHouseEngineReplacingMergeTree: ClickHouseEngine {
PRIMARY KEY (\(ids.joined(separator: ",")))
"""
if let partitionBy = partitionBy {
query += " PARTITION BY (\(partitionBy))"
query += " PARTITION BY (\(partitionBy))"
}
query += " ORDER BY (\(order.joined(separator: ",")))"
return query
Expand All @@ -89,50 +89,16 @@ extension ClickHouseTypeName {
// basically all numerical data types except for Decimal support LowCardinality
// https://clickhouse.tech/docs/en/sql-reference/data-types/lowcardinality/
switch self {
case .float:
return true
case .float64:
return true
case .int8:
return true
case .int16:
return true
case .int32:
return true
case .int64:
return true
case .uint8:
return true
case .uint16:
return true
case .uint32:
return true
case .uint64:
case .float, .float64, .int8, .int16, .int32, .int64, .uint8, .uint16, .uint32, .uint64:
return true
case .uuid:
return false
case .fixedString(_):
return true
case .string:
case .fixedString, .string:
return true
case .nullable(let type):
return type.supportsLowCardinality
case .array:
case .array, .boolean, .date, .date32, .dateTime, .dateTime64, .enum16, .enum8:
return false
case .boolean:
return false
case .date:
return false
case .date32:
return false
case .dateTime(_):
return false
case .dateTime64(_):
return false
case .enum16(_):
return false
case .enum8(_):
return false
}
}
}
}
16 changes: 7 additions & 9 deletions Sources/ClickHouseVapor/ClickHouseModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
// Created by Patrick Zippenfenig on 2020-11-10.
//

import ClickHouseNIO
import Foundation
import Vapor
import ClickHouseNIO


public protocol ClickHouseModel: AnyObject {
static var engine: ClickHouseEngine { get }
Expand All @@ -19,7 +18,7 @@ extension ClickHouseModel {
/// Get the number of rows.
/// In case only some rows are populated (e.g. only certain ), the first column with more than 0 rows is considered.
public var count: Int {
return properties.first(where: {$0.count > 0})?.count ?? 0
return properties.first(where: { $0.count > 0 })?.count ?? 0
}

/// Only include column rows where the isIncluded array is true.
Expand Down Expand Up @@ -55,7 +54,7 @@ extension ClickHouseModel {
allMirrors.append(superMirror)
}
return allMirrors.reversed().flatMap { m in
m.children.compactMap {
m.children.compactMap {
$0.value as? ClickHouseColumnConvertible
}
}
Expand All @@ -66,7 +65,7 @@ extension ClickHouseModel {
on connection: ClickHouseConnectionProtocol,
engine: ClickHouseEngine? = nil
) -> EventLoopFuture<Void> {
let fields = Self.init().properties
let fields = Self().properties
let engine = engine ?? Self.engine
let query = engine.createTableQuery(columns: fields)
connection.logger.debug("\(query)")
Expand Down Expand Up @@ -128,11 +127,11 @@ extension ClickHouseModel {
sql: String
) -> EventLoopFuture<Self> {
connection.logger.debug("\(sql)")
let this = Self.init()
let this = Self()
let properties = this.properties
return connection.query(sql: sql).flatMapThrowing { res -> Self in
try res.columns.forEach { column in
guard let prop = properties.first(where: {$0.key == column.name}) else {
guard let prop = properties.first(where: { $0.key == column.name }) else {
return
}
try prop.setClickHouseArray(column.values)
Expand All @@ -153,9 +152,8 @@ extension ClickHouseModel {
offset: Int? = nil,
engine: ClickHouseEngine? = nil
) -> EventLoopFuture<Self> {

let engine = engine ?? Self.engine
let fields = fields ?? Self.init().properties.map { "`\($0.key)`" }
let fields = fields ?? Self().properties.map { "`\($0.key)`" }

var sql = "SELECT "
sql += fields.joined(separator: ",")
Expand Down
56 changes: 27 additions & 29 deletions Tests/ClickHouseVaporTests/ClickHouseVaporTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import XCTest
@testable import ClickHouseVapor
import Foundation
import Vapor
import XCTest

@testable import ClickHouseVapor

extension Application {
func configureClickHouseDatabases() throws {
Expand Down Expand Up @@ -40,10 +41,9 @@ public class TestModel: ClickHouseModel {
@Field(key: "dat")
var dat: [ClickHouseDate]


/// Not implemented on test-server
// @Field(key: "dat32")
// var dat32: [ClickHouseDate32]
// var dat32: [ClickHouseDate32]

@Field(key: "datt")
var datt: [ ClickHouseDateTime ]
Expand All @@ -53,22 +53,20 @@ public class TestModel: ClickHouseModel {

@Field(key: "datt64", precision: 3)
var datt64: [ ClickHouseDateTime64 ]

@Field(key: "datt64z", precision: 3, timeZone: "'GMT'")
var datt64z: [ ClickHouseDateTime64 ]

@Field(key: "en8", mapping: ["a": 0, "b": 1])
var en8: [ ClickHouseEnum8 ]

@Field(key: "en16", mapping: ["a": 12, "b": 1, "c": 600])
var en16: [ ClickHouseEnum16 ]

@Field(key: "temperature")
var temperature: [Float]

required public init() {

}
public required init() {}

public static var engine: ClickHouseEngine {
return ClickHouseEngineReplacingMergeTree(
Expand Down Expand Up @@ -105,7 +103,6 @@ public final class InheritedTestModel: TestParentClass, ClickHouseModel {
}

final class ClickHouseVaporTests: XCTestCase {

static var allTests = [
("testPing", testPing),
("testModel", testModel),
Expand Down Expand Up @@ -134,7 +131,7 @@ final class ClickHouseVaporTests: XCTestCase {

model.id = [ "x010", "ax51", "cd22" ]
model.fixed = [ "", "123456", "12345678901234" ]
model.arr = [[1], [], [76, 56, 2]]
model.arr = [[1], [], [76, 56, 2]]
model.dat = [.clickhouseDefault, .clickhouseDefault, .clickhouseDefault]
model.datt = [.clickhouseDefault, .clickhouseDefault, .clickhouseDefault]
model.datt64 = [.clickhouseDefault, .clickhouseDefault, .clickhouseDefault]
Expand All @@ -144,7 +141,7 @@ final class ClickHouseVaporTests: XCTestCase {
model.en16 = [.init(word: "a"), .init(word: "b"), .init(word: "c")]
model.timestamp = [ 100, 200, 300 ]
model.temperature = [ 11.1, 10.4, 8.9 ]

let createQuery = TestModel.engine.createTableQuery(columns: model.properties)
XCTAssertEqual(createQuery
.replacingOccurrences(of: "Enum8('b'=1,'a'=0)", with: "Enum8('a'=0,'b'=1)")
Expand All @@ -166,16 +163,16 @@ final class ClickHouseVaporTests: XCTestCase {
XCTAssertEqual(model.id, model2.id)
XCTAssertEqual(["", "123456", "1234567890"], model2.fixed)
XCTAssertEqual(model.timestamp, model2.timestamp)
XCTAssertEqual(model.dat.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.dat.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.datt.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.datt.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.dattz.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.dattz.map { $0.date}, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.en8.map { $0.word}, ["a", "b", "a"])
XCTAssertEqual(model2.en8.map { $0.word}, ["a", "b", "a"])
XCTAssertEqual(model.en16.map { $0.word}, ["a", "b", "c"])
XCTAssertEqual(model2.en16.map { $0.word}, ["a", "b", "c"])
XCTAssertEqual(model.dat.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.dat.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.datt.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.datt.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.dattz.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model2.dattz.map { $0.date }, [Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0), Date(timeIntervalSince1970: 0.0)])
XCTAssertEqual(model.en8.map { $0.word }, ["a", "b", "a"])
XCTAssertEqual(model2.en8.map { $0.word }, ["a", "b", "a"])
XCTAssertEqual(model.en16.map { $0.word }, ["a", "b", "c"])
XCTAssertEqual(model2.en16.map { $0.word }, ["a", "b", "c"])
XCTAssertEqual(model.arr, [[1], [], [76, 56, 2]])
XCTAssertEqual(model2.arr, [[1], [], [76, 56, 2]])

Expand All @@ -192,7 +189,7 @@ final class ClickHouseVaporTests: XCTestCase {
XCTAssertEqual(filtered.id, ["ax51", "x010"])
XCTAssertEqual(filtered.timestamp, [200, 100])

/// Raw select query, that gets applied to
// Raw select query, that gets applied to
let model3 = try! TestModel.select(
on: app.clickHouse,
sql: "SELECT timestamp, stationID FROM default.test"
Expand All @@ -210,7 +207,8 @@ final class ClickHouseVaporTests: XCTestCase {

let model = InheritedTestModel()
let createQuery = InheritedTestModel.engine.createTableQuery(columns: model.properties)
XCTAssertEqual(createQuery,
XCTAssertEqual(
createQuery,
"""
CREATE TABLE IF NOT EXISTS `testInherited` (timestamp Int64,stationID LowCardinality(String),temperature Float32)
ENGINE = ReplacingMergeTree()
Expand All @@ -222,7 +220,7 @@ final class ClickHouseVaporTests: XCTestCase {
try! InheritedTestModel.deleteTable(on: app.clickHouse).wait()
// create table
try! InheritedTestModel.createTable(on: app.clickHouse).wait()

// fill model with data and insert it
model.id = [ "x010", "ax51", "cd22" ]
model.timestamp = [ 100, 200, 300 ]
Expand All @@ -232,9 +230,9 @@ final class ClickHouseVaporTests: XCTestCase {
// select the data again
let model2 = try! InheritedTestModel.select(on: app.clickHouse).wait()

XCTAssertEqual(model2.id, model.id)
XCTAssertEqual(model2.timestamp, model.timestamp)
XCTAssertEqual(model2.temperature, model.temperature)
XCTAssertEqual(model2.id, model.id)
XCTAssertEqual(model2.timestamp, model.timestamp)
XCTAssertEqual(model2.temperature, model.temperature)
}

/// insert should fail if some columns are not set, but others are
Expand Down

0 comments on commit 8e44041

Please sign in to comment.