RLMQueryUtil.mm 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMQueryUtil.hpp"
  19. #import "RLMAccessor.hpp"
  20. #import "RLMObjectSchema_Private.hpp"
  21. #import "RLMObject_Private.hpp"
  22. #import "RLMPredicateUtil.hpp"
  23. #import "RLMProperty_Private.h"
  24. #import "RLMSchema.h"
  25. #import "RLMUtil.hpp"
  26. #import <realm/object-store/object_store.hpp>
  27. #import <realm/object-store/results.hpp>
  28. #import <realm/query_engine.hpp>
  29. #import <realm/query_expression.hpp>
  30. #import <realm/util/cf_ptr.hpp>
  31. #import <realm/util/overload.hpp>
  32. using namespace realm;
  33. NSString * const RLMPropertiesComparisonTypeMismatchException = @"RLMPropertiesComparisonTypeMismatchException";
  34. NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException = @"RLMUnsupportedTypesFoundInPropertyComparisonException";
  35. NSString * const RLMPropertiesComparisonTypeMismatchReason = @"Property type mismatch between %@ and %@";
  36. NSString * const RLMUnsupportedTypesFoundInPropertyComparisonReason = @"Comparison between %@ and %@";
  37. namespace {
  38. // small helper to create the many exceptions thrown when parsing predicates
  39. [[gnu::cold]] [[noreturn]]
  40. void throwException(NSString *name, NSString *format, ...) {
  41. va_list args;
  42. va_start(args, format);
  43. NSString *reason = [[NSString alloc] initWithFormat:format arguments:args];
  44. va_end(args);
  45. @throw [NSException exceptionWithName:name reason:reason userInfo:nil];
  46. }
  47. // check a precondition and throw an exception if it is not met
  48. // this should be used iff the condition being false indicates a bug in the caller
  49. // of the function checking its preconditions
  50. void RLMPrecondition(bool condition, NSString *name, NSString *format, ...) {
  51. if (__builtin_expect(condition, 1)) {
  52. return;
  53. }
  54. va_list args;
  55. va_start(args, format);
  56. NSString *reason = [[NSString alloc] initWithFormat:format arguments:args];
  57. va_end(args);
  58. @throw [NSException exceptionWithName:name reason:reason userInfo:nil];
  59. }
  60. BOOL propertyTypeIsNumeric(RLMPropertyType propertyType) {
  61. switch (propertyType) {
  62. case RLMPropertyTypeInt:
  63. case RLMPropertyTypeFloat:
  64. case RLMPropertyTypeDouble:
  65. case RLMPropertyTypeDecimal128:
  66. case RLMPropertyTypeDate:
  67. case RLMPropertyTypeAny:
  68. return YES;
  69. default:
  70. return NO;
  71. }
  72. }
  73. bool propertyTypeIsLink(RLMPropertyType type) {
  74. return type == RLMPropertyTypeObject || type == RLMPropertyTypeLinkingObjects;
  75. }
  76. bool isObjectValidForProperty(id value, RLMProperty *prop) {
  77. if (prop.collection) {
  78. if (propertyTypeIsLink(prop.type)) {
  79. RLMObjectBase *obj = RLMDynamicCast<RLMObjectBase>(value);
  80. if (!obj) {
  81. obj = RLMDynamicCast<RLMObjectBase>(RLMBridgeSwiftValue(value));
  82. }
  83. if (!obj) {
  84. return false;
  85. }
  86. return [RLMObjectBaseObjectSchema(obj).className isEqualToString:prop.objectClassName];
  87. }
  88. return RLMValidateValue(value, prop.type, prop.optional, false, nil);
  89. }
  90. return RLMIsObjectValidForProperty(value, prop);
  91. }
  92. // Equal and ContainsSubstring are used by QueryBuilder::add_string_constraint as the comparator
  93. // for performing diacritic-insensitive comparisons.
  94. StringData get_string(Mixed const& m) {
  95. if (m.is_null())
  96. return StringData();
  97. if (m.get_type() == type_String)
  98. return m.get_string();
  99. auto b = m.get_binary();
  100. return StringData(b.data(), b.size());
  101. }
  102. bool equal(CFStringCompareFlags options, StringData v1, StringData v2)
  103. {
  104. if (v1.is_null() || v2.is_null()) {
  105. return v1.is_null() == v2.is_null();
  106. }
  107. auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
  108. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  109. auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
  110. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  111. return CFStringCompare(s1.get(), s2.get(), options) == kCFCompareEqualTo;
  112. }
  113. template <CFStringCompareFlags options>
  114. struct Equal {
  115. using CaseSensitive = Equal<options & ~kCFCompareCaseInsensitive>;
  116. using CaseInsensitive = Equal<options | kCFCompareCaseInsensitive>;
  117. bool operator()(Mixed v1, Mixed v2) const
  118. {
  119. return equal(options, get_string(v1), get_string(v2));
  120. }
  121. static const char* description() { return options & kCFCompareCaseInsensitive ? "==[cd]" : "==[d]"; }
  122. };
  123. template <CFStringCompareFlags options>
  124. struct NotEqual {
  125. using CaseSensitive = NotEqual<options & ~kCFCompareCaseInsensitive>;
  126. using CaseInsensitive = NotEqual<options | kCFCompareCaseInsensitive>;
  127. bool operator()(Mixed v1, Mixed v2) const
  128. {
  129. return !equal(options, get_string(v1), get_string(v2));
  130. }
  131. static const char* description() { return options & kCFCompareCaseInsensitive ? "!=[cd]" : "!=[d]"; }
  132. };
  133. bool contains_substring(CFStringCompareFlags options, StringData v1, StringData v2)
  134. {
  135. if (v2.is_null()) {
  136. // Everything contains NULL
  137. return true;
  138. }
  139. if (v1.is_null()) {
  140. // NULL contains nothing (except NULL, handled above)
  141. return false;
  142. }
  143. if (v2.size() == 0) {
  144. // Everything (except NULL, handled above) contains the empty string
  145. return true;
  146. }
  147. auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
  148. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  149. auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
  150. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  151. return CFStringFind(s1.get(), s2.get(), options).location != kCFNotFound;
  152. }
  153. template <CFStringCompareFlags options>
  154. struct ContainsSubstring {
  155. using CaseSensitive = ContainsSubstring<options & ~kCFCompareCaseInsensitive>;
  156. using CaseInsensitive = ContainsSubstring<options | kCFCompareCaseInsensitive>;
  157. bool operator()(Mixed v1, Mixed v2) const
  158. {
  159. return contains_substring(options, get_string(v1), get_string(v2));
  160. }
  161. static const char* description() { return options & kCFCompareCaseInsensitive ? "CONTAINS[cd]" : "CONTAINS[d]"; }
  162. };
  163. NSString *operatorName(NSPredicateOperatorType operatorType)
  164. {
  165. switch (operatorType) {
  166. case NSLessThanPredicateOperatorType:
  167. return @"<";
  168. case NSLessThanOrEqualToPredicateOperatorType:
  169. return @"<=";
  170. case NSGreaterThanPredicateOperatorType:
  171. return @">";
  172. case NSGreaterThanOrEqualToPredicateOperatorType:
  173. return @">=";
  174. case NSEqualToPredicateOperatorType:
  175. return @"==";
  176. case NSNotEqualToPredicateOperatorType:
  177. return @"!=";
  178. case NSMatchesPredicateOperatorType:
  179. return @"MATCHES";
  180. case NSLikePredicateOperatorType:
  181. return @"LIKE";
  182. case NSBeginsWithPredicateOperatorType:
  183. return @"BEGINSWITH";
  184. case NSEndsWithPredicateOperatorType:
  185. return @"ENDSWITH";
  186. case NSInPredicateOperatorType:
  187. return @"IN";
  188. case NSContainsPredicateOperatorType:
  189. return @"CONTAINS";
  190. case NSBetweenPredicateOperatorType:
  191. return @"BETWEEN";
  192. case NSCustomSelectorPredicateOperatorType:
  193. return @"custom selector";
  194. }
  195. return [NSString stringWithFormat:@"unknown operator %lu", (unsigned long)operatorType];
  196. }
  197. [[gnu::cold]] [[noreturn]]
  198. void unsupportedOperator(RLMPropertyType datatype, NSPredicateOperatorType operatorType) {
  199. throwException(@"Invalid operator type",
  200. @"Operator '%@' not supported for type '%@'",
  201. operatorName(operatorType), RLMTypeToString(datatype));
  202. }
  203. bool isNSNull(id value) {
  204. return !value || value == NSNull.null;
  205. }
  206. template<typename T>
  207. bool isNSNull(T) {
  208. return false;
  209. }
  210. Table& get_table(Group& group, RLMObjectSchema *objectSchema)
  211. {
  212. return *ObjectStore::table_for_object_type(group, objectSchema.objectStoreName);
  213. }
  214. // A reference to a column within a query. Can be resolved to a Columns<T> for use in query expressions.
  215. class ColumnReference {
  216. public:
  217. ColumnReference(Query& query, Group& group, RLMSchema *schema, RLMProperty* property, std::vector<RLMProperty*>&& links = {})
  218. : m_links(std::move(links)), m_property(property), m_schema(schema), m_group(&group), m_query(&query), m_link_chain(query.get_table())
  219. {
  220. for (const auto& link : m_links) {
  221. if (link.type != RLMPropertyTypeLinkingObjects) {
  222. m_link_chain.link(link.columnName.UTF8String);
  223. }
  224. else {
  225. auto [link_origin_table, link_origin_column] = link_origin(link);
  226. m_link_chain.backlink(link_origin_table, link_origin_column);
  227. }
  228. }
  229. m_col = m_link_chain.get_current_table()->get_column_key(m_property.columnName.UTF8String);
  230. }
  231. ColumnReference(Query& query, Group& group, RLMSchema *schema)
  232. : m_schema(schema), m_group(&group), m_query(&query)
  233. {
  234. }
  235. template <typename T, typename... SubQuery>
  236. auto resolve(SubQuery&&... subquery) const
  237. {
  238. static_assert(sizeof...(SubQuery) < 2, "resolve() takes at most one subquery");
  239. // LinkChain::column() mutates it, so we need to make a copy
  240. auto lc = m_link_chain;
  241. if (type() != RLMPropertyTypeLinkingObjects) {
  242. return lc.column<T>(column(), std::forward<SubQuery>(subquery)...);
  243. }
  244. if constexpr (std::is_same_v<T, Link>) {
  245. auto [table, col] = link_origin(m_property);
  246. return lc.column<T>(table, col, std::forward<SubQuery>(subquery)...);
  247. }
  248. REALM_TERMINATE("LinkingObjects property did not have column type Link");
  249. }
  250. RLMProperty *property() const { return m_property; }
  251. ColKey column() const { return m_col; }
  252. RLMPropertyType type() const { return property().type; }
  253. RLMObjectSchema *link_target_object_schema() const
  254. {
  255. REALM_ASSERT(is_link());
  256. return m_schema[property().objectClassName];
  257. }
  258. bool is_link() const noexcept {
  259. return propertyTypeIsLink(type());
  260. }
  261. bool has_any_to_many_links() const {
  262. return std::any_of(begin(m_links), end(m_links),
  263. [](RLMProperty *property) { return property.collection; });
  264. }
  265. ColumnReference last_link_column() const {
  266. REALM_ASSERT(!m_links.empty());
  267. return {*m_query, *m_group, m_schema, m_links.back(), {m_links.begin(), m_links.end() - 1}};
  268. }
  269. ColumnReference column_ignoring_links(Query& query) const {
  270. return {query, *m_group, m_schema, m_property};
  271. }
  272. ColumnReference append(RLMProperty *prop) const {
  273. auto links = m_links;
  274. if (m_property) {
  275. links.push_back(m_property);
  276. }
  277. return ColumnReference(*m_query, *m_group, m_schema, prop, std::move(links));
  278. }
  279. void validate_comparison(id value) const {
  280. RLMPrecondition(isObjectValidForProperty(value, m_property),
  281. @"Invalid value", @"Cannot compare value '%@' of type '%@' to property '%@' of type '%@'",
  282. value, [value class], m_property.name, m_property.objectClassName ?: RLMTypeToString(m_property.type));
  283. if (RLMObjectBase *obj = RLMDynamicCast<RLMObjectBase>(value)) {
  284. RLMPrecondition(!obj->_row.is_valid() || m_group == &obj->_realm.group,
  285. @"Invalid value origin", @"Object must be from the Realm being queried");
  286. }
  287. }
  288. private:
  289. std::pair<Table&, ColKey> link_origin(RLMProperty *prop) const
  290. {
  291. RLMObjectSchema *link_origin_schema = m_schema[prop.objectClassName];
  292. Table& link_origin_table = get_table(*m_group, link_origin_schema);
  293. NSString *column_name = link_origin_schema[prop.linkOriginPropertyName].columnName;
  294. auto link_origin_column = link_origin_table.get_column_key(column_name.UTF8String);
  295. return {link_origin_table, link_origin_column};
  296. }
  297. std::vector<RLMProperty*> m_links;
  298. RLMProperty *m_property;
  299. RLMSchema *m_schema;
  300. Group *m_group;
  301. Query *m_query;
  302. LinkChain m_link_chain;
  303. ColKey m_col;
  304. };
  305. class CollectionOperation {
  306. public:
  307. enum Type {
  308. None,
  309. Count,
  310. Minimum,
  311. Maximum,
  312. Sum,
  313. Average,
  314. // Dictionary specific.
  315. AllKeys
  316. };
  317. CollectionOperation(Type type, ColumnReference&& link_column, ColumnReference&& column)
  318. : m_type(type)
  319. , m_link_column(std::move(link_column))
  320. , m_column(std::move(column))
  321. {
  322. REALM_ASSERT(m_type != None);
  323. }
  324. Type type() const { return m_type; }
  325. const ColumnReference& link_column() const { return m_link_column; }
  326. const ColumnReference& column() const { return m_column; }
  327. void validate_comparison(id value) const {
  328. bool valid = true;
  329. switch (m_type) {
  330. case Count:
  331. RLMPrecondition([value isKindOfClass:[NSNumber class]], @"Invalid operand",
  332. @"%@ can only be compared with a numeric value.", name_for_type(m_type));
  333. return;
  334. case Average:
  335. case Minimum:
  336. case Maximum:
  337. // Null on min/max/average matches arrays with no non-null values, including on non-nullable types
  338. valid = isNSNull(value) || isObjectValidForProperty(value, m_column.property());
  339. break;
  340. case Sum:
  341. // Sums are never null
  342. valid = !isNSNull(value) && isObjectValidForProperty(value, m_column.property());
  343. break;
  344. case AllKeys:
  345. RLMPrecondition(isNSNull(value) || [value isKindOfClass:[NSString class]], @"Invalid operand",
  346. @"@allKeys can only be compared with a string value.");
  347. return;
  348. case None: break;
  349. }
  350. RLMPrecondition(valid, @"Invalid operand",
  351. @"%@ on a property of type %@ cannot be compared with '%@'",
  352. name_for_type(m_type), RLMTypeToString(m_column.type()), value);
  353. }
  354. void validate_comparison(const ColumnReference& column) const {
  355. switch (m_type) {
  356. case Count:
  357. RLMPrecondition(propertyTypeIsNumeric(column.type()), @"Invalid operand",
  358. @"%@ can only be compared with a numeric value.", name_for_type(m_type));
  359. break;
  360. case Average: case Minimum: case Maximum: case Sum:
  361. RLMPrecondition(propertyTypeIsNumeric(column.type()), @"Invalid operand",
  362. @"%@ on a property of type %@ cannot be compared with property of type '%@'",
  363. name_for_type(m_type), RLMTypeToString(m_column.type()), RLMTypeToString(column.type()));
  364. break;
  365. case AllKeys:
  366. RLMPrecondition(column.type() == RLMPropertyTypeString, @"Invalid operand",
  367. @"@allKeys can only be compared with a string value.");
  368. break;
  369. case None: break;
  370. }
  371. }
  372. static Type type_for_name(NSString *name) {
  373. if ([name isEqualToString:@"@count"]) {
  374. return Count;
  375. }
  376. if ([name isEqualToString:@"@min"]) {
  377. return Minimum;
  378. }
  379. if ([name isEqualToString:@"@max"]) {
  380. return Maximum;
  381. }
  382. if ([name isEqualToString:@"@sum"]) {
  383. return Sum;
  384. }
  385. if ([name isEqualToString:@"@avg"]) {
  386. return Average;
  387. }
  388. if ([name isEqualToString:@"@allKeys"]) {
  389. return AllKeys;
  390. }
  391. return None;
  392. }
  393. private:
  394. static NSString *name_for_type(Type type) {
  395. switch (type) {
  396. case Count: return @"@count";
  397. case Minimum: return @"@min";
  398. case Maximum: return @"@max";
  399. case Sum: return @"@sum";
  400. case Average: return @"@avg";
  401. case AllKeys: return @"@allKeys";
  402. case None: REALM_UNREACHABLE();
  403. }
  404. }
  405. Type m_type;
  406. ColumnReference m_link_column;
  407. ColumnReference m_column;
  408. };
  409. struct KeyPath;
  410. class QueryBuilder {
  411. public:
  412. QueryBuilder(Query& query, Group& group, RLMSchema *schema)
  413. : m_query(query), m_group(group), m_schema(schema) { }
  414. void apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema);
  415. void apply_collection_operator_expression(KeyPath&& kp, id value, NSComparisonPredicate *pred);
  416. void apply_value_expression(KeyPath&& kp, id value, NSComparisonPredicate *pred);
  417. void apply_column_expression(KeyPath&& left, KeyPath&& right, NSComparisonPredicate *predicate);
  418. void apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  419. NSPredicateOperatorType operatorType, NSExpression *right);
  420. void apply_map_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  421. NSComparisonPredicateOptions options, NSPredicateOperatorType operatorType,
  422. NSExpression *right);
  423. template <typename A, typename B>
  424. void add_numeric_constraint(RLMPropertyType datatype,
  425. NSPredicateOperatorType operatorType,
  426. A&& lhs, B&& rhs);
  427. template <typename A, typename B>
  428. void add_bool_constraint(RLMPropertyType, NSPredicateOperatorType operatorType, A&& lhs, B&& rhs);
  429. template <typename C, typename T>
  430. void add_mixed_constraint(NSPredicateOperatorType operatorType,
  431. NSComparisonPredicateOptions predicateOptions,
  432. Columns<C>&& column, T value);
  433. template <typename C>
  434. void add_mixed_constraint(NSPredicateOperatorType operatorType,
  435. NSComparisonPredicateOptions predicateOptions,
  436. Columns<C>&& column, const ColumnReference& c);
  437. template <typename C>
  438. void do_add_mixed_constraint(NSPredicateOperatorType operatorType,
  439. NSComparisonPredicateOptions predicateOptions,
  440. Columns<C>&& column, Mixed&& value);
  441. template<typename T>
  442. void add_substring_constraint(const T& value, Query condition);
  443. template<typename T>
  444. void add_substring_constraint(const Columns<T>& value, Query condition);
  445. template <typename C, typename T>
  446. void add_string_constraint(NSPredicateOperatorType operatorType,
  447. NSComparisonPredicateOptions predicateOptions,
  448. C&& column, T&& value);
  449. template <typename C, typename T>
  450. void add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
  451. NSComparisonPredicateOptions predicateOptions,
  452. C&& column, T&& value);
  453. template <typename C, typename T>
  454. void do_add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
  455. NSComparisonPredicateOptions predicateOptions,
  456. C&& column, T&& value);
  457. template <typename R>
  458. void add_constraint(NSPredicateOperatorType operatorType,
  459. NSComparisonPredicateOptions predicateOptions,
  460. ColumnReference const& column, R const& rhs);
  461. template <template<typename> typename W, typename T>
  462. void do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType,
  463. NSComparisonPredicateOptions predicateOptions, ColumnReference const& column, T&& value);
  464. void add_between_constraint(const ColumnReference& column, id value);
  465. void add_memberwise_equality_constraint(const ColumnReference& column, RLMObjectBase *obj);
  466. void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, RLMObjectBase *obj);
  467. void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, realm::null);
  468. void add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&);
  469. template <CollectionOperation::Type Operation, bool IsLinkCollection, bool IsDictionary, typename R>
  470. void add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  471. const CollectionOperation& collectionOperation, R&& rhs,
  472. NSComparisonPredicateOptions comparisionOptions);
  473. template <CollectionOperation::Type Operation, typename R>
  474. void add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  475. const CollectionOperation& collectionOperation, R&& rhs,
  476. NSComparisonPredicateOptions comparisionOptions);
  477. template <typename R>
  478. void add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  479. const CollectionOperation& collectionOperation, R&& rhs,
  480. NSComparisonPredicateOptions comparisionOptions);
  481. CollectionOperation collection_operation_from_key_path(KeyPath&& kp);
  482. ColumnReference column_reference_from_key_path(KeyPath&& kp, bool isAggregate);
  483. private:
  484. Query& m_query;
  485. Group& m_group;
  486. RLMSchema *m_schema;
  487. };
  488. #pragma mark Numeric Constraints
  489. // add a clause for numeric constraints based on operator type
  490. template <typename A, typename B>
  491. void QueryBuilder::add_numeric_constraint(RLMPropertyType datatype,
  492. NSPredicateOperatorType operatorType,
  493. A&& lhs, B&& rhs)
  494. {
  495. switch (operatorType) {
  496. case NSLessThanPredicateOperatorType:
  497. m_query.and_query(lhs < rhs);
  498. break;
  499. case NSLessThanOrEqualToPredicateOperatorType:
  500. m_query.and_query(lhs <= rhs);
  501. break;
  502. case NSGreaterThanPredicateOperatorType:
  503. m_query.and_query(lhs > rhs);
  504. break;
  505. case NSGreaterThanOrEqualToPredicateOperatorType:
  506. m_query.and_query(lhs >= rhs);
  507. break;
  508. case NSEqualToPredicateOperatorType:
  509. m_query.and_query(lhs == rhs);
  510. break;
  511. case NSNotEqualToPredicateOperatorType:
  512. m_query.and_query(lhs != rhs);
  513. break;
  514. default:
  515. unsupportedOperator(datatype, operatorType);
  516. }
  517. }
  518. template <typename A, typename B>
  519. void QueryBuilder::add_bool_constraint(RLMPropertyType datatype,
  520. NSPredicateOperatorType operatorType,
  521. A&& lhs, B&& rhs) {
  522. switch (operatorType) {
  523. case NSEqualToPredicateOperatorType:
  524. m_query.and_query(lhs == rhs);
  525. break;
  526. case NSNotEqualToPredicateOperatorType:
  527. m_query.and_query(lhs != rhs);
  528. break;
  529. default:
  530. unsupportedOperator(datatype, operatorType);
  531. }
  532. }
  533. #pragma mark String Constraints
  534. template<typename T>
  535. void QueryBuilder::add_substring_constraint(const T& value, Query condition) {
  536. // Foundation always returns false for substring operations with a RHS of null or "".
  537. m_query.and_query(value.size()
  538. ? std::move(condition)
  539. : std::unique_ptr<Expression>(new FalseExpression));
  540. }
  541. template<>
  542. void QueryBuilder::add_substring_constraint(const Mixed& value, Query condition) {
  543. // Foundation always returns false for substring operations with a RHS of null or "".
  544. m_query.and_query(value.get_string().size()
  545. ? std::move(condition)
  546. : std::unique_ptr<Expression>(new FalseExpression));
  547. }
  548. template<typename T>
  549. void QueryBuilder::add_substring_constraint(const Columns<T>& value, Query condition) {
  550. // Foundation always returns false for substring operations with a RHS of null or "".
  551. // We don't need to concern ourselves with the possibility of value traversing a link list
  552. // and producing multiple values per row as such expressions will have been rejected.
  553. m_query.and_query(const_cast<Columns<T>&>(value).size() != 0 && std::move(condition));
  554. }
  555. template<typename Comparator>
  556. Query make_diacritic_insensitive_constraint(bool caseSensitive, std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right) {
  557. using CompareCS = Compare<typename Comparator::CaseSensitive>;
  558. using CompareCI = Compare<typename Comparator::CaseInsensitive>;
  559. if (caseSensitive) {
  560. return make_expression<CompareCS>(std::move(left), std::move(right));
  561. }
  562. else {
  563. return make_expression<CompareCI>(std::move(left), std::move(right));
  564. }
  565. };
  566. Query make_diacritic_insensitive_constraint(NSPredicateOperatorType operatorType, bool caseSensitive,
  567. std::unique_ptr<Subexpr> left, std::unique_ptr<Subexpr> right) {
  568. switch (operatorType) {
  569. case NSBeginsWithPredicateOperatorType: {
  570. constexpr auto flags = kCFCompareDiacriticInsensitive | kCFCompareAnchored;
  571. return make_diacritic_insensitive_constraint<ContainsSubstring<flags>>(caseSensitive, std::move(left), std::move(right));
  572. }
  573. case NSEndsWithPredicateOperatorType: {
  574. constexpr auto flags = kCFCompareDiacriticInsensitive | kCFCompareAnchored | kCFCompareBackwards;
  575. return make_diacritic_insensitive_constraint<ContainsSubstring<flags>>(caseSensitive, std::move(left), std::move(right));
  576. }
  577. case NSContainsPredicateOperatorType: {
  578. constexpr auto flags = kCFCompareDiacriticInsensitive;
  579. return make_diacritic_insensitive_constraint<ContainsSubstring<flags>>(caseSensitive, std::move(left), std::move(right));
  580. }
  581. default:
  582. REALM_COMPILER_HINT_UNREACHABLE();
  583. }
  584. }
  585. template <typename C, typename T>
  586. void QueryBuilder::do_add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
  587. NSComparisonPredicateOptions predicateOptions,
  588. C&& column, T&& value) {
  589. bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
  590. switch (operatorType) {
  591. case NSBeginsWithPredicateOperatorType:
  592. add_substring_constraint(value, column.begins_with(value, caseSensitive));
  593. break;
  594. case NSEndsWithPredicateOperatorType:
  595. add_substring_constraint(value, column.ends_with(value, caseSensitive));
  596. break;
  597. case NSContainsPredicateOperatorType:
  598. add_substring_constraint(value, column.contains(value, caseSensitive));
  599. break;
  600. case NSEqualToPredicateOperatorType:
  601. m_query.and_query(column.equal(value, caseSensitive));
  602. break;
  603. case NSNotEqualToPredicateOperatorType:
  604. m_query.and_query(column.not_equal(value, caseSensitive));
  605. break;
  606. case NSLikePredicateOperatorType:
  607. m_query.and_query(column.like(value, caseSensitive));
  608. break;
  609. default: {
  610. if constexpr (is_any_v<C, Columns<String>, Columns<Lst<String>>, Columns<Set<String>>>) {
  611. unsupportedOperator(RLMPropertyTypeString, operatorType);
  612. }
  613. else if constexpr (is_any_v<C, Columns<Binary>, Columns<Lst<Binary>>, Columns<Set<Binary>>>) {
  614. unsupportedOperator(RLMPropertyTypeData, operatorType);
  615. }
  616. else if constexpr (is_any_v<C, Columns<Mixed>, Columns<Lst<Mixed>>, Columns<Set<Mixed>>>) {
  617. unsupportedOperator(RLMPropertyTypeAny, operatorType);
  618. }
  619. else if constexpr (is_any_v<C, Columns<Dictionary>>) {
  620. // The underlying storage type Dictionary is always Mixed. This creates an issue
  621. // where we cannot be descriptive about the exception as we do not know
  622. // the actual value type.
  623. throwException(@"Invalid operand type",
  624. @"Operator '%@' not supported for string queries on Dictionary.",
  625. operatorName(operatorType));
  626. }
  627. }
  628. }
  629. }
  630. template <typename C, typename T>
  631. void QueryBuilder::add_diacritic_sensitive_string_constraint(NSPredicateOperatorType operatorType,
  632. NSComparisonPredicateOptions predicateOptions,
  633. C&& column, T&& value) {
  634. if constexpr (is_any_v<C, Columns<Dictionary>>) {
  635. // This nesting isnt pretty but without it the compiler will complain about `T` having no known
  636. // conversion from Columns<StringData> to Mixed. This is due to the fact that all values on a
  637. // dictionary column are boxed in Mixed.
  638. if constexpr (is_any_v<T, Mixed, BinaryData, StringData>) {
  639. do_add_diacritic_sensitive_string_constraint(operatorType, predicateOptions, std::forward<C>(column), value);
  640. }
  641. }
  642. else {
  643. do_add_diacritic_sensitive_string_constraint(operatorType, predicateOptions, std::forward<C>(column), value);
  644. }
  645. }
  646. template <typename C, typename T>
  647. void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType,
  648. NSComparisonPredicateOptions predicateOptions,
  649. C&& column, T&& value) {
  650. if (!(predicateOptions & NSDiacriticInsensitivePredicateOption)) {
  651. add_diacritic_sensitive_string_constraint(operatorType, predicateOptions, std::forward<C>(column), std::move(value));
  652. return;
  653. }
  654. auto as_subexpr = util::overload{
  655. [](StringData value) { return make_subexpr<ConstantStringValue>(value); },
  656. [](BinaryData value) { return make_subexpr<ConstantStringValue>(StringData(value.data(), value.size())); },
  657. [](Mixed value) {
  658. // When Mixed is null calling `get_type` will throw an exception.
  659. if (value.is_null())
  660. return make_subexpr<ConstantStringValue>(value.get_string());
  661. switch (value.get_type()) {
  662. case DataType::Type::String:
  663. return make_subexpr<ConstantStringValue>(value.get_string());
  664. case DataType::Type::Binary:
  665. return make_subexpr<ConstantStringValue>(StringData(value.get_binary().data(), value.get_binary().size()));
  666. default:
  667. REALM_UNREACHABLE();
  668. }
  669. },
  670. [](auto& c) { return c.clone(); }
  671. };
  672. auto left = as_subexpr(column);
  673. auto right = as_subexpr(value);
  674. bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
  675. constexpr auto flags = kCFCompareDiacriticInsensitive | kCFCompareAnchored;
  676. switch (operatorType) {
  677. case NSBeginsWithPredicateOperatorType:
  678. case NSEndsWithPredicateOperatorType:
  679. case NSContainsPredicateOperatorType:
  680. add_substring_constraint(value, make_diacritic_insensitive_constraint(operatorType, caseSensitive, std::move(left), std::move(right)));
  681. break;
  682. case NSNotEqualToPredicateOperatorType:
  683. m_query.and_query(make_diacritic_insensitive_constraint<NotEqual<flags>>(caseSensitive, std::move(left), std::move(right)));
  684. break;
  685. case NSEqualToPredicateOperatorType:
  686. m_query.and_query(make_diacritic_insensitive_constraint<Equal<flags>>(caseSensitive, std::move(left), std::move(right)));
  687. break;
  688. case NSLikePredicateOperatorType:
  689. throwException(@"Invalid operator type",
  690. @"Operator 'LIKE' not supported with diacritic-insensitive modifier.");
  691. default:
  692. unsupportedOperator(RLMPropertyTypeString, operatorType);
  693. }
  694. }
  695. id value_from_constant_expression_or_value(id value) {
  696. if (NSExpression *exp = RLMDynamicCast<NSExpression>(value)) {
  697. RLMPrecondition(exp.expressionType == NSConstantValueExpressionType,
  698. @"Invalid value",
  699. @"Expressions within predicate aggregates must be constant values");
  700. return exp.constantValue;
  701. }
  702. return value;
  703. }
  704. void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, id *to) {
  705. NSArray *array = RLMDynamicCast<NSArray>(value);
  706. RLMPrecondition(array, @"Invalid value", @"object must be of type NSArray for BETWEEN operations");
  707. RLMPrecondition(array.count == 2, @"Invalid value", @"NSArray object must contain exactly two objects for BETWEEN operations");
  708. *from = value_from_constant_expression_or_value(array.firstObject);
  709. *to = value_from_constant_expression_or_value(array.lastObject);
  710. RLMPrecondition(isObjectValidForProperty(*from, prop) && isObjectValidForProperty(*to, prop),
  711. @"Invalid value",
  712. @"NSArray objects must be of type %@ for BETWEEN operations", RLMTypeToString(prop.type));
  713. }
  714. #pragma mark Between Constraint
  715. void QueryBuilder::add_between_constraint(const ColumnReference& column, id value) {
  716. if (column.has_any_to_many_links()) {
  717. auto link_column = column.last_link_column();
  718. Query subquery = get_table(m_group, link_column.link_target_object_schema()).where();
  719. QueryBuilder(subquery, m_group, m_schema).add_between_constraint(column.column_ignoring_links(subquery), value);
  720. m_query.and_query(link_column.resolve<Link>(std::move(subquery)).count() > 0);
  721. return;
  722. }
  723. id from, to;
  724. validate_and_extract_between_range(value, column.property(), &from, &to);
  725. if (!propertyTypeIsNumeric(column.type())) {
  726. return unsupportedOperator(column.type(), NSBetweenPredicateOperatorType);
  727. }
  728. m_query.group();
  729. add_constraint(NSGreaterThanOrEqualToPredicateOperatorType, 0, column, from);
  730. add_constraint(NSLessThanOrEqualToPredicateOperatorType, 0, column, to);
  731. m_query.end_group();
  732. }
  733. #pragma mark Link Constraints
  734. void QueryBuilder::add_memberwise_equality_constraint(const ColumnReference& column, RLMObjectBase *obj) {
  735. for (RLMProperty *property in obj->_objectSchema.properties) {
  736. // Both of these probably are implementable, but are significantly more complicated.
  737. RLMPrecondition(!property.collection, @"Invalid predicate",
  738. @"Unsupported property '%@.%@' for memberwise equality query: equality on collections is not implemented.",
  739. obj->_objectSchema.className, property.name);
  740. RLMPrecondition(!propertyTypeIsLink(property.type), @"Invalid predicate",
  741. @"Unsupported property '%@.%@' for memberwise equality query: object links are not implemented.",
  742. obj->_objectSchema.className, property.name);
  743. add_constraint(NSEqualToPredicateOperatorType, 0, column.append(property), RLMDynamicGet(obj, property));
  744. }
  745. }
  746. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
  747. const ColumnReference& column, RLMObjectBase *obj) {
  748. // If the value isn't actually a RLMObject then it's something which bridges
  749. // to RLMObject, i.e. a custom type mapping to an embedded object. For those
  750. // we want to perform memberwise equality rather than equality on the link itself.
  751. if (![obj isKindOfClass:[RLMObjectBase class]]) {
  752. obj = RLMBridgeSwiftValue(obj);
  753. REALM_ASSERT([obj isKindOfClass:[RLMObjectBase class]]);
  754. // Collections need to use subqueries, but unary links can just use a
  755. // group. Unary links could also use a subquery but that has worse performance.
  756. if (column.property().collection) {
  757. Query subquery = get_table(m_group, column.link_target_object_schema()).where();
  758. QueryBuilder(subquery, m_group, m_schema)
  759. .add_memberwise_equality_constraint(ColumnReference(subquery, m_group, m_schema), obj);
  760. if (operatorType == NSEqualToPredicateOperatorType) {
  761. m_query.and_query(column.resolve<Link>(std::move(subquery)).count() > 0);
  762. }
  763. else {
  764. // This strange condition is because "ANY list != x" isn't
  765. // "NONE list == x"; there must be an object in the list for
  766. // this to match
  767. m_query.and_query(column.resolve<Link>().count() > 0 &&
  768. column.resolve<Link>(std::move(subquery)).count() == 0);
  769. }
  770. }
  771. else {
  772. if (operatorType == NSNotEqualToPredicateOperatorType) {
  773. m_query.Not();
  774. }
  775. m_query.group();
  776. add_memberwise_equality_constraint(column, obj);
  777. m_query.end_group();
  778. }
  779. return;
  780. }
  781. if (!obj->_row.is_valid()) {
  782. // Unmanaged or deleted objects are not equal to any managed objects.
  783. // For arrays this effectively checks if there are any objects in the
  784. // array, while for links it's just always constant true or false
  785. // (for != and = respectively).
  786. if (column.has_any_to_many_links() || column.property().collection) {
  787. add_link_constraint(operatorType, column, null());
  788. }
  789. else if (operatorType == NSEqualToPredicateOperatorType) {
  790. m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  791. }
  792. else {
  793. m_query.and_query(std::unique_ptr<Expression>(new TrueExpression));
  794. }
  795. }
  796. else {
  797. if (column.property().dictionary) {
  798. add_bool_constraint(RLMPropertyTypeObject, operatorType, column.resolve<Dictionary>(), obj->_row);
  799. }
  800. else {
  801. add_bool_constraint(RLMPropertyTypeObject, operatorType, column.resolve<Link>(), obj->_row);
  802. }
  803. }
  804. }
  805. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
  806. const ColumnReference& column, realm::null) {
  807. if (column.property().dictionary) {
  808. add_bool_constraint(RLMPropertyTypeObject, operatorType, column.resolve<Dictionary>(), null());
  809. }
  810. else {
  811. add_bool_constraint(RLMPropertyTypeObject, operatorType, column.resolve<Link>(), null());
  812. }
  813. }
  814. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
  815. const ColumnReference& a, const ColumnReference& b) {
  816. if (a.property().dictionary) {
  817. add_bool_constraint(RLMPropertyTypeObject, operatorType, a.resolve<Dictionary>(), b.resolve<Dictionary>());
  818. }
  819. else {
  820. add_bool_constraint(RLMPropertyTypeObject, operatorType, a.resolve<Link>(), b.resolve<Link>());
  821. }
  822. }
  823. // iterate over an array of subpredicates, using @func to build a query from each
  824. // one and ORing them together
  825. template<typename Func>
  826. void process_or_group(Query &query, id array, Func&& func) {
  827. array = RLMAsFastEnumeration(array);
  828. RLMPrecondition(array, @"Invalid value", @"IN clause requires an array of items");
  829. query.group();
  830. bool first = true;
  831. for (id item in array) {
  832. if (!first) {
  833. query.Or();
  834. }
  835. first = false;
  836. func(item);
  837. }
  838. if (first) {
  839. // Queries can't be empty, so if there's zero things in the OR group
  840. // validation will fail. Work around this by adding an expression which
  841. // will never find any rows in a table.
  842. query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  843. }
  844. query.end_group();
  845. }
  846. #pragma mark Conversion Helpers
  847. template <typename>
  848. realm::null value_of_type(realm::null) {
  849. return realm::null();
  850. }
  851. template <typename RequestedType>
  852. auto value_of_type(__unsafe_unretained const id value) {
  853. return RLMStatelessAccessorContext::unbox<RequestedType>(value);
  854. }
  855. template <>
  856. auto value_of_type<Mixed>(id value) {
  857. return RLMObjcToMixed(value, nil, CreatePolicy::Skip);
  858. }
  859. template <typename RequestedType>
  860. auto value_of_type(const ColumnReference& column) {
  861. return column.resolve<RequestedType>();
  862. }
  863. template <typename T, typename Fn>
  864. void convert_null(T&& value, Fn&& fn) {
  865. if (isNSNull(value)) {
  866. fn(null());
  867. }
  868. else {
  869. fn(value);
  870. }
  871. }
  872. template <template<typename> typename W, typename T>
  873. void QueryBuilder::do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType,
  874. NSComparisonPredicateOptions predicateOptions, ColumnReference const& column, T&& value)
  875. {
  876. switch (type) {
  877. case RLMPropertyTypeBool:
  878. convert_null(value, [&](auto&& value) {
  879. add_bool_constraint(type, operatorType, column.resolve<W<bool>>(),
  880. value_of_type<bool>(value));
  881. });
  882. break;
  883. case RLMPropertyTypeObjectId:
  884. convert_null(value, [&](auto&& value) {
  885. add_bool_constraint(type, operatorType, column.resolve<W<ObjectId>>(),
  886. value_of_type<ObjectId>(value));
  887. });
  888. break;
  889. case RLMPropertyTypeDate:
  890. convert_null(value, [&](auto&& value) {
  891. add_numeric_constraint(type, operatorType, column.resolve<W<Timestamp>>(),
  892. value_of_type<Timestamp>(value));
  893. });
  894. break;
  895. case RLMPropertyTypeDouble:
  896. convert_null(value, [&](auto&& value) {
  897. add_numeric_constraint(type, operatorType, column.resolve<W<Double>>(),
  898. value_of_type<Double>(value));
  899. });
  900. break;
  901. case RLMPropertyTypeFloat:
  902. convert_null(value, [&](auto&& value) {
  903. add_numeric_constraint(type, operatorType, column.resolve<W<Float>>(),
  904. value_of_type<Float>(value));
  905. });
  906. break;
  907. case RLMPropertyTypeInt:
  908. convert_null(value, [&](auto&& value) {
  909. add_numeric_constraint(type, operatorType, column.resolve<W<Int>>(),
  910. value_of_type<Int>(value));
  911. });
  912. break;
  913. case RLMPropertyTypeDecimal128:
  914. convert_null(value, [&](auto&& value) {
  915. add_numeric_constraint(type, operatorType, column.resolve<W<Decimal128>>(),
  916. value_of_type<Decimal128>(value));
  917. });
  918. break;
  919. case RLMPropertyTypeString:
  920. add_string_constraint(operatorType, predicateOptions, column.resolve<W<String>>(),
  921. value_of_type<String>(value));
  922. break;
  923. case RLMPropertyTypeData:
  924. add_string_constraint(operatorType, predicateOptions,
  925. column.resolve<W<Binary>>(),
  926. value_of_type<Binary>(value));
  927. break;
  928. case RLMPropertyTypeObject:
  929. case RLMPropertyTypeLinkingObjects:
  930. convert_null(value, [&](auto&& value) {
  931. add_link_constraint(operatorType, column, value);
  932. });
  933. break;
  934. case RLMPropertyTypeUUID:
  935. convert_null(value, [&](auto&& value) {
  936. add_bool_constraint(type, operatorType, column.resolve<W<UUID>>(),
  937. value_of_type<UUID>(value));
  938. });
  939. break;
  940. case RLMPropertyTypeAny:
  941. convert_null(value, [&](auto&& value) {
  942. add_mixed_constraint(operatorType,
  943. predicateOptions,
  944. column.resolve<W<Mixed>>(),
  945. value);
  946. });
  947. }
  948. }
  949. #pragma mark Mixed Constraints
  950. template<typename C, typename T>
  951. void QueryBuilder::add_mixed_constraint(NSPredicateOperatorType operatorType,
  952. NSComparisonPredicateOptions predicateOptions,
  953. Columns<C>&& column,
  954. T value)
  955. {
  956. // Handle cases where a string might be '1' or '0'. Without this the string
  957. // will be boxed as a bool and thus string query operations will crash in core.
  958. if constexpr(std::is_same_v<T, id>) {
  959. if (auto str = RLMDynamicCast<NSString>(value)) {
  960. add_string_constraint(operatorType, predicateOptions,
  961. std::move(column), realm::Mixed([str UTF8String]));
  962. return;
  963. }
  964. }
  965. do_add_mixed_constraint(operatorType, predicateOptions,
  966. std::move(column), value_of_type<Mixed>(value));
  967. }
  968. template<typename C>
  969. void QueryBuilder::do_add_mixed_constraint(NSPredicateOperatorType operatorType,
  970. NSComparisonPredicateOptions predicateOptions,
  971. Columns<C>&& column,
  972. Mixed&& value)
  973. {
  974. switch (operatorType) {
  975. case NSLessThanPredicateOperatorType:
  976. m_query.and_query(column < value);
  977. break;
  978. case NSLessThanOrEqualToPredicateOperatorType:
  979. m_query.and_query(column <= value);
  980. break;
  981. case NSGreaterThanPredicateOperatorType:
  982. m_query.and_query(column > value);
  983. break;
  984. case NSGreaterThanOrEqualToPredicateOperatorType:
  985. m_query.and_query(column >= value);
  986. break;
  987. case NSEqualToPredicateOperatorType:
  988. m_query.and_query(column == value);
  989. break;
  990. case NSNotEqualToPredicateOperatorType:
  991. m_query.and_query(column != value);
  992. break;
  993. // String comparison operators: There isn't a way for a string value
  994. // to get down here, but a rhs of NULL can
  995. case NSLikePredicateOperatorType:
  996. case NSMatchesPredicateOperatorType:
  997. case NSBeginsWithPredicateOperatorType:
  998. case NSEndsWithPredicateOperatorType:
  999. case NSContainsPredicateOperatorType:
  1000. add_string_constraint(operatorType, predicateOptions,
  1001. std::move(column), value);
  1002. break;
  1003. default:
  1004. break;
  1005. }
  1006. }
  1007. template<typename C>
  1008. void QueryBuilder::add_mixed_constraint(NSPredicateOperatorType operatorType,
  1009. NSComparisonPredicateOptions,
  1010. Columns<C>&& column,
  1011. const ColumnReference& value)
  1012. {
  1013. add_bool_constraint(RLMPropertyTypeObject, operatorType, column, value.resolve<Mixed>());
  1014. }
  1015. template<typename T>
  1016. using Identity = T;
  1017. template<typename>
  1018. using AlwaysDictionary = Dictionary;
  1019. template <typename R>
  1020. void QueryBuilder::add_constraint(NSPredicateOperatorType operatorType,
  1021. NSComparisonPredicateOptions predicateOptions, ColumnReference const& column, R const& rhs)
  1022. {
  1023. auto type = column.type();
  1024. if (column.property().array) {
  1025. do_add_constraint<Lst>(type, operatorType, predicateOptions, column, rhs);
  1026. }
  1027. else if (column.property().set) {
  1028. do_add_constraint<Set>(type, operatorType, predicateOptions, column, rhs);
  1029. }
  1030. else if (column.property().dictionary) {
  1031. do_add_constraint<AlwaysDictionary>(type, operatorType, predicateOptions, column, rhs);
  1032. }
  1033. else {
  1034. do_add_constraint<Identity>(type, operatorType, predicateOptions, column, rhs);
  1035. }
  1036. }
  1037. struct KeyPath {
  1038. std::vector<RLMProperty *> links;
  1039. RLMProperty *property;
  1040. CollectionOperation::Type collectionOperation;
  1041. bool containsToManyRelationship;
  1042. };
  1043. KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, NSString *keyPath)
  1044. {
  1045. RLMProperty *property;
  1046. std::vector<RLMProperty *> links;
  1047. CollectionOperation::Type collectionOperation = CollectionOperation::None;
  1048. NSString *collectionOperationName;
  1049. bool keyPathContainsToManyRelationship = false;
  1050. NSUInteger start = 0, length = keyPath.length, end = length;
  1051. for (; end != NSNotFound; start = end + 1) {
  1052. end = [keyPath rangeOfString:@"." options:0 range:{start, length - start}].location;
  1053. RLMPrecondition(end == NSNotFound || end + 1 < length, @"Invalid predicate",
  1054. @"Invalid keypath '%@': no key name after last '.'", keyPath);
  1055. RLMPrecondition(end > start, @"Invalid predicate",
  1056. @"Invalid keypath '%@': no key name before '.'", keyPath);
  1057. NSString *propertyName = [keyPath substringWithRange:{start, end == NSNotFound ? length - start : end - start}];
  1058. if ([propertyName characterAtIndex:0] == '@') {
  1059. if ([propertyName isEqualToString:@"@allValues"]) {
  1060. RLMPrecondition(property.dictionary, @"Invalid predicate",
  1061. @"Invalid keypath '%@': @allValues must follow a dictionary property.", keyPath);
  1062. continue;
  1063. }
  1064. RLMPrecondition(collectionOperation == CollectionOperation::None, @"Invalid predicate",
  1065. @"Invalid keypath '%@': at most one collection operation per keypath is supported.", keyPath);
  1066. collectionOperation = CollectionOperation::type_for_name(propertyName);
  1067. RLMPrecondition(collectionOperation != CollectionOperation::None, @"Invalid predicate",
  1068. @"Invalid keypath '%@': Unsupported collection operation '%@'", keyPath, propertyName);
  1069. RLMPrecondition(property.collection, @"Invalid predicate",
  1070. @"Invalid keypath '%@': collection operation '%@' must be applied to a collection", keyPath, propertyName);
  1071. switch (collectionOperation) {
  1072. case CollectionOperation::None:
  1073. REALM_UNREACHABLE();
  1074. case CollectionOperation::Count:
  1075. RLMPrecondition(end == NSNotFound, @"Invalid predicate",
  1076. @"Invalid keypath '%@': @count must appear at the end of a keypath.", keyPath);
  1077. break;
  1078. case CollectionOperation::AllKeys:
  1079. RLMPrecondition(end == NSNotFound, @"Invalid predicate",
  1080. @"Invalid keypath '%@': @allKeys must appear at the end of a keypath.", keyPath);
  1081. RLMPrecondition(property.dictionary, @"Invalid predicate",
  1082. @"Invalid keypath '%@': @allKeys must follow a dictionary property.", keyPath);
  1083. break;
  1084. default:
  1085. if (propertyTypeIsLink(property.type)) {
  1086. RLMPrecondition(end != NSNotFound, @"Invalid predicate",
  1087. @"Invalid keypath '%@': %@ on a collection of objects cannot appear at the end of a keypath.", keyPath, propertyName);
  1088. }
  1089. else {
  1090. RLMPrecondition(end == NSNotFound, @"Invalid predicate",
  1091. @"Invalid keypath '%@': %@ on a collection of values must appear at the end of a keypath.", keyPath, propertyName);
  1092. RLMPrecondition(propertyTypeIsNumeric(property.type), @"Invalid predicate",
  1093. @"Invalid keypath '%@': %@ can only be applied to a collection of numeric values.", keyPath, propertyName);
  1094. }
  1095. }
  1096. collectionOperationName = propertyName;
  1097. continue;
  1098. }
  1099. RLMPrecondition(objectSchema, @"Invalid predicate",
  1100. @"Invalid keypath '%@': %@ property %@ can only be followed by a collection operation.",
  1101. keyPath, property.typeName, property.name);
  1102. property = objectSchema[propertyName];
  1103. RLMPrecondition(property, @"Invalid predicate",
  1104. @"Invalid keypath '%@': Property '%@' not found in object of type '%@'",
  1105. keyPath, propertyName, objectSchema.className);
  1106. RLMPrecondition(collectionOperation == CollectionOperation::None || (propertyTypeIsNumeric(property.type) && !property.collection),
  1107. @"Invalid predicate",
  1108. @"Invalid keypath '%@': %@ must be followed by a numeric property.", keyPath, collectionOperationName);
  1109. if (property.collection)
  1110. keyPathContainsToManyRelationship = true;
  1111. links.push_back(property);
  1112. if (end != NSNotFound) {
  1113. RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeLinkingObjects || property.collection,
  1114. @"Invalid predicate", @"Invalid keypath '%@': Property '%@.%@' is not a link or collection and can only appear at the end of a keypath.",
  1115. keyPath, objectSchema.className, propertyName);
  1116. objectSchema = property.objectClassName ? schema[property.objectClassName] : nil;
  1117. }
  1118. };
  1119. links.pop_back();
  1120. return {std::move(links), property, collectionOperation, keyPathContainsToManyRelationship};
  1121. }
  1122. ColumnReference QueryBuilder::column_reference_from_key_path(KeyPath&& kp, bool isAggregate)
  1123. {
  1124. if (isAggregate && !kp.containsToManyRelationship) {
  1125. throwException(@"Invalid predicate",
  1126. @"Aggregate operations can only be used on key paths that include an collection property");
  1127. } else if (!isAggregate && kp.containsToManyRelationship) {
  1128. throwException(@"Invalid predicate",
  1129. @"Key paths that include a collection property must use aggregate operations");
  1130. }
  1131. return ColumnReference(m_query, m_group, m_schema, kp.property, std::move(kp.links));
  1132. }
  1133. #pragma mark Collection Operations
  1134. // static_assert is always evaluated even if it's inside a if constexpr
  1135. // unless the value is derived from the template argument, in which case it's
  1136. // only evaluated if that branch is active
  1137. template <CollectionOperation::Type> struct AlwaysFalse : std::false_type {};
  1138. template <CollectionOperation::Type OperationType, typename Column>
  1139. auto collection_operation_expr_2(Column&& column) {
  1140. if constexpr (OperationType == CollectionOperation::Minimum) {
  1141. return column.min();
  1142. }
  1143. else if constexpr (OperationType == CollectionOperation::Maximum) {
  1144. return column.max();
  1145. }
  1146. else if constexpr (OperationType == CollectionOperation::Sum) {
  1147. return column.sum();
  1148. }
  1149. else if constexpr (OperationType == CollectionOperation::Average) {
  1150. return column.average();
  1151. }
  1152. else {
  1153. static_assert(AlwaysFalse<OperationType>::value, "invalid operation type");
  1154. }
  1155. }
  1156. template <typename Requested, CollectionOperation::Type OperationType, bool IsLinkCollection, bool IsDictionary>
  1157. auto collection_operation_expr(CollectionOperation operation) {
  1158. REALM_ASSERT(operation.type() == OperationType);
  1159. if constexpr (IsLinkCollection) {
  1160. auto&& resolved = operation.link_column().resolve<Link>();
  1161. auto col = operation.column().column();
  1162. return collection_operation_expr_2<OperationType>(resolved.template column<Requested>(col));
  1163. }
  1164. else if constexpr (IsDictionary) {
  1165. return collection_operation_expr_2<OperationType>(operation.link_column().resolve<Dictionary>());
  1166. }
  1167. else {
  1168. return collection_operation_expr_2<OperationType>(operation.link_column().resolve<Lst<Requested>>());
  1169. }
  1170. }
  1171. template <CollectionOperation::Type Operation, bool IsLinkCollection, bool IsDictionary, typename R>
  1172. void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  1173. CollectionOperation const& collectionOperation, R&& rhs,
  1174. NSComparisonPredicateOptions)
  1175. {
  1176. auto type = IsLinkCollection ? collectionOperation.column().type() : collectionOperation.link_column().type();
  1177. switch (type) {
  1178. case RLMPropertyTypeInt:
  1179. add_numeric_constraint(type, operatorType,
  1180. collection_operation_expr<Int, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1181. value_of_type<Int>(rhs));
  1182. break;
  1183. case RLMPropertyTypeFloat:
  1184. add_numeric_constraint(type, operatorType,
  1185. collection_operation_expr<Float, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1186. value_of_type<Float>(rhs));
  1187. break;
  1188. case RLMPropertyTypeDouble:
  1189. add_numeric_constraint(type, operatorType,
  1190. collection_operation_expr<Double, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1191. value_of_type<Double>(rhs));
  1192. break;
  1193. case RLMPropertyTypeDecimal128:
  1194. add_numeric_constraint(type, operatorType,
  1195. collection_operation_expr<Decimal128, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1196. value_of_type<Decimal128>(rhs));
  1197. break;
  1198. case RLMPropertyTypeDate:
  1199. if constexpr (Operation == CollectionOperation::Sum || Operation == CollectionOperation::Average) {
  1200. throwException(@"Unsupported predicate value type",
  1201. @"Cannot sum or average date properties");
  1202. }
  1203. else {
  1204. add_numeric_constraint(type, operatorType,
  1205. collection_operation_expr<Timestamp, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1206. value_of_type<Timestamp>(rhs));
  1207. }
  1208. break;
  1209. case RLMPropertyTypeAny:
  1210. add_numeric_constraint(type, operatorType,
  1211. collection_operation_expr<Mixed, Operation, IsLinkCollection, IsDictionary>(collectionOperation),
  1212. value_of_type<Mixed>(rhs));
  1213. break;
  1214. default:
  1215. REALM_ASSERT(false && "Only numeric property types should hit this path.");
  1216. }
  1217. }
  1218. template <CollectionOperation::Type Operation, typename R>
  1219. void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  1220. CollectionOperation const& collectionOperation, R&& rhs,
  1221. NSComparisonPredicateOptions options)
  1222. {
  1223. convert_null(rhs, [&](auto&& rhs) {
  1224. if (collectionOperation.link_column().is_link()) {
  1225. add_collection_operation_constraint<Operation, true, false>(operatorType, collectionOperation, std::move(rhs), options);
  1226. }
  1227. else if (collectionOperation.column().property().dictionary) {
  1228. add_collection_operation_constraint<Operation, false, true>(operatorType, collectionOperation, std::move(rhs), options);
  1229. }
  1230. else {
  1231. add_collection_operation_constraint<Operation, false, false>(operatorType, collectionOperation, std::move(rhs), options);
  1232. }
  1233. });
  1234. }
  1235. template <typename T, typename Fn>
  1236. void get_collection_type(__unsafe_unretained RLMProperty *prop, Fn&& fn) {
  1237. if (prop.array) {
  1238. fn((Lst<T>*)0);
  1239. }
  1240. else if (prop.set) {
  1241. fn((Set<T>*)0);
  1242. }
  1243. else {
  1244. fn((Dictionary*)0);
  1245. }
  1246. }
  1247. template <typename R>
  1248. void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  1249. CollectionOperation const& collectionOperation, R&& rhs,
  1250. NSComparisonPredicateOptions comparisonOptions)
  1251. {
  1252. switch (collectionOperation.type()) {
  1253. case CollectionOperation::None:
  1254. break;
  1255. case CollectionOperation::Count: {
  1256. auto& column = collectionOperation.link_column();
  1257. RLMPropertyType type = column.type();
  1258. auto rhsValue = value_of_type<Int>(rhs);
  1259. auto continuation = [&](auto t) {
  1260. add_numeric_constraint(type, operatorType, column.resolve<std::decay_t<decltype(*t)>>().size(), rhsValue);
  1261. };
  1262. switch (type) {
  1263. case RLMPropertyTypeBool:
  1264. return get_collection_type<Bool>(column.property(), std::move(continuation));
  1265. case RLMPropertyTypeObjectId:
  1266. return get_collection_type<ObjectId>(column.property(), std::move(continuation));
  1267. case RLMPropertyTypeDate:
  1268. return get_collection_type<Timestamp>(column.property(), std::move(continuation));
  1269. case RLMPropertyTypeDouble:
  1270. return get_collection_type<Double>(column.property(), std::move(continuation));
  1271. case RLMPropertyTypeFloat:
  1272. return get_collection_type<Float>(column.property(), std::move(continuation));
  1273. case RLMPropertyTypeInt:
  1274. return get_collection_type<Int>(column.property(), std::move(continuation));
  1275. case RLMPropertyTypeDecimal128:
  1276. return get_collection_type<Decimal128>(column.property(), std::move(continuation));
  1277. case RLMPropertyTypeString:
  1278. return get_collection_type<String>(column.property(), std::move(continuation));
  1279. case RLMPropertyTypeData:
  1280. return get_collection_type<Binary>(column.property(), std::move(continuation));
  1281. case RLMPropertyTypeUUID:
  1282. return get_collection_type<UUID>(column.property(), std::move(continuation));
  1283. case RLMPropertyTypeAny:
  1284. return get_collection_type<Mixed>(column.property(), std::move(continuation));
  1285. case RLMPropertyTypeObject:
  1286. case RLMPropertyTypeLinkingObjects:
  1287. return add_numeric_constraint(type, operatorType, column.resolve<Link>().count(), rhsValue);
  1288. }
  1289. }
  1290. case CollectionOperation::Minimum:
  1291. add_collection_operation_constraint<CollectionOperation::Minimum>(operatorType, collectionOperation, std::move(rhs), comparisonOptions);
  1292. break;
  1293. case CollectionOperation::Maximum:
  1294. add_collection_operation_constraint<CollectionOperation::Maximum>(operatorType, collectionOperation, std::move(rhs), comparisonOptions);
  1295. break;
  1296. case CollectionOperation::Sum:
  1297. add_collection_operation_constraint<CollectionOperation::Sum>(operatorType, collectionOperation, std::move(rhs), comparisonOptions);
  1298. break;
  1299. case CollectionOperation::Average:
  1300. add_collection_operation_constraint<CollectionOperation::Average>(operatorType, collectionOperation, std::move(rhs), comparisonOptions);
  1301. break;
  1302. case CollectionOperation::AllKeys: {
  1303. // BETWEEN and IN are not supported by @allKeys as the parsing for collection
  1304. // operators happens before and disection of a rhs array of values.
  1305. add_string_constraint(operatorType, comparisonOptions,
  1306. Columns<Dictionary>(collectionOperation.link_column().column(), m_query.get_table()).keys(),
  1307. value_of_type<StringData>(rhs));
  1308. break;
  1309. }
  1310. }
  1311. }
  1312. bool key_path_contains_collection_operator(const KeyPath& kp) {
  1313. return kp.collectionOperation != CollectionOperation::None;
  1314. }
  1315. CollectionOperation QueryBuilder::collection_operation_from_key_path(KeyPath&& kp) {
  1316. // Collection operations can either come at the end, or immediately before
  1317. // the last property. Count and AllKeys are always the end, while
  1318. // min/max/sum/avg are at the end for collections of primitives and one
  1319. // before the end for collections of objects (with the aggregate done on a
  1320. // property of those objects). For one-before-the-end we need to construct
  1321. // a KeyPath to both the final link and the final property.
  1322. KeyPath linkPrefix = kp;
  1323. if (kp.collectionOperation != CollectionOperation::Count && kp.collectionOperation != CollectionOperation::AllKeys && !kp.property.collection) {
  1324. REALM_ASSERT(!kp.links.empty());
  1325. linkPrefix.property = linkPrefix.links.back();
  1326. linkPrefix.links.pop_back();
  1327. }
  1328. return CollectionOperation(kp.collectionOperation, column_reference_from_key_path(std::move(linkPrefix), true),
  1329. column_reference_from_key_path(std::move(kp), true));
  1330. }
  1331. NSPredicateOperatorType invert_comparison_operator(NSPredicateOperatorType type) {
  1332. switch (type) {
  1333. case NSLessThanPredicateOperatorType:
  1334. return NSGreaterThanPredicateOperatorType;
  1335. case NSLessThanOrEqualToPredicateOperatorType:
  1336. return NSGreaterThanOrEqualToPredicateOperatorType;
  1337. case NSGreaterThanPredicateOperatorType:
  1338. return NSLessThanPredicateOperatorType;
  1339. case NSGreaterThanOrEqualToPredicateOperatorType:
  1340. return NSLessThanOrEqualToPredicateOperatorType;
  1341. case NSBeginsWithPredicateOperatorType:
  1342. case NSEndsWithPredicateOperatorType:
  1343. case NSContainsPredicateOperatorType:
  1344. case NSLikePredicateOperatorType:
  1345. throwException(@"Unsupported predicate", @"Operator '%@' requires a keypath on the left side.", operatorName(type));
  1346. default:
  1347. return type;
  1348. }
  1349. }
  1350. void QueryBuilder::apply_collection_operator_expression(KeyPath&& kp, id value,
  1351. NSComparisonPredicate *pred) {
  1352. CollectionOperation operation = collection_operation_from_key_path(std::move(kp));
  1353. operation.validate_comparison(value);
  1354. auto type = pred.predicateOperatorType;
  1355. if (pred.leftExpression.expressionType != NSKeyPathExpressionType) {
  1356. // Turn "a > b" into "b < a" so that we can always put the column on the lhs
  1357. type = invert_comparison_operator(type);
  1358. }
  1359. add_collection_operation_constraint(type, operation, value, pred.options);
  1360. }
  1361. void QueryBuilder::apply_value_expression(KeyPath&& kp, id value, NSComparisonPredicate *pred)
  1362. {
  1363. if (key_path_contains_collection_operator(kp)) {
  1364. apply_collection_operator_expression(std::move(kp), value, pred);
  1365. return;
  1366. }
  1367. bool isAny = pred.comparisonPredicateModifier == NSAnyPredicateModifier;
  1368. ColumnReference column = column_reference_from_key_path(std::move(kp), isAny);
  1369. // check to see if this is a between query
  1370. if (pred.predicateOperatorType == NSBetweenPredicateOperatorType) {
  1371. add_between_constraint(std::move(column), value);
  1372. return;
  1373. }
  1374. // turn "key.path IN collection" into ored together ==. "collection IN key.path" is handled elsewhere.
  1375. if (pred.predicateOperatorType == NSInPredicateOperatorType) {
  1376. process_or_group(m_query, value, [&](id item) {
  1377. id normalized = value_from_constant_expression_or_value(item);
  1378. column.validate_comparison(normalized);
  1379. add_constraint(NSEqualToPredicateOperatorType, pred.options, column, normalized);
  1380. });
  1381. return;
  1382. }
  1383. column.validate_comparison(value);
  1384. if (pred.leftExpression.expressionType == NSKeyPathExpressionType) {
  1385. add_constraint(pred.predicateOperatorType, pred.options, std::move(column), value);
  1386. } else {
  1387. add_constraint(invert_comparison_operator(pred.predicateOperatorType), pred.options, std::move(column), value);
  1388. }
  1389. }
  1390. void QueryBuilder::apply_column_expression(KeyPath&& leftKeyPath, KeyPath&& rightKeyPath, NSComparisonPredicate *predicate)
  1391. {
  1392. bool left_key_path_contains_collection_operator = key_path_contains_collection_operator(leftKeyPath);
  1393. bool right_key_path_contains_collection_operator = key_path_contains_collection_operator(rightKeyPath);
  1394. if (left_key_path_contains_collection_operator && right_key_path_contains_collection_operator) {
  1395. throwException(@"Unsupported predicate", @"Key paths including aggregate operations cannot be compared with other aggregate operations.");
  1396. }
  1397. if (left_key_path_contains_collection_operator) {
  1398. CollectionOperation left = collection_operation_from_key_path(std::move(leftKeyPath));
  1399. ColumnReference right = column_reference_from_key_path(std::move(rightKeyPath), false);
  1400. left.validate_comparison(right);
  1401. add_collection_operation_constraint(predicate.predicateOperatorType, std::move(left), std::move(right), predicate.options);
  1402. return;
  1403. }
  1404. if (right_key_path_contains_collection_operator) {
  1405. ColumnReference left = column_reference_from_key_path(std::move(leftKeyPath), false);
  1406. CollectionOperation right = collection_operation_from_key_path(std::move(rightKeyPath));
  1407. right.validate_comparison(left);
  1408. add_collection_operation_constraint(invert_comparison_operator(predicate.predicateOperatorType),
  1409. std::move(right), std::move(left), predicate.options);
  1410. return;
  1411. }
  1412. bool isAny = false;
  1413. ColumnReference left = column_reference_from_key_path(std::move(leftKeyPath), isAny);
  1414. ColumnReference right = column_reference_from_key_path(std::move(rightKeyPath), isAny);
  1415. // NOTE: It's assumed that column type must match and no automatic type conversion is supported.
  1416. RLMPrecondition(left.type() == right.type(),
  1417. RLMPropertiesComparisonTypeMismatchException,
  1418. RLMPropertiesComparisonTypeMismatchReason,
  1419. RLMTypeToString(left.type()),
  1420. RLMTypeToString(right.type()));
  1421. // TODO: Should we handle special case where left row is the same as right row (tautology)
  1422. add_constraint(predicate.predicateOperatorType, predicate.options,
  1423. std::move(left), std::move(right));
  1424. }
  1425. // Identify expressions of the form [SELF valueForKeyPath:]
  1426. bool is_self_value_for_key_path_function_expression(NSExpression *expression)
  1427. {
  1428. if (expression.expressionType != NSFunctionExpressionType)
  1429. return false;
  1430. if (expression.operand.expressionType != NSEvaluatedObjectExpressionType)
  1431. return false;
  1432. return [expression.function isEqualToString:@"valueForKeyPath:"];
  1433. }
  1434. // -[NSPredicate predicateWithSubtitutionVariables:] results in function expressions of the form [SELF valueForKeyPath:]
  1435. // that apply_predicate cannot handle. Replace such expressions with equivalent NSKeyPathExpressionType expressions.
  1436. NSExpression *simplify_self_value_for_key_path_function_expression(NSExpression *expression) {
  1437. if (is_self_value_for_key_path_function_expression(expression)) {
  1438. if (NSString *keyPath = [expression.arguments.firstObject keyPath]) {
  1439. return [NSExpression expressionForKeyPath:keyPath];
  1440. }
  1441. }
  1442. return expression;
  1443. }
  1444. void QueryBuilder::apply_map_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  1445. NSComparisonPredicateOptions options, NSPredicateOperatorType operatorType,
  1446. NSExpression *right) {
  1447. NSString *keyPath;
  1448. NSString *mapKey;
  1449. if (functionExpression.operand.expressionType == NSKeyPathExpressionType) {
  1450. NSExpression *mapItems = [functionExpression.arguments firstObject];
  1451. NSExpression *linkCol = [[functionExpression.operand arguments] firstObject];
  1452. NSExpression *mapCol = [mapItems.arguments firstObject];
  1453. mapKey = [mapItems.arguments[1] constantValue];
  1454. keyPath = [NSString stringWithFormat:@"%@.%@", linkCol.keyPath, mapCol.keyPath];
  1455. } else {
  1456. keyPath = [functionExpression.arguments.firstObject keyPath];
  1457. mapKey = [functionExpression.arguments[1] constantValue];
  1458. }
  1459. ColumnReference collectionColumn = column_reference_from_key_path(key_path_from_string(m_schema, objectSchema, keyPath), true);
  1460. RLMPrecondition(collectionColumn.property().dictionary, @"Invalid predicate",
  1461. @"Invalid keypath '%@': only dictionaries support subscript predicates.", functionExpression);
  1462. add_mixed_constraint(operatorType, options, collectionColumn.resolve<Dictionary>().key(mapKey.UTF8String), right.constantValue);
  1463. }
  1464. void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  1465. NSPredicateOperatorType operatorType, NSExpression *right) {
  1466. RLMPrecondition(functionExpression.operand.expressionType == NSSubqueryExpressionType,
  1467. @"Invalid predicate", @"The '%@' function is not supported.", functionExpression.function);
  1468. RLMPrecondition([functionExpression.function isEqualToString:@"valueForKeyPath:"] && functionExpression.arguments.count == 1,
  1469. @"Invalid predicate", @"The '%@' function is not supported on the result of a SUBQUERY.", functionExpression.function);
  1470. NSExpression *keyPathExpression = functionExpression.arguments.firstObject;
  1471. RLMPrecondition([keyPathExpression.keyPath isEqualToString:@"@count"],
  1472. @"Invalid predicate", @"SUBQUERY is only supported when immediately followed by .@count that is compared with a constant number.");
  1473. RLMPrecondition(right.expressionType == NSConstantValueExpressionType && [right.constantValue isKindOfClass:[NSNumber class]],
  1474. @"Invalid predicate expression", @"SUBQUERY(…).@count is only supported when compared with a constant number.");
  1475. NSExpression *subqueryExpression = functionExpression.operand;
  1476. int64_t value = [right.constantValue integerValue];
  1477. ColumnReference collectionColumn = column_reference_from_key_path(key_path_from_string(m_schema, objectSchema, [subqueryExpression.collection keyPath]), true);
  1478. RLMObjectSchema *collectionMemberObjectSchema = m_schema[collectionColumn.property().objectClassName];
  1479. // Eliminate references to the iteration variable in the subquery.
  1480. NSPredicate *subqueryPredicate = [subqueryExpression.predicate predicateWithSubstitutionVariables:@{subqueryExpression.variable: [NSExpression expressionForEvaluatedObject]}];
  1481. subqueryPredicate = transformPredicate(subqueryPredicate, simplify_self_value_for_key_path_function_expression);
  1482. Query subquery = RLMPredicateToQuery(subqueryPredicate, collectionMemberObjectSchema, m_schema, m_group);
  1483. add_numeric_constraint(RLMPropertyTypeInt, operatorType,
  1484. collectionColumn.resolve<Link>(std::move(subquery)).count(), value);
  1485. }
  1486. void QueryBuilder::apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema)
  1487. {
  1488. // Compound predicates.
  1489. if ([predicate isMemberOfClass:[NSCompoundPredicate class]]) {
  1490. NSCompoundPredicate *comp = (NSCompoundPredicate *)predicate;
  1491. switch ([comp compoundPredicateType]) {
  1492. case NSAndPredicateType:
  1493. if (comp.subpredicates.count) {
  1494. // Add all of the subpredicates.
  1495. m_query.group();
  1496. for (NSPredicate *subp in comp.subpredicates) {
  1497. apply_predicate(subp, objectSchema);
  1498. }
  1499. m_query.end_group();
  1500. } else {
  1501. // NSCompoundPredicate's documentation states that an AND predicate with no subpredicates evaluates to TRUE.
  1502. m_query.and_query(std::unique_ptr<Expression>(new TrueExpression));
  1503. }
  1504. break;
  1505. case NSOrPredicateType: {
  1506. // Add all of the subpredicates with ors inbetween.
  1507. process_or_group(m_query, comp.subpredicates, [&](__unsafe_unretained NSPredicate *const subp) {
  1508. apply_predicate(subp, objectSchema);
  1509. });
  1510. break;
  1511. }
  1512. case NSNotPredicateType:
  1513. // Add the negated subpredicate
  1514. m_query.Not();
  1515. apply_predicate(comp.subpredicates.firstObject, objectSchema);
  1516. break;
  1517. default:
  1518. // Not actually possible short of users making their own weird
  1519. // broken subclass of NSPredicate
  1520. throwException(@"Invalid compound predicate type",
  1521. @"Only AND, OR, and NOT compound predicates are supported");
  1522. }
  1523. }
  1524. else if ([predicate isMemberOfClass:[NSComparisonPredicate class]]) {
  1525. NSComparisonPredicate *compp = (NSComparisonPredicate *)predicate;
  1526. RLMPrecondition(compp.comparisonPredicateModifier != NSAllPredicateModifier,
  1527. @"Invalid predicate", @"ALL modifier not supported");
  1528. NSExpressionType exp1Type = compp.leftExpression.expressionType;
  1529. NSExpressionType exp2Type = compp.rightExpression.expressionType;
  1530. if (compp.predicateOperatorType == NSBetweenPredicateOperatorType || compp.predicateOperatorType == NSInPredicateOperatorType) {
  1531. // Inserting an array via %@ gives NSConstantValueExpressionType, but including it directly gives NSAggregateExpressionType
  1532. if (exp1Type == NSKeyPathExpressionType && (exp2Type == NSAggregateExpressionType || exp2Type == NSConstantValueExpressionType)) {
  1533. // "key.path IN %@", "key.path IN {…}", "key.path BETWEEN %@", or "key.path BETWEEN {…}".
  1534. exp2Type = NSConstantValueExpressionType;
  1535. }
  1536. else if (compp.predicateOperatorType == NSInPredicateOperatorType && exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) {
  1537. // "%@ IN key.path" is equivalent to "ANY key.path IN %@". Rewrite the former into the latter.
  1538. compp = [NSComparisonPredicate predicateWithLeftExpression:compp.rightExpression rightExpression:compp.leftExpression
  1539. modifier:NSAnyPredicateModifier type:NSEqualToPredicateOperatorType options:0];
  1540. exp1Type = NSKeyPathExpressionType;
  1541. exp2Type = NSConstantValueExpressionType;
  1542. }
  1543. else {
  1544. if (compp.predicateOperatorType == NSBetweenPredicateOperatorType) {
  1545. throwException(@"Invalid predicate",
  1546. @"Predicate with BETWEEN operator must compare a KeyPath with an aggregate with two values");
  1547. }
  1548. else if (compp.predicateOperatorType == NSInPredicateOperatorType) {
  1549. throwException(@"Invalid predicate",
  1550. @"Predicate with IN operator must compare a KeyPath with an aggregate");
  1551. }
  1552. }
  1553. }
  1554. if (exp1Type == NSKeyPathExpressionType && exp2Type == NSKeyPathExpressionType) {
  1555. // both expression are KeyPaths
  1556. apply_column_expression(key_path_from_string(m_schema, objectSchema, compp.leftExpression.keyPath),
  1557. key_path_from_string(m_schema, objectSchema, compp.rightExpression.keyPath),
  1558. compp);
  1559. }
  1560. else if (exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType) {
  1561. // comparing keypath to value
  1562. apply_value_expression(key_path_from_string(m_schema, objectSchema, compp.leftExpression.keyPath),
  1563. compp.rightExpression.constantValue, compp);
  1564. }
  1565. else if (exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) {
  1566. // comparing value to keypath
  1567. apply_value_expression(key_path_from_string(m_schema, objectSchema, compp.rightExpression.keyPath),
  1568. compp.leftExpression.constantValue, compp);
  1569. }
  1570. else if (exp1Type == NSFunctionExpressionType) {
  1571. if (compp.leftExpression.operand.expressionType == NSSubqueryExpressionType) {
  1572. apply_function_expression(objectSchema, compp.leftExpression, compp.predicateOperatorType, compp.rightExpression);
  1573. } else {
  1574. apply_map_expression(objectSchema, compp.leftExpression, compp.options, compp.predicateOperatorType, compp.rightExpression);
  1575. }
  1576. }
  1577. else if (exp1Type == NSSubqueryExpressionType) {
  1578. // The subquery expressions that we support are handled by the NSFunctionExpressionType case above.
  1579. throwException(@"Invalid predicate expression", @"SUBQUERY is only supported when immediately followed by .@count.");
  1580. }
  1581. else {
  1582. throwException(@"Invalid predicate expressions",
  1583. @"Predicate expressions must compare a keypath and another keypath or a constant value");
  1584. }
  1585. }
  1586. else if ([predicate isEqual:[NSPredicate predicateWithValue:YES]]) {
  1587. m_query.and_query(std::unique_ptr<Expression>(new TrueExpression));
  1588. } else if ([predicate isEqual:[NSPredicate predicateWithValue:NO]]) {
  1589. m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  1590. }
  1591. else {
  1592. // invalid predicate type
  1593. throwException(@"Invalid predicate",
  1594. @"Only support compound, comparison, and constant predicates");
  1595. }
  1596. }
  1597. } // namespace
  1598. realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema,
  1599. RLMSchema *schema, Group &group)
  1600. {
  1601. auto query = get_table(group, objectSchema).where();
  1602. // passing a nil predicate is a no-op
  1603. if (!predicate) {
  1604. return query;
  1605. }
  1606. try {
  1607. @autoreleasepool {
  1608. QueryBuilder(query, group, schema).apply_predicate(predicate, objectSchema);
  1609. }
  1610. }
  1611. catch (std::exception const& e) {
  1612. @throw RLMException(e);
  1613. }
  1614. return query;
  1615. }
  1616. // return the property for a validated column name
  1617. RLMProperty *RLMValidatedProperty(RLMObjectSchema *desc, NSString *columnName) {
  1618. RLMProperty *prop = desc[columnName];
  1619. RLMPrecondition(prop, @"Invalid property name",
  1620. @"Property '%@' not found in object of type '%@'", columnName, desc.className);
  1621. return prop;
  1622. }