Skip to content

Commit

Permalink
Adding Date as an available type
Browse files Browse the repository at this point in the history
  • Loading branch information
abandy committed Aug 3, 2024
1 parent 2400351 commit c6fa14c
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 14 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ This repo uses submodules and in order to build the arrow submodule will need to
#### String:
- LEN
- LOWER
#### Date:
- DATE: formats
- yyyy-MM-dd
- yyyy-MM-dd'T'HH:mm:ss
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd'T'HH:mm:ssZ
- yyyy-MM-dd HH:mm:ssZ

## Engine Config Options:

Expand Down
12 changes: 9 additions & 3 deletions Sources/FilterTasks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ public class FilterFactory {
case .col:
return FilterBuilderCol(predicate: predicate!, context: context)
case .colP:
if #available(iOS 13.0, *) {
return FilterBuilderColParallel(predicate: predicate!, context: context)
} else {
guard #unavailable(iOS 13.0) else {
return FilterBuilderCol(predicate: predicate!, context: context)
}

guard #unavailable(tvOS 13.0) else {
return FilterBuilderCol(predicate: predicate!, context: context)
}

return FilterBuilderColParallel(predicate: predicate!, context: context)
}
}
}
Expand Down Expand Up @@ -116,6 +120,8 @@ public class FilterBuilderRow: FilterBuilder {
}

@available(iOS 13.0, *)
@available(tvOS 13.0, *)
@available(watchOS 6.0, *)
public class FilterBuilderColParallel: FilterBuilder {
public let wrapper: PredicateBuilderColParallel
public var predicateNode: Relation.PredicateNode { wrapper.predicateNode }
Expand Down
3 changes: 2 additions & 1 deletion Sources/PredicateBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ public class PredicateBuilderBasic {
.UINT64: ComparionOps<UInt64>(),
.FLOAT: ComparionOps<Float>(),
.DOUBLE: ComparionOps<Double>(),
.VARCHAR: ComparionOps<String>()
.VARCHAR: ComparionOps<String>(),
.DATE: ComparionOps<Date>()
]
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/PredicateBuilderCol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ public class PredicateBuilderCol { // swiftlint:disable:this type_body_length
.UINT64: ComparionOps<UInt64>(),
.FLOAT: ComparionOps<Float>(),
.DOUBLE: ComparionOps<Double>(),
.VARCHAR: ComparionOps<String>()
.VARCHAR: ComparionOps<String>(),
.DATE: ComparionOps<Date>()
]
}

Expand Down
5 changes: 4 additions & 1 deletion Sources/PredicateBuilderColParallel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public protocol PredicateComparorColParallel {
}

@available(iOS 13.0, *)
@available(tvOS 13.0, *)
@available(watchOS 6.0, *)
public class PredicateBuilderColParallel { // swiftlint:disable:this type_body_length
public enum CompOps {
case EQ, NEQ, GT, LT, GTE, LTE
Expand Down Expand Up @@ -222,7 +224,8 @@ public class PredicateBuilderColParallel { // swiftlint:disable:this type_body_l
.UINT64: ComparionOps<UInt64>(),
.FLOAT: ComparionOps<Float>(),
.DOUBLE: ComparionOps<Double>(),
.VARCHAR: ComparionOps<String>()
.VARCHAR: ComparionOps<String>(),
.DATE: ComparionOps<Date>()
]
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/ProjectBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class ProjectBuilder {
try makeBuilder(Double.self, field: field)
case .VARCHAR:
try makeBuilder(String.self, field: field)
case .DATE:
try makeBuilder(Date.self, field: field)
}
}

Expand Down
59 changes: 59 additions & 0 deletions Sources/ScalarFuncs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ extension FieldScalarFuncDef {
return nil
}
return inputTypes[1] != .INT32 ? nil : POWER(inputTypes)
case .DATE:
if inputTypes.count != 1 {
return nil
}
return inputTypes[0] != .VARCHAR ? nil : DATE(inputTypes)
}
}

Expand Down Expand Up @@ -188,4 +193,58 @@ extension FieldScalarFuncDef {
return (data![0]! as! String).lowercased()
}
}

class DATE: ScalarFunc {
var sqlType: SqlType
var funcSqlType: SqlType {.DATE}
var date: Date?
var hasBuiltValue = false

required init(_ sqlType: [SqlType]) {
self.sqlType = .VARCHAR
self.date = nil
}

func buildValue(_ dateStr: String) {
var format = ""
self.hasBuiltValue = true
if dateStr.range(of: "^\\d{4}-\\d{2}-\\d{2}$", options: .regularExpression) != nil {
format = "yyyy-MM-dd"
} else if dateStr.range(of: "^\\d{4}-\\d{2}-\\d{2}[T\\s]\\d{2}:\\d{2}:\\d{2}$",
options: .regularExpression) != nil {
if dateStr.firstIndex(of: "T") != nil {
format = "yyyy-MM-dd'T'HH:mm:ss"
} else {
format = "yyyy-MM-dd HH:mm:ss"
}
} else if dateStr.range(of: "\\d{4}-\\d{2}-\\d{2}[T\\s]\\d{2}:\\d{2}:\\d{2}\\s*[\\+-]\\d{2}:\\d{2}",
options: .regularExpression) != nil {
if dateStr.firstIndex(of: "T") != nil {
format = "yyyy-MM-dd'T'HH:mm:ssZ"
} else {
format = "yyyy-MM-dd HH:mm:ssZ"
}
} else {
return
}

let currentDateFormatter = DateFormatter()
currentDateFormatter.dateFormat = format
self.date = currentDateFormatter.date(from: dateStr)
}

func getValue(_ data: [Any?]?) -> Any? {
if self.hasBuiltValue {
return self.date
}

if data == nil || data!.count != 1 || data![0] == nil {
self.hasBuiltValue = true
return nil
}

buildValue(data![0]! as! String)
return self.date
}
}
}
4 changes: 2 additions & 2 deletions Sources/SqlNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public enum Operator {

public enum SqlType: Int32 {
case BOOLEAN, INT8, UINT8, INT16, UINT16, UINT32, INT32
case INT64, UINT64, FLOAT, DOUBLE, VARCHAR
case INT64, UINT64, FLOAT, DOUBLE, VARCHAR, DATE
}

public enum JoinType {
Expand All @@ -34,7 +34,7 @@ public enum WindowFuncType: String {
}

public enum ScalarFuncType: String {
case ABS, LENGTH, LOWER, POWER
case ABS, LENGTH, LOWER, POWER, DATE
}

public class SqlNode {
Expand Down
2 changes: 2 additions & 0 deletions Sources/TableDef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public class TableDef: Equatable {
sqlType = SqlType.DOUBLE
case .string:
sqlType = SqlType.VARCHAR
case .date32, .date64:
sqlType = SqlType.DATE
default:
fatalError("Unsupported field type id: \(field.type.id)")
}
Expand Down
20 changes: 14 additions & 6 deletions Sources/Tasks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,26 @@ public class GroupByTask {
case .VARCHAR:
let value = field.getValue(data: row, context: self.context) as String?
if let val = value { data.append(val.data(using: .utf8)!)}
case .DATE:
let value = field.getValue(data: row, context: self.context) as Date?
if let val = value {
appendInt(Int(val.timeIntervalSince1970), data: &data, field: field)
}
}
}

if #available(iOS 13.0, *) {
let digest = CryptoKit.Insecure.MD5.hash(data: data)
return digest.map {
String(format: "%02hhx", $0)
}.joined()
} else {
guard #unavailable(tvOS 13.0) else {
return md5Hash(data: data)
}

guard #unavailable(iOS 13.0) else {
return md5Hash(data: data)
}

let digest = CryptoKit.Insecure.MD5.hash(data: data)
return digest.map {
String(format: "%02hhx", $0)
}.joined()
}

private func md5Hash (data: Data) -> String {
Expand Down
25 changes: 25 additions & 0 deletions Tests/DateFuncTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import Foundation
import XCTest
import Arrow
@testable import swiftqe

final class DateFuncTests: XCTestCase {
func testDATE() throws {
let dateFunc1 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNotNil(dateFunc1.getValue(["2014-06-02"]))
let dateFunc2 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNil(dateFunc2.getValue(["2014-06"]))
let dateFunc3 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNotNil(dateFunc3.getValue(["2024-08-07T15:12:30"]))
let dateFunc4 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNotNil(dateFunc4.getValue(["2024-08-07 00:00:00"]))
let dateFunc5 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNotNil(dateFunc5.getValue(["2024-08-07T15:12:30+05:00"]))
let dateFunc6 = FieldScalarFuncDef.getAggFunc(.DATE, inputTypes: [.VARCHAR])!
XCTAssertNotNil(dateFunc6.getValue(["2024-08-06 20:00:30 -05:00"]))
}
}
37 changes: 37 additions & 0 deletions Tests/SelectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ final class SelectTests: XCTestCase {
var stringField: String? = ""
}

class DateField: Codable {
var dateField: Date?
}

func testInt( // swiftlint:disable:this function_body_length
) throws {
do {
Expand Down Expand Up @@ -152,4 +156,37 @@ final class SelectTests: XCTestCase {
XCTFail("Error occured executing query: \(error)")
}
}

func testDate(
) throws {
do {
var testDates = [DateField]()
let currentDateFormatter = DateFormatter()
currentDateFormatter.dateFormat = "yyyy-MM-dd 00:00:00"
let baseDate = currentDateFormatter.date(from: "2024-08-01")!
let calendar = Calendar(identifier: .gregorian)
var dateCmoponents = calendar.dateComponents([.year, .month, .day], from: baseDate)
for _ in 0..<10 {
let date = DateField()
dateCmoponents.day! += 1
date.dateField = calendar.date(from: dateCmoponents)
testDates.append(date)
}

let engine = QueryEngine()
engine.add(try ArrowEncoder.encode(testDates)!, name: "tab")
let queryRb = try engine.run("SELECT dateField FROM tab")!
XCTAssertEqual(queryRb.length, 10)
XCTAssertEqual(queryRb.columnCount, 1)
let decoder = ArrowDecoder(queryRb)
let outputFields = try decoder.decode(DateField.self)
XCTAssertEqual(outputFields.count, 10)
for itemIndex in 0..<outputFields.count {
XCTAssertEqual(Int(outputFields[itemIndex].dateField!.timeIntervalSince1970),
Int(testDates[itemIndex].dateField!.timeIntervalSince1970))
}
} catch let error {
XCTFail("Error occured executing query: \(error)")
}
}
}
49 changes: 49 additions & 0 deletions Tests/WhereTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ final class WhereTests: XCTestCase {
var stringField: String? = ""
}

class DateField: Codable {
var dateField: Date?
}

func testSimpleInt() throws {
do {
var testInts = [IntFields]()
Expand Down Expand Up @@ -211,4 +215,49 @@ final class WhereTests: XCTestCase {
XCTFail("Error occured executing query: \(error)")
}
}

func testDate(
) throws {
do {
var testDates = [DateField]()
let currentDateFormatter = DateFormatter()
currentDateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
let baseDate = currentDateFormatter.date(from: "2024-08-01 00:00:00 +00:00")!
let calendar = Calendar(identifier: .gregorian)
var dateCmoponents = calendar.dateComponents(
[.year, .month, .day, .hour, .minute, .second, .timeZone], from: baseDate)
for _ in 0..<10 {
let date = DateField()
dateCmoponents.day! += 1
date.dateField = calendar.date(from: dateCmoponents)
testDates.append(date)
}

let queryInfos = [
(query: "SELECT dateField FROM tab WHERE dateField <= DATE('2024-08-07T00:00:00 +00:00')",
cnt: 6,
startIndex: 0),
(query: "SELECT dateField FROM tab WHERE dateField >= DATE('2024-08-06 20:00:30 -05:00')",
cnt: 4,
startIndex: 6)
]

for queryInfo in queryInfos {
let engine = QueryEngine()
engine.add(try ArrowEncoder.encode(testDates)!, name: "tab")
let queryRb = try engine.run(queryInfo.query)!
XCTAssertEqual(queryRb.length, UInt(queryInfo.cnt))
XCTAssertEqual(queryRb.columnCount, 1)
let decoder = ArrowDecoder(queryRb)
let outputFields = try decoder.decode(DateField.self)
XCTAssertEqual(outputFields.count, queryInfo.cnt)
for itemIndex in 0..<outputFields.count {
XCTAssertEqual(Int(outputFields[itemIndex].dateField!.timeIntervalSince1970),
Int(testDates[queryInfo.startIndex + itemIndex].dateField!.timeIntervalSince1970))
}
}
} catch let error {
XCTFail("Error occured executing query: \(error)")
}
}
}

0 comments on commit c6fa14c

Please sign in to comment.