@@ -274,6 +274,10 @@ private extension FetchRequest.Predicate.Expression {
274274 return try function. sqlFragment ( for: entity, predicate: predicate)
275275 case . attribute, . relationship:
276276 return SQLFragment ( sql: " ? " , bindings: [ try constantBinding ( predicate: predicate) ] )
277+ case . arithmetic:
278+ // - TODO: Translate arithmetic expressions to SQL. Until then they are
279+ // rejected so the caller can fall back to in-memory evaluation.
280+ throw SQLiteDatabaseError . invalidPredicate ( predicate)
277281 }
278282 }
279283
@@ -291,7 +295,7 @@ private extension FetchRequest.Predicate.Expression {
291295 case . toMany:
292296 throw SQLiteDatabaseError . invalidPredicate ( predicate)
293297 }
294- case . keyPath, . function:
298+ case . keyPath, . function, . arithmetic :
295299 throw SQLiteDatabaseError . invalidPredicate ( predicate)
296300 }
297301 }
@@ -336,7 +340,10 @@ internal extension EntityDescription {
336340 if property. rawValue == SQLiteDatabase . primaryKeyColumn {
337341 return true
338342 }
339- if attributes. contains ( where: { $0. id == property } ) {
343+ // Composite attributes are expanded into leaf columns named by dotted path, so an
344+ // element key path such as `address.location.latitude` matches its column directly.
345+ // A composite attribute *as a whole* deliberately does not: it has no single column.
346+ if attributeColumns. contains ( where: { $0. name == property. rawValue } ) {
340347 return true
341348 }
342349 return relationships. contains ( where: { $0. id == property && $0. type == . toOne } )
0 commit comments