Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
helje5 committed Oct 3, 2024
2 parents 4fd43bb + 8ef2f23 commit 212e3aa
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 61 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ jobs:
image:
- swift:5.9.2-focal
- swift:5.10-jammy
- swift:5.10.1-noble
- swift:6.0-noble
container: ${{ matrix.image }}
steps:
- name: Install SQLite
run: |
apt-get -qq update
apt-get -y -qq install libsqlite3-dev
- name: Checkout Repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Install Override Package.swift
run: cp .github/_support/Package.swift .github/_support/Package\@swift-5.swift .
- name: Build Swift Debug Package
Expand All @@ -34,11 +34,11 @@ jobs:
runs-on: macos-latest
steps:
- name: Select latest available Xcode
uses: maxim-lobanov/setup-xcode@v1.2.1
uses: maxim-lobanov/setup-xcode@v1.5.1
with:
xcode-version: latest
- name: Checkout Repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Log Swift Version
run: swift --version
- name: Install Override Package.swift
Expand Down
34 changes: 7 additions & 27 deletions Plugins/Enlighter/Enlighter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,9 @@ extension Enlighter: XcodeBuildToolPlugin {
}
}


fileprivate func locateConfigFile(in context: XcodePluginContext) -> URL? {
#if false && compiler(>=6) && canImport(Foundation) // TODO: 16 Beta 2?
let dirURL = URL(filePath: context.package.directory.string)
#if compiler(>=6) && canImport(Foundation) // TODO: 16 Beta 2?
let dirURL = context.package.directoryURL
let url = dirURL.appending(component: configFileName,
directoryHint: .notDirectory)
#else
Expand Down Expand Up @@ -469,7 +468,7 @@ extension Enlighter: XcodeBuildToolPlugin {
var buildCommands = [ Command ]()

for group in groups {
#if false && compiler(>=6) && canImport(Foundation)
#if compiler(>=6) && canImport(Foundation)
let outputURL = configuration.outputFile.flatMap {
context.pluginWorkDirectoryURL.appending(component: $0)
} ?? context.pluginWorkDirectoryURL
Expand Down Expand Up @@ -533,29 +532,7 @@ extension Enlighter: XcodeBuildToolPlugin {
inputFiles : inputFiles,
outputFiles : outputFiles
))

// So, in Xcode, if a resource is handled by Enlighter, Xcode itself
// doesn't copy the resource anymore. Likely a bug.
// So what we do is copy them ourselves into the plugin dir. They then
// get bundled properly.
let inResourceURLs = groups.map({ $0.resourceURLs }).reduce([], +)
try inResourceURLs.forEach { ( inputResource : URL ) in
let outResourceFile = context.pluginWorkDirectory // TODO: Xcode16b2?
.appending(inputResource.lastPathComponent)
let outResourceURL = URL(fileURLWithPath: outResourceFile.string)

buildCommands.append(.buildCommand(
displayName : "Copy \(group.stem) resource "
+ "\(inputResource.lastPathComponent) into \(target.name)",
executable : try context.tool(named: "cp").url,
arguments : [ "-a",
inputResource .path(percentEncoded: false),
outResourceURL.path(percentEncoded: false) ],
inputFiles : [ inputResource ],
outputFiles : [ outResourceURL ]
))
}
#else
#else // Xcode <16
let inputFiles : [ Path ] = {
var inputFiles = group.matches.map { Path($0.path) }
if let configURL = configuration.configURL {
Expand Down Expand Up @@ -588,6 +565,9 @@ extension Enlighter: XcodeBuildToolPlugin {
outputFiles : outputFiles
))

// Fixed in Xcode 16, and maybe in 15.4 already.
// The fix resulted in this issue I think:
// https://github.com/Lighter-swift/Lighter/issues/27
// So, in Xcode, if a resource is handled by Enlighter, Xcode itself
// doesn't copy the resource anymore. Likely a bug.
// So what we do is copy them ourselves into the plugin dir. They then
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// Created by Helge Heß.
// Copyright © 2022 ZeeZide GmbH.
// Copyright © 2022-2024 ZeeZide GmbH.
//

import LighterCodeGenAST
Expand Down Expand Up @@ -89,9 +89,9 @@ extension EnlighterASTGenerator {

case .bool:
switch value {
case "true", "YES", "1" : return .true
case "false", "NO", "0" : return .false
default : return cannotConvert()
case "true", "YES", "1", "TRUE" : return .true
case "false", "NO", "0", "FALSE" : return .false
default : return cannotConvert()
}

case .date:
Expand Down Expand Up @@ -140,6 +140,49 @@ extension EnlighterASTGenerator {
( "uuid", .tuple(value.map { .integer(Int($0)) }) )
])
}
case .currentDate, .currentTime: // YYYY-MM-DD & HH:MM:SS
// those only make sense for string properties?
switch property.propertyType {
case .integer, .double, .decimal, .bool, .date, .url, .uint8Array,
.data, .uuid, .custom:
return cannotConvert()
case .string:
/* TODO: https://github.com/Lighter-swift/Lighter/issues/34
needs to generate this:
var fmt = DateFormatter()
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "yyyy-MM-dd" // "HH:mm:ss"
fmt.timeZone = TimeZone(secondsFromGMT: 0)
Date().string(from: Date())
*/
return cannotConvert()
}
case .currentTimestamp:
switch property.propertyType {
case .decimal, .bool, .url, .uint8Array, .data, .uuid, .custom:
return cannotConvert()
case .date:
return .call(name: "Foundation.Date()")
case .double:
return .variableReference(
instance: "Foundation.Date()", name: "timeIntervalSince1970")
case .integer:
return .cast(
.variableReference(
instance: "Foundation.Date()", name: "timeIntervalSince1970"),
to: .int
)
case .string:
/* TODO: https://github.com/Lighter-swift/Lighter/issues/34
needs to generate this:
var fmt = DateFormatter()
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
fmt.timeZone = TimeZone(secondsFromGMT: 0)
Date().string(from: Date())
*/
return cannotConvert()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ public func generateSwiftInitForSchema(_ schema: Schema,
if let value = column.defaultValue {
source += ", defaultValue: "
switch value {
case .null : source += ".null"
case .integer(let v) : source += ".integer(\(v))"
case .real (let v) : source += ".real(\(v))"
case .text (let v) : source += ".text(\"\(v)\")"
case .blob (let v) : source += ".blob(\(v) /* Not implemented */)"
case .null : source += ".null"
case .integer(let v) : source += ".integer(\(v))"
case .real (let v) : source += ".real(\(v))"
case .text (let v) : source += ".text(\"\(v)\")"
case .blob (let v) : source += ".blob(\(v) /* Not implemented */)"
case .currentDate : source += ".currentDate"
case .currentTime : source += ".currentTime"
case .currentTimestamp : source += ".currentTimestamp"
}
}
if column.isPrimaryKey { source += ", isPrimaryKey: true" }
Expand Down
9 changes: 0 additions & 9 deletions Sources/Lighter/Schema/SQLiteValueType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -699,13 +699,6 @@ extension URL {
}
}

#if compiler(<6) // Unavailable due to a swiftc crasher in 16b6
//SILFunction type mismatch for 'NSDecimalString':
// '$@convention(c) (UnsafePointer<Decimal>, Optional<AnyObject>)
// -> @autoreleased Optional<NSString>'
// !=
// '$@convention(c) (UnsafePointer<Decimal>, Optional<AnyObject>)
// -> @autoreleased NSString'
extension Decimal : SQLiteValueType {

public struct SQLCouldNotParseDecimal: Swift.Error, Sendable {
Expand Down Expand Up @@ -759,7 +752,6 @@ extension Decimal : SQLiteValueType {
.bind(unsafeSQLite3StatementHandle: stmt, index: index, then: execute)
}
}
#endif // compiler(<6) // Unavailable due to a swiftc crasher in 16b6

extension UUID : SQLiteValueType {

Expand Down Expand Up @@ -855,5 +847,4 @@ extension UUID : SQLiteValueType {
}
}
}

#endif // canImport(Foundation)
111 changes: 105 additions & 6 deletions Sources/SQLite3Schema/Column.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ extension Schema {
case real(Double)
case text(String)
case blob([ UInt8 ])

case currentTime
case currentDate
case currentTimestamp
}

/// The internal SQLite3 identifier of the column.
Expand Down Expand Up @@ -79,7 +83,7 @@ extension Schema {
public extension Schema.Column.DefaultValue {

/**
* Returns the ``Schema/TypeAffinity`` of the column.
* Returns the ``Schema/TypeAffinity`` of the columns default value.
*
* In SQLite columns can store any type, even if declared otherwise.
* E.g. you can insert a a TEXT into an INT column, and the TEXT will be
Expand All @@ -98,6 +102,7 @@ public extension Schema.Column.DefaultValue {
case .real : return .real
case .text : return .text
case .blob : return .blob
case .currentDate, .currentTime, .currentTimestamp : return .text
}
}
}
Expand Down Expand Up @@ -142,8 +147,8 @@ public extension Schema.Column {
if rc == SQLITE_DONE { break }
else if rc != SQLITE_ROW { return nil }

if let fkey = Schema.Column(stmt) {
columns.append(fkey)
if let columnInfo = Schema.Column(stmt) {
columns.append(columnInfo)
}
else {
assertionFailure("Could not create foreign key?!")
Expand Down Expand Up @@ -234,7 +239,7 @@ fileprivate extension Schema.Column {

// Distinguish between an explicit NULL (which defaultValue can't store?),
// and just NULL (which then the default fallback is)
let defaultValue = DefaultValue(stmt, 4)
let defaultValue = DefaultValue(stmt, 4, type: type)
self.defaultValue = defaultValue == .null ? nil : defaultValue

isPrimaryKey = sqlite3_column_int64(stmt, 5) != 0
Expand All @@ -250,12 +255,106 @@ fileprivate extension Schema.Column.DefaultValue {
* - stmt: A SQLite API statement handle
* - iCol: The column in the result set, 0-based.
*/
init(_ stmt: OpaquePointer?, _ iCol: Int32) {
init(_ stmt: OpaquePointer?, _ iCol: Int32, type: Schema.ColumnType?) {
guard iCol >= 0 && iCol < sqlite3_column_count(stmt) else {
assertionFailure("Column out of range: \(iCol)")
self = .null
return
}

// This actually contains SQL
// https://www.sqlite.org/lang_createtable.html#the_default_clause
func buildForText(_ text: String, type: Schema.ColumnType?) -> Self {
switch text {
// This happens if the NULL is explicitly specified, like:
// `street VARCHAR DEFAULT NULL`
case "NULL" : return .null
case "CURRENT_TIME" : return .currentTime
case "CURRENT_DATE" : return .currentDate
case "CURRENT_TIMESTAMP" : return .currentTimestamp
default : break
}
// There could be dynamic expressions, like `round(julianday('now'))`,
// but we can't resolve such?

// Some shortcuts for simple constants.
switch type {
case .int, .integer:
if let value = Int64(text) { return .integer(value) }
case .real:
if let value = Double(text) { return .real(value) }
default:
break
}

// This is a "little" inefficient, but an easy starting point.
// TODO: Figure out what to do on issues
let sql = "SELECT \(text);"
var memDBMaybe: OpaquePointer?
guard sqlite3_open_v2(":memory:", &memDBMaybe, SQLITE_OPEN_READONLY,
nil) == SQLITE_OK, let db = memDBMaybe else
{
print("WARN: Could not open in-mem DB for DEFAULT eval.") // eek
return .null
}
defer { sqlite3_close(db) }
var stmt : OpaquePointer?
guard sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK else {
print("WARN: Could not parse DEFAULT value:", text) // eek
return .null
}
defer { sqlite3_finalize(stmt); stmt = nil }

let rc = sqlite3_step(stmt)
guard rc == SQLITE_ROW else {
print("WARN: DEFAULT value produced no row?:", text) // eek
return .null
}

switch sqlite3_column_type(stmt, 0) {
case SQLITE_NULL:
return .null

case SQLITE_INTEGER:
return .integer(sqlite3_column_int64(stmt, 0))

case SQLITE_TEXT:
if let cstr = sqlite3_column_text(stmt, 0) {
return .text(String(cString: cstr))
}
else {
assertionFailure("Unexpected NULL in TEXT affinity default value")
return .null
}

case SQLITE_FLOAT:
return .real(sqlite3_column_double(stmt, 0))

case SQLITE_BLOB:
if let blob = sqlite3_column_blob(stmt, 0) {
let count = Int(sqlite3_column_bytes(stmt, 0))
let buffer = UnsafeRawBufferPointer(start: blob, count: count)
return .blob([UInt8](buffer))
}
else {
assertionFailure("Unexpected NULL in BLOB affinity default value")
return .null
}

default:
if let cstr = sqlite3_column_text(stmt, 0) {
return .text(String(cString: cstr))
}
else {
assertionFailure("Unexpected NULL in TEXT affinity default value")
return .null
}
}
}

// hh(2024-10-03): This always seems to be TEXT or NULL?
// The column does not contain the value, but the SQL expression producing
// a value.
switch sqlite3_column_type(stmt, iCol) {
case SQLITE_NULL:
self = .null
Expand All @@ -265,7 +364,7 @@ fileprivate extension Schema.Column.DefaultValue {

case SQLITE_TEXT:
if let cstr = sqlite3_column_text(stmt, iCol) {
self = .text(String(cString: cstr))
self = buildForText(String(cString: cstr), type: type)
}
else {
assertionFailure("Unexpected NULL in TEXT affinity default value")
Expand Down
Loading

0 comments on commit 212e3aa

Please sign in to comment.