Parsing Swift Predicates into Database Statements with PredicateExpressions - Stack Overflow

admin2025-05-01  3

Context

I am attempting to take a Swift Predicate created with the #Predicate macro and parse it into a database query statement. (SwiftData does this under the hood to query the underlying SQLite database that powers Core Data).

Example:

class Foo
{
    var title: String = ""
}

let p = #Predicate<Foo> { $0.title == "test" }

Given that predicate, p, I want to turn it into this string-ified query statement:

SELECT * FROM `Foo` WHERE `title` = "test"

Question

The process for doing this is very sparsely documented. I'm following that example by declaring a custom protocol and then having each type of PredicateExpression that I support adopt that protocol. (This code is very proof-of-concept at the moment; it ignores errors and handles only this very simple Predicate for now):

protocol DBPredicateExpression {
    func dbQuery() -> String
}


// Handle the "==" operator
extension PredicateExpressions.Equal: DBPredicateExpression where LHS: DBPredicateExpression, RHS: DBPredicateExpression
{
    func dbQuery() -> String {
        return lhs.dbQuery() + " = " + rhs.dbQuery()
    }
} 


// Handle constant values
extension PredicateExpressions.Value: DBPredicateExpression
{
    func dbQuery() -> String {
        if let v = value as? String {
            return "\"\(v)\""
        }

        return ""  // TODO: handle other value types; throw for unsupported ones.
    }
}


// Handle KeyPaths
extension PredicateExpressions.Keypath: DBPredicateExpression
{
    func dbQuery() -> String
    {
        let rootType: Any.Type = type(of: root)      // String(describing:) --> Variable<Foo>
        let kpType: Any.Type = type(of: keyPath)     // String(describing:) --> ReferenceWritableKeyPath<Foo, String>

        // THIS IS WHERE I NEED HELP.
        // How do I introspect `root` and `keyPath` to get string versions of them?
        // This should return: "FROM `Foo` WHERE `title`"
        // But getting "Foo" and "title" from `root` and `keyPath` is the tricky bit.
    }
}

As stated in the comment: how do I transform the KeyPath and Root types into their String equivalents (title and Foo)? Using string(describing:) seems very fragile and non-performant. How does SwiftData take one of these KeyPath Predicates and transform it into the SQL statements that ultimately get run in Core Data?

转载请注明原文地址:http://anycun.com/QandA/1746028834a91558.html