stephencelis / SQLite.swift

A type-safe, Swift-language layer over SQLite3.
MIT License
9.57k stars 1.54k forks source link

Custom function cannot return Blob/Data #1220

Open tylerbrandt opened 11 months ago

tylerbrandt commented 11 months ago

Issues are used to track bugs and feature requests. Need help or have a general question? Ask on Stack Overflow (tag sqlite.swift).

Build Information

0.14.1

Xcode 14.3.1 Ventura 13.5

SPM

General guidelines

When implementing a function that returns Data (or any type bridged to Blob), the results don't seem to be propagating to the database correctly. The stored value has the correct data length but the values are all 0. See example playground code:

import Foundation
import SQLite

let db = try Connection(.inMemory)
db.trace { print("\($0);") }

// A Swift function to convert a string into Data (for testing purposes)
func data_literal(_ message: String) -> Data {
  message.data(using: .utf8)!
}

// A SQL function that returns the same data
let data_func = try db.createFunction("data_func", { message in
  data_literal(message)
})

try db.run(Table("blob").create { tbl in
  tbl.column(Expression<Blob>("blob_func"), primaryKey: true)
  tbl.column(Expression<Blob>("blob_literal"))
})

try db.run(Table("blob").insert(Expression<Data>("blob_func") <- data_func(Expression<String>("hello world")), Expression<Data>("blob_literal") <- data_literal("hello world")))
let blobresults = try db.prepare(Table("blob"))
for result in blobresults {
  // Prints blob_func x'0000000000000000000000'
  print("blob_func", result[Expression<Blob>("blob_func")])
  // Prints blob_literal x'68656c6c6f20776f726c64'
  print("blob_literal", result[Expression<Blob>("blob_literal")])
}

// Doesn't crash because of different data length, but results are still wrong
try db.run(Table("blob").insert(Expression<Data>("blob_func") <- data_func(Expression<String>("foobar")), Expression<Data>("blob_literal") <- data_literal("foobar")))
let blobresults2 = try db.prepare(Table("blob"))
for result in blobresults2 {
  // Prints blob_func x'000000000000'
  print("blob_func", result[Expression<Blob>("blob_func")])
  // Prints blob_literal x'666f6f626172'
  print("blob_literal", result[Expression<Blob>("blob_literal")])
}

// Crashes here due to uniqueness constraint on blob_func (same length as hello world)
try db.run(Table("blob").insert(Expression<Data>("blob_func") <- data_func(Expression<String>("hello earth")), Expression<Data>("blob_literal") <- data_literal("hello earth")))
let blobresults3 = try db.prepare(Table("blob"))
for result in blobresults3 {
  print("blob_func", result[Expression<Blob>("blob_func")])
  print("blob_literal", result[Expression<Blob>("blob_literal")])
}

Prints

CREATE TABLE "blob" ("blob_func" BLOB PRIMARY KEY NOT NULL, "blob_literal" BLOB NOT NULL);
INSERT INTO "blob" ("blob_func", "blob_literal") VALUES ("data_func"("hello world"), x'68656c6c6f20776f726c64');
SELECT * FROM "blob";
blob_func x'0000000000000000000000'
blob_literal x'68656c6c6f20776f726c64'
INSERT INTO "blob" ("blob_func", "blob_literal") VALUES ("data_func"("foobar"), x'666f6f626172');
SELECT * FROM "blob";
blob_func x'0000000000000000000000'
blob_literal x'68656c6c6f20776f726c64'
blob_func x'000000000000'
blob_literal x'666f6f626172'
INSERT INTO "blob" ("blob_func", "blob_literal") VALUES ("data_func"("hello earth"), x'68656c6c6f206561727468');
Swift/ErrorType.swift:200: Fatal error: Error raised at top level: UNIQUE constraint failed: blob.blob_func (code: 19)
2023-07-14 12:37:44.059336-0700 SQLiteXidDemo[46862:21639780] Swift/ErrorType.swift:200: Fatal error: Error raised at top level: UNIQUE constraint failed: blob.blob_func (code: 19)

This came up for me when trying to use createFunction to emit new Id values from https://github.com/uditha-atukorala/swift-xid; after conforming the type to Value everything compiled correctly, but as soon as I tried to set the second row, I hit the uniqueness constraint, and upon inspection saw that the value inserted was 12 bytes of 0.