RLMQueryUtil.mm 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  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 "RLMArray.h"
  20. #import "RLMObjectSchema_Private.h"
  21. #import "RLMObject_Private.hpp"
  22. #import "RLMPredicateUtil.hpp"
  23. #import "RLMProperty_Private.h"
  24. #import "RLMSchema.h"
  25. #import "RLMUtil.hpp"
  26. #import "object_store.hpp"
  27. #import "results.hpp"
  28. #include <realm/query_engine.hpp>
  29. #include <realm/query_expression.hpp>
  30. #include <realm/util/cf_ptr.hpp>
  31. #include <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. // small helper to create the many exceptions thrown when parsing predicates
  38. static NSException *RLMPredicateException(NSString *name, NSString *format, ...) {
  39. va_list args;
  40. va_start(args, format);
  41. NSString *reason = [[NSString alloc] initWithFormat:format arguments:args];
  42. va_end(args);
  43. return [NSException exceptionWithName:name reason:reason userInfo:nil];
  44. }
  45. // check a precondition and throw an exception if it is not met
  46. // this should be used iff the condition being false indicates a bug in the caller
  47. // of the function checking its preconditions
  48. static void RLMPrecondition(bool condition, NSString *name, NSString *format, ...) {
  49. if (__builtin_expect(condition, 1)) {
  50. return;
  51. }
  52. va_list args;
  53. va_start(args, format);
  54. NSString *reason = [[NSString alloc] initWithFormat:format arguments:args];
  55. va_end(args);
  56. @throw [NSException exceptionWithName:name reason:reason userInfo:nil];
  57. }
  58. // return the property for a validated column name
  59. RLMProperty *RLMValidatedProperty(RLMObjectSchema *desc, NSString *columnName) {
  60. RLMProperty *prop = desc[columnName];
  61. RLMPrecondition(prop, @"Invalid property name",
  62. @"Property '%@' not found in object of type '%@'", columnName, desc.className);
  63. return prop;
  64. }
  65. namespace {
  66. BOOL RLMPropertyTypeIsNumeric(RLMPropertyType propertyType) {
  67. switch (propertyType) {
  68. case RLMPropertyTypeInt:
  69. case RLMPropertyTypeFloat:
  70. case RLMPropertyTypeDouble:
  71. return YES;
  72. default:
  73. return NO;
  74. }
  75. }
  76. // Equal and ContainsSubstring are used by QueryBuilder::add_string_constraint as the comparator
  77. // for performing diacritic-insensitive comparisons.
  78. bool equal(CFStringCompareFlags options, StringData v1, StringData v2)
  79. {
  80. if (v1.is_null() || v2.is_null()) {
  81. return v1.is_null() == v2.is_null();
  82. }
  83. auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
  84. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  85. auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
  86. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  87. return CFStringCompare(s1.get(), s2.get(), options) == kCFCompareEqualTo;
  88. }
  89. template <CFStringCompareFlags options>
  90. struct Equal {
  91. using CaseSensitive = Equal<options & ~kCFCompareCaseInsensitive>;
  92. using CaseInsensitive = Equal<options | kCFCompareCaseInsensitive>;
  93. bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const
  94. {
  95. REALM_ASSERT_DEBUG(v1_null == v1.is_null());
  96. REALM_ASSERT_DEBUG(v2_null == v2.is_null());
  97. return equal(options, v1, v2);
  98. }
  99. static const char* description() { return options & kCFCompareCaseInsensitive ? "==[cd]" : "==[d]"; }
  100. };
  101. bool contains_substring(CFStringCompareFlags options, StringData v1, StringData v2)
  102. {
  103. if (v2.is_null()) {
  104. // Everything contains NULL
  105. return true;
  106. }
  107. if (v1.is_null()) {
  108. // NULL contains nothing (except NULL, handled above)
  109. return false;
  110. }
  111. if (v2.size() == 0) {
  112. // Everything (except NULL, handled above) contains the empty string
  113. return true;
  114. }
  115. auto s1 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v1.data(), v1.size(),
  116. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  117. auto s2 = util::adoptCF(CFStringCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8*)v2.data(), v2.size(),
  118. kCFStringEncodingUTF8, false, kCFAllocatorNull));
  119. return CFStringFind(s1.get(), s2.get(), options).location != kCFNotFound;
  120. }
  121. template <CFStringCompareFlags options>
  122. struct ContainsSubstring {
  123. using CaseSensitive = ContainsSubstring<options & ~kCFCompareCaseInsensitive>;
  124. using CaseInsensitive = ContainsSubstring<options | kCFCompareCaseInsensitive>;
  125. bool operator()(StringData v1, StringData v2, bool v1_null, bool v2_null) const
  126. {
  127. REALM_ASSERT_DEBUG(v1_null == v1.is_null());
  128. REALM_ASSERT_DEBUG(v2_null == v2.is_null());
  129. return contains_substring(options, v1, v2);
  130. }
  131. static const char* description() { return options & kCFCompareCaseInsensitive ? "CONTAINS[cd]" : "CONTAINS[d]"; }
  132. };
  133. NSString *operatorName(NSPredicateOperatorType operatorType)
  134. {
  135. switch (operatorType) {
  136. case NSLessThanPredicateOperatorType:
  137. return @"<";
  138. case NSLessThanOrEqualToPredicateOperatorType:
  139. return @"<=";
  140. case NSGreaterThanPredicateOperatorType:
  141. return @">";
  142. case NSGreaterThanOrEqualToPredicateOperatorType:
  143. return @">=";
  144. case NSEqualToPredicateOperatorType:
  145. return @"==";
  146. case NSNotEqualToPredicateOperatorType:
  147. return @"!=";
  148. case NSMatchesPredicateOperatorType:
  149. return @"MATCHES";
  150. case NSLikePredicateOperatorType:
  151. return @"LIKE";
  152. case NSBeginsWithPredicateOperatorType:
  153. return @"BEGINSWITH";
  154. case NSEndsWithPredicateOperatorType:
  155. return @"ENDSWITH";
  156. case NSInPredicateOperatorType:
  157. return @"IN";
  158. case NSContainsPredicateOperatorType:
  159. return @"CONTAINS";
  160. case NSBetweenPredicateOperatorType:
  161. return @"BETWEEN";
  162. case NSCustomSelectorPredicateOperatorType:
  163. return @"custom selector";
  164. }
  165. return [NSString stringWithFormat:@"unknown operator %lu", (unsigned long)operatorType];
  166. }
  167. Table& get_table(Group& group, RLMObjectSchema *objectSchema)
  168. {
  169. return *ObjectStore::table_for_object_type(group, objectSchema.objectName.UTF8String);
  170. }
  171. // A reference to a column within a query. Can be resolved to a Columns<T> for use in query expressions.
  172. class ColumnReference {
  173. public:
  174. ColumnReference(Query& query, Group& group, RLMSchema *schema, RLMProperty* property, const std::vector<RLMProperty*>& links = {})
  175. : m_links(links), m_property(property), m_schema(schema), m_group(&group), m_query(&query), m_table(query.get_table())
  176. {
  177. auto& table = walk_link_chain([](Table const&, ColKey, RLMPropertyType) { });
  178. m_col = table.get_column_key(m_property.columnName.UTF8String);
  179. }
  180. template <typename T, typename... SubQuery>
  181. auto resolve(SubQuery&&... subquery) const
  182. {
  183. static_assert(sizeof...(SubQuery) < 2, "resolve() takes at most one subquery");
  184. LinkChain lc(m_table);
  185. walk_link_chain([&](Table const& link_origin, ColKey col, RLMPropertyType type) {
  186. if (type != RLMPropertyTypeLinkingObjects) {
  187. lc.link(col);
  188. }
  189. else {
  190. lc.backlink(link_origin, col);
  191. }
  192. });
  193. if (type() != RLMPropertyTypeLinkingObjects) {
  194. return lc.column<T>(column(), std::forward<SubQuery>(subquery)...);
  195. }
  196. if constexpr (std::is_same_v<T, Link>) {
  197. return with_link_origin(m_property, [&](Table& table, ColKey col) {
  198. return lc.column<T>(table, col, std::forward<SubQuery>(subquery)...);
  199. });
  200. }
  201. REALM_TERMINATE("LinkingObjects property did not have column type Link");
  202. }
  203. RLMProperty *property() const { return m_property; }
  204. ColKey column() const { return m_col; }
  205. RLMPropertyType type() const { return property().type; }
  206. Group& group() const { return *m_group; }
  207. RLMObjectSchema *link_target_object_schema() const
  208. {
  209. switch (type()) {
  210. case RLMPropertyTypeObject:
  211. case RLMPropertyTypeLinkingObjects:
  212. return m_schema[property().objectClassName];
  213. default:
  214. REALM_UNREACHABLE();
  215. }
  216. }
  217. bool has_links() const { return m_links.size(); }
  218. bool has_any_to_many_links() const {
  219. return std::any_of(begin(m_links), end(m_links),
  220. [](RLMProperty *property) { return property.array; });
  221. }
  222. ColumnReference last_link_column() const {
  223. REALM_ASSERT(!m_links.empty());
  224. return {*m_query, *m_group, m_schema, m_links.back(), {m_links.begin(), m_links.end() - 1}};
  225. }
  226. ColumnReference column_ignoring_links(Query& query) const {
  227. return {query, *m_group, m_schema, m_property};
  228. }
  229. private:
  230. template<typename Func>
  231. Table const& walk_link_chain(Func&& func) const
  232. {
  233. auto table = m_query->get_table().unchecked_ptr();
  234. for (const auto& link : m_links) {
  235. if (link.type != RLMPropertyTypeLinkingObjects) {
  236. auto index = table->get_column_key(link.columnName.UTF8String);
  237. func(*table, index, link.type);
  238. table = table->get_link_target(index).unchecked_ptr();
  239. }
  240. else {
  241. with_link_origin(link, [&](Table& link_origin_table, ColKey link_origin_column) {
  242. func(link_origin_table, link_origin_column, link.type);
  243. table = &link_origin_table;
  244. });
  245. }
  246. }
  247. return *table;
  248. }
  249. template<typename Func>
  250. auto with_link_origin(RLMProperty *prop, Func&& func) const
  251. {
  252. RLMObjectSchema *link_origin_schema = m_schema[prop.objectClassName];
  253. Table& link_origin_table = get_table(*m_group, link_origin_schema);
  254. NSString *column_name = link_origin_schema[prop.linkOriginPropertyName].columnName;
  255. auto link_origin_column = link_origin_table.get_column_key(column_name.UTF8String);
  256. return func(link_origin_table, link_origin_column);
  257. }
  258. std::vector<RLMProperty*> m_links;
  259. RLMProperty *m_property;
  260. RLMSchema *m_schema;
  261. Group *m_group;
  262. Query *m_query;
  263. ConstTableRef m_table;
  264. ColKey m_col;
  265. };
  266. class CollectionOperation {
  267. public:
  268. enum Type {
  269. Count,
  270. Minimum,
  271. Maximum,
  272. Sum,
  273. Average,
  274. };
  275. CollectionOperation(Type type, ColumnReference link_column, util::Optional<ColumnReference> column)
  276. : m_type(type)
  277. , m_link_column(std::move(link_column))
  278. , m_column(std::move(column))
  279. {
  280. RLMPrecondition(m_link_column.property().array,
  281. @"Invalid predicate", @"Collection operation can only be applied to a property of type RLMArray.");
  282. switch (m_type) {
  283. case Count:
  284. RLMPrecondition(!m_column, @"Invalid predicate", @"Result of @count does not have any properties.");
  285. break;
  286. case Minimum:
  287. case Maximum:
  288. case Sum:
  289. case Average:
  290. RLMPrecondition(m_column && RLMPropertyTypeIsNumeric(m_column->type()), @"Invalid predicate",
  291. @"%@ can only be applied to a numeric property.", name_for_type(m_type));
  292. break;
  293. }
  294. }
  295. CollectionOperation(NSString *operationName, ColumnReference link_column, util::Optional<ColumnReference> column = util::none)
  296. : CollectionOperation(type_for_name(operationName), std::move(link_column), std::move(column))
  297. {
  298. }
  299. Type type() const { return m_type; }
  300. const ColumnReference& link_column() const { return m_link_column; }
  301. const ColumnReference& column() const { return *m_column; }
  302. void validate_comparison(id value) const {
  303. switch (m_type) {
  304. case Count:
  305. case Average:
  306. RLMPrecondition([value isKindOfClass:[NSNumber class]], @"Invalid operand",
  307. @"%@ can only be compared with a numeric value.", name_for_type(m_type));
  308. break;
  309. case Minimum:
  310. case Maximum:
  311. case Sum:
  312. RLMPrecondition(RLMIsObjectValidForProperty(value, m_column->property()), @"Invalid operand",
  313. @"%@ on a property of type %@ cannot be compared with '%@'",
  314. name_for_type(m_type), RLMTypeToString(m_column->type()), value);
  315. break;
  316. }
  317. }
  318. void validate_comparison(const ColumnReference& column) const {
  319. switch (m_type) {
  320. case Count:
  321. RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand",
  322. @"%@ can only be compared with a numeric value.", name_for_type(m_type));
  323. break;
  324. case Average:
  325. case Minimum:
  326. case Maximum:
  327. case Sum:
  328. RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand",
  329. @"%@ on a property of type %@ cannot be compared with property of type '%@'",
  330. name_for_type(m_type), RLMTypeToString(m_column->type()), RLMTypeToString(column.type()));
  331. break;
  332. }
  333. }
  334. private:
  335. static Type type_for_name(NSString *name) {
  336. if ([name isEqualToString:@"@count"]) {
  337. return Count;
  338. }
  339. if ([name isEqualToString:@"@min"]) {
  340. return Minimum;
  341. }
  342. if ([name isEqualToString:@"@max"]) {
  343. return Maximum;
  344. }
  345. if ([name isEqualToString:@"@sum"]) {
  346. return Sum;
  347. }
  348. if ([name isEqualToString:@"@avg"]) {
  349. return Average;
  350. }
  351. @throw RLMPredicateException(@"Invalid predicate", @"Unsupported collection operation '%@'", name);
  352. }
  353. static NSString *name_for_type(Type type) {
  354. switch (type) {
  355. case Count: return @"@count";
  356. case Minimum: return @"@min";
  357. case Maximum: return @"@max";
  358. case Sum: return @"@sum";
  359. case Average: return @"@avg";
  360. }
  361. }
  362. Type m_type;
  363. ColumnReference m_link_column;
  364. util::Optional<ColumnReference> m_column;
  365. };
  366. class QueryBuilder {
  367. public:
  368. QueryBuilder(Query& query, Group& group, RLMSchema *schema)
  369. : m_query(query), m_group(group), m_schema(schema) { }
  370. void apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema);
  371. void apply_collection_operator_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred);
  372. void apply_value_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred);
  373. void apply_column_expression(RLMObjectSchema *desc, NSString *leftKeyPath, NSString *rightKeyPath, NSComparisonPredicate *predicate);
  374. void apply_subquery_count_expression(RLMObjectSchema *objectSchema, NSExpression *subqueryExpression,
  375. NSPredicateOperatorType operatorType, NSExpression *right);
  376. void apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  377. NSPredicateOperatorType operatorType, NSExpression *right);
  378. void apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  379. NSPredicateOperatorType operatorType, NSExpression *right);
  380. template <typename A, typename B>
  381. void add_numeric_constraint(RLMPropertyType datatype,
  382. NSPredicateOperatorType operatorType,
  383. A&& lhs, B&& rhs);
  384. template <typename A, typename B>
  385. void add_bool_constraint(NSPredicateOperatorType operatorType, A&& lhs, B&& rhs);
  386. void add_substring_constraint(null, Query condition);
  387. template<typename T>
  388. void add_substring_constraint(const T& value, Query condition);
  389. template<typename T>
  390. void add_substring_constraint(const Columns<T>& value, Query condition);
  391. template <typename T>
  392. void add_string_constraint(NSPredicateOperatorType operatorType,
  393. NSComparisonPredicateOptions predicateOptions,
  394. Columns<String> &&column,
  395. T value);
  396. void add_string_constraint(NSPredicateOperatorType operatorType,
  397. NSComparisonPredicateOptions predicateOptions,
  398. StringData value,
  399. Columns<String>&& column);
  400. template <typename L, typename R>
  401. void add_constraint(RLMPropertyType type,
  402. NSPredicateOperatorType operatorType,
  403. NSComparisonPredicateOptions predicateOptions,
  404. L const& lhs, R const& rhs);
  405. template <typename... T>
  406. void do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType,
  407. NSComparisonPredicateOptions predicateOptions, T&&... values);
  408. void do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null);
  409. void add_between_constraint(const ColumnReference& column, id value);
  410. void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, BinaryData value);
  411. void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value);
  412. void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, null);
  413. void add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column);
  414. void add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&);
  415. void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, RLMObjectBase *obj);
  416. void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, realm::null);
  417. template<typename T>
  418. void add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column);
  419. void add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&);
  420. template <CollectionOperation::Type Operation, typename... T>
  421. void add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values);
  422. template <typename... T>
  423. void add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  424. CollectionOperation collectionOperation, T... values);
  425. CollectionOperation collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath);
  426. ColumnReference column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPath, bool isAggregate);
  427. private:
  428. Query& m_query;
  429. Group& m_group;
  430. RLMSchema *m_schema;
  431. };
  432. // add a clause for numeric constraints based on operator type
  433. template <typename A, typename B>
  434. void QueryBuilder::add_numeric_constraint(RLMPropertyType datatype,
  435. NSPredicateOperatorType operatorType,
  436. A&& lhs, B&& rhs)
  437. {
  438. switch (operatorType) {
  439. case NSLessThanPredicateOperatorType:
  440. m_query.and_query(lhs < rhs);
  441. break;
  442. case NSLessThanOrEqualToPredicateOperatorType:
  443. m_query.and_query(lhs <= rhs);
  444. break;
  445. case NSGreaterThanPredicateOperatorType:
  446. m_query.and_query(lhs > rhs);
  447. break;
  448. case NSGreaterThanOrEqualToPredicateOperatorType:
  449. m_query.and_query(lhs >= rhs);
  450. break;
  451. case NSEqualToPredicateOperatorType:
  452. m_query.and_query(lhs == rhs);
  453. break;
  454. case NSNotEqualToPredicateOperatorType:
  455. m_query.and_query(lhs != rhs);
  456. break;
  457. default:
  458. @throw RLMPredicateException(@"Invalid operator type",
  459. @"Operator '%@' not supported for type %@",
  460. operatorName(operatorType), RLMTypeToString(datatype));
  461. }
  462. }
  463. template <typename A, typename B>
  464. void QueryBuilder::add_bool_constraint(NSPredicateOperatorType operatorType, A&& lhs, B&& rhs) {
  465. switch (operatorType) {
  466. case NSEqualToPredicateOperatorType:
  467. m_query.and_query(lhs == rhs);
  468. break;
  469. case NSNotEqualToPredicateOperatorType:
  470. m_query.and_query(lhs != rhs);
  471. break;
  472. default:
  473. @throw RLMPredicateException(@"Invalid operator type",
  474. @"Operator '%@' not supported for bool type", operatorName(operatorType));
  475. }
  476. }
  477. void QueryBuilder::add_substring_constraint(null, Query) {
  478. // Foundation always returns false for substring operations with a RHS of null or "".
  479. m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  480. }
  481. template<typename T>
  482. void QueryBuilder::add_substring_constraint(const T& value, Query condition) {
  483. // Foundation always returns false for substring operations with a RHS of null or "".
  484. m_query.and_query(value.size()
  485. ? std::move(condition)
  486. : std::unique_ptr<Expression>(new FalseExpression));
  487. }
  488. template<typename T>
  489. void QueryBuilder::add_substring_constraint(const Columns<T>& value, Query condition) {
  490. // Foundation always returns false for substring operations with a RHS of null or "".
  491. // We don't need to concern ourselves with the possibility of value traversing a link list
  492. // and producing multiple values per row as such expressions will have been rejected.
  493. m_query.and_query(const_cast<Columns<String>&>(value).size() != 0 && std::move(condition));
  494. }
  495. template <typename T>
  496. void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType,
  497. NSComparisonPredicateOptions predicateOptions,
  498. Columns<String> &&column,
  499. T value) {
  500. bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption);
  501. bool diacriticSensitive = !(predicateOptions & NSDiacriticInsensitivePredicateOption);
  502. if (diacriticSensitive) {
  503. switch (operatorType) {
  504. case NSBeginsWithPredicateOperatorType:
  505. add_substring_constraint(value, column.begins_with(value, caseSensitive));
  506. break;
  507. case NSEndsWithPredicateOperatorType:
  508. add_substring_constraint(value, column.ends_with(value, caseSensitive));
  509. break;
  510. case NSContainsPredicateOperatorType:
  511. add_substring_constraint(value, column.contains(value, caseSensitive));
  512. break;
  513. case NSEqualToPredicateOperatorType:
  514. m_query.and_query(column.equal(value, caseSensitive));
  515. break;
  516. case NSNotEqualToPredicateOperatorType:
  517. m_query.and_query(column.not_equal(value, caseSensitive));
  518. break;
  519. case NSLikePredicateOperatorType:
  520. m_query.and_query(column.like(value, caseSensitive));
  521. break;
  522. default:
  523. @throw RLMPredicateException(@"Invalid operator type",
  524. @"Operator '%@' not supported for string type",
  525. operatorName(operatorType));
  526. }
  527. return;
  528. }
  529. auto as_subexpr = util::overload([](StringData value) { return make_subexpr<ConstantStringValue>(value); },
  530. [](const Columns<String>& c) { return c.clone(); });
  531. auto left = as_subexpr(column);
  532. auto right = as_subexpr(value);
  533. auto make_constraint = [&](auto comparator) {
  534. using Comparator = decltype(comparator);
  535. using CompareCS = Compare<typename Comparator::CaseSensitive, StringData>;
  536. using CompareCI = Compare<typename Comparator::CaseInsensitive, StringData>;
  537. if (caseSensitive) {
  538. return make_expression<CompareCS>(std::move(left), std::move(right));
  539. }
  540. else {
  541. return make_expression<CompareCI>(std::move(left), std::move(right));
  542. }
  543. };
  544. switch (operatorType) {
  545. case NSBeginsWithPredicateOperatorType: {
  546. using C = ContainsSubstring<kCFCompareDiacriticInsensitive | kCFCompareAnchored>;
  547. add_substring_constraint(value, make_constraint(C{}));
  548. break;
  549. }
  550. case NSEndsWithPredicateOperatorType: {
  551. using C = ContainsSubstring<kCFCompareDiacriticInsensitive | kCFCompareAnchored | kCFCompareBackwards>;
  552. add_substring_constraint(value, make_constraint(C{}));
  553. break;
  554. }
  555. case NSContainsPredicateOperatorType: {
  556. using C = ContainsSubstring<kCFCompareDiacriticInsensitive>;
  557. add_substring_constraint(value, make_constraint(C{}));
  558. break;
  559. }
  560. case NSNotEqualToPredicateOperatorType:
  561. m_query.Not();
  562. REALM_FALLTHROUGH;
  563. case NSEqualToPredicateOperatorType:
  564. m_query.and_query(make_constraint(Equal<kCFCompareDiacriticInsensitive>{}));
  565. break;
  566. case NSLikePredicateOperatorType:
  567. @throw RLMPredicateException(@"Invalid operator type",
  568. @"Operator 'LIKE' not supported with diacritic-insensitive modifier.");
  569. default:
  570. @throw RLMPredicateException(@"Invalid operator type",
  571. @"Operator '%@' not supported for string type", operatorName(operatorType));
  572. }
  573. }
  574. void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType,
  575. NSComparisonPredicateOptions predicateOptions,
  576. StringData value,
  577. Columns<String>&& column) {
  578. switch (operatorType) {
  579. case NSEqualToPredicateOperatorType:
  580. case NSNotEqualToPredicateOperatorType:
  581. add_string_constraint(operatorType, predicateOptions, std::move(column), value);
  582. break;
  583. default:
  584. @throw RLMPredicateException(@"Invalid operator type",
  585. @"Operator '%@' is not supported for string type with key path on right side of operator",
  586. operatorName(operatorType));
  587. }
  588. }
  589. id value_from_constant_expression_or_value(id value) {
  590. if (NSExpression *exp = RLMDynamicCast<NSExpression>(value)) {
  591. RLMPrecondition(exp.expressionType == NSConstantValueExpressionType,
  592. @"Invalid value",
  593. @"Expressions within predicate aggregates must be constant values");
  594. return exp.constantValue;
  595. }
  596. return value;
  597. }
  598. void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, id *to) {
  599. NSArray *array = RLMDynamicCast<NSArray>(value);
  600. RLMPrecondition(array, @"Invalid value", @"object must be of type NSArray for BETWEEN operations");
  601. RLMPrecondition(array.count == 2, @"Invalid value", @"NSArray object must contain exactly two objects for BETWEEN operations");
  602. *from = value_from_constant_expression_or_value(array.firstObject);
  603. *to = value_from_constant_expression_or_value(array.lastObject);
  604. RLMPrecondition(RLMIsObjectValidForProperty(*from, prop) && RLMIsObjectValidForProperty(*to, prop),
  605. @"Invalid value",
  606. @"NSArray objects must be of type %@ for BETWEEN operations", RLMTypeToString(prop.type));
  607. }
  608. void QueryBuilder::add_between_constraint(const ColumnReference& column, id value) {
  609. if (column.has_any_to_many_links()) {
  610. auto link_column = column.last_link_column();
  611. Query subquery = get_table(m_group, link_column.link_target_object_schema()).where();
  612. QueryBuilder(subquery, m_group, m_schema).add_between_constraint(column.column_ignoring_links(subquery), value);
  613. m_query.and_query(link_column.resolve<Link>(std::move(subquery)).count() > 0);
  614. return;
  615. }
  616. id from, to;
  617. validate_and_extract_between_range(value, column.property(), &from, &to);
  618. RLMPropertyType type = column.type();
  619. m_query.group();
  620. add_constraint(type, NSGreaterThanOrEqualToPredicateOperatorType, 0, column, from);
  621. add_constraint(type, NSLessThanOrEqualToPredicateOperatorType, 0, column, to);
  622. m_query.end_group();
  623. }
  624. void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType,
  625. const ColumnReference& column,
  626. BinaryData value) {
  627. RLMPrecondition(!column.has_links(), @"Unsupported operator", @"NSData properties cannot be queried over an object link.");
  628. auto index = column.column();
  629. Query query = m_query.get_table()->where();
  630. switch (operatorType) {
  631. case NSBeginsWithPredicateOperatorType:
  632. add_substring_constraint(value, query.begins_with(index, value));
  633. break;
  634. case NSEndsWithPredicateOperatorType:
  635. add_substring_constraint(value, query.ends_with(index, value));
  636. break;
  637. case NSContainsPredicateOperatorType:
  638. add_substring_constraint(value, query.contains(index, value));
  639. break;
  640. case NSEqualToPredicateOperatorType:
  641. m_query.equal(index, value);
  642. break;
  643. case NSNotEqualToPredicateOperatorType:
  644. m_query.not_equal(index, value);
  645. break;
  646. default:
  647. @throw RLMPredicateException(@"Invalid operator type",
  648. @"Operator '%@' not supported for binary type", operatorName(operatorType));
  649. }
  650. }
  651. void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value) {
  652. add_binary_constraint(operatorType, column, RLMBinaryDataForNSData(value));
  653. }
  654. void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, null) {
  655. add_binary_constraint(operatorType, column, BinaryData());
  656. }
  657. void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column) {
  658. switch (operatorType) {
  659. case NSEqualToPredicateOperatorType:
  660. case NSNotEqualToPredicateOperatorType:
  661. add_binary_constraint(operatorType, column, value);
  662. break;
  663. default:
  664. @throw RLMPredicateException(@"Invalid operator type",
  665. @"Operator '%@' is not supported for binary type with key path on right side of operator",
  666. operatorName(operatorType));
  667. }
  668. }
  669. void QueryBuilder::add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) {
  670. @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two NSData properties are not supported");
  671. }
  672. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
  673. const ColumnReference& column, RLMObjectBase *obj) {
  674. RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType,
  675. @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison");
  676. if (!obj->_row.is_valid()) {
  677. // Unmanaged or deleted objects, so compare it to an object that doesn't
  678. // exist from the target table
  679. struct FakeObj : public ConstObj {
  680. FakeObj(ColumnReference const& column) {
  681. m_table = TableRef::unsafe_create(&::get_table(column.group(), column.link_target_object_schema()));
  682. }
  683. } fake(column);
  684. add_bool_constraint(operatorType, column.resolve<Link>(), fake);
  685. }
  686. else {
  687. add_bool_constraint(operatorType, column.resolve<Link>(), obj->_row);
  688. }
  689. }
  690. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType,
  691. const ColumnReference& column,
  692. realm::null) {
  693. RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType,
  694. @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison");
  695. add_bool_constraint(operatorType, column.resolve<Link>(), null());
  696. }
  697. template<typename T>
  698. void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column) {
  699. // Link constraints only support the equal-to and not-equal-to operators. The order of operands
  700. // is not important for those comparisons so we can delegate to the other implementation.
  701. add_link_constraint(operatorType, column, obj);
  702. }
  703. void QueryBuilder::add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) {
  704. // This is not actually reachable as this case is caught earlier, but this
  705. // overload is needed for the code to compile
  706. @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two RLMArray properties are not supported");
  707. }
  708. // iterate over an array of subpredicates, using @func to build a query from each
  709. // one and ORing them together
  710. template<typename Func>
  711. void process_or_group(Query &query, id array, Func&& func) {
  712. array = RLMAsFastEnumeration(array);
  713. RLMPrecondition(array, @"Invalid value", @"IN clause requires an array of items");
  714. query.group();
  715. bool first = true;
  716. for (id item in array) {
  717. if (!first) {
  718. query.Or();
  719. }
  720. first = false;
  721. func(item);
  722. }
  723. if (first) {
  724. // Queries can't be empty, so if there's zero things in the OR group
  725. // validation will fail. Work around this by adding an expression which
  726. // will never find any rows in a table.
  727. query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  728. }
  729. query.end_group();
  730. }
  731. template <typename RequestedType>
  732. RequestedType convert(id value);
  733. template <>
  734. Timestamp convert<Timestamp>(id value) {
  735. return RLMTimestampForNSDate(value);
  736. }
  737. template <>
  738. bool convert<bool>(id value) {
  739. return [value boolValue];
  740. }
  741. template <>
  742. Double convert<Double>(id value) {
  743. return [value doubleValue];
  744. }
  745. template <>
  746. Float convert<Float>(id value) {
  747. return [value floatValue];
  748. }
  749. template <>
  750. Int convert<Int>(id value) {
  751. return [value longLongValue];
  752. }
  753. template <>
  754. String convert<String>(id value) {
  755. return RLMStringDataWithNSString(value);
  756. }
  757. template <typename>
  758. realm::null value_of_type(realm::null) {
  759. return realm::null();
  760. }
  761. template <typename RequestedType>
  762. auto value_of_type(id value) {
  763. return ::convert<RequestedType>(value);
  764. }
  765. template <typename RequestedType>
  766. auto value_of_type(const ColumnReference& column) {
  767. return column.resolve<RequestedType>();
  768. }
  769. template <typename... T>
  770. void QueryBuilder::do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType,
  771. NSComparisonPredicateOptions predicateOptions, T&&... values)
  772. {
  773. static_assert(sizeof...(T) == 2, "do_add_constraint accepts only two values as arguments");
  774. switch (type) {
  775. case RLMPropertyTypeBool:
  776. add_bool_constraint(operatorType, value_of_type<bool>(values)...);
  777. break;
  778. case RLMPropertyTypeDate:
  779. add_numeric_constraint(type, operatorType, value_of_type<realm::Timestamp>(values)...);
  780. break;
  781. case RLMPropertyTypeDouble:
  782. add_numeric_constraint(type, operatorType, value_of_type<Double>(values)...);
  783. break;
  784. case RLMPropertyTypeFloat:
  785. add_numeric_constraint(type, operatorType, value_of_type<Float>(values)...);
  786. break;
  787. case RLMPropertyTypeInt:
  788. add_numeric_constraint(type, operatorType, value_of_type<Int>(values)...);
  789. break;
  790. case RLMPropertyTypeString:
  791. add_string_constraint(operatorType, predicateOptions, value_of_type<String>(values)...);
  792. break;
  793. case RLMPropertyTypeData:
  794. add_binary_constraint(operatorType, values...);
  795. break;
  796. case RLMPropertyTypeObject:
  797. case RLMPropertyTypeLinkingObjects:
  798. add_link_constraint(operatorType, values...);
  799. break;
  800. default:
  801. @throw RLMPredicateException(@"Unsupported predicate value type",
  802. @"Object type %@ not supported", RLMTypeToString(type));
  803. }
  804. }
  805. void QueryBuilder::do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null)
  806. {
  807. // This is not actually reachable as this case is caught earlier, but this
  808. // overload is needed for the code to compile
  809. @throw RLMPredicateException(@"Invalid predicate expressions",
  810. @"Predicate expressions must compare a keypath and another keypath or a constant value");
  811. }
  812. bool is_nsnull(id value) {
  813. return !value || value == NSNull.null;
  814. }
  815. template<typename T>
  816. bool is_nsnull(T) {
  817. return false;
  818. }
  819. template <typename L, typename R>
  820. void QueryBuilder::add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType,
  821. NSComparisonPredicateOptions predicateOptions, L const& lhs, R const& rhs)
  822. {
  823. // The expression operators are only overloaded for realm::null on the rhs
  824. RLMPrecondition(!is_nsnull(lhs), @"Unsupported operator",
  825. @"Nil is only supported on the right side of operators");
  826. if (is_nsnull(rhs)) {
  827. do_add_constraint(type, operatorType, predicateOptions, lhs, realm::null());
  828. }
  829. else {
  830. do_add_constraint(type, operatorType, predicateOptions, lhs, rhs);
  831. }
  832. }
  833. struct KeyPath {
  834. std::vector<RLMProperty *> links;
  835. RLMProperty *property;
  836. bool containsToManyRelationship;
  837. };
  838. KeyPath key_path_from_string(RLMSchema *schema, RLMObjectSchema *objectSchema, NSString *keyPath)
  839. {
  840. RLMProperty *property;
  841. std::vector<RLMProperty *> links;
  842. bool keyPathContainsToManyRelationship = false;
  843. NSUInteger start = 0, length = keyPath.length, end = NSNotFound;
  844. do {
  845. end = [keyPath rangeOfString:@"." options:0 range:{start, length - start}].location;
  846. NSString *propertyName = [keyPath substringWithRange:{start, end == NSNotFound ? length - start : end - start}];
  847. property = objectSchema[propertyName];
  848. RLMPrecondition(property, @"Invalid property name",
  849. @"Property '%@' not found in object of type '%@'",
  850. propertyName, objectSchema.className);
  851. if (property.array)
  852. keyPathContainsToManyRelationship = true;
  853. if (end != NSNotFound) {
  854. RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeLinkingObjects,
  855. @"Invalid value", @"Property '%@' is not a link in object of type '%@'",
  856. propertyName, objectSchema.className);
  857. links.push_back(property);
  858. REALM_ASSERT(property.objectClassName);
  859. objectSchema = schema[property.objectClassName];
  860. }
  861. start = end + 1;
  862. } while (end != NSNotFound);
  863. return {std::move(links), property, keyPathContainsToManyRelationship};
  864. }
  865. ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema,
  866. NSString *keyPathString, bool isAggregate)
  867. {
  868. auto keyPath = key_path_from_string(m_schema, objectSchema, keyPathString);
  869. if (isAggregate && !keyPath.containsToManyRelationship) {
  870. @throw RLMPredicateException(@"Invalid predicate",
  871. @"Aggregate operations can only be used on key paths that include an array property");
  872. } else if (!isAggregate && keyPath.containsToManyRelationship) {
  873. @throw RLMPredicateException(@"Invalid predicate",
  874. @"Key paths that include an array property must use aggregate operations");
  875. }
  876. return ColumnReference(m_query, m_group, m_schema, keyPath.property, std::move(keyPath.links));
  877. }
  878. void validate_property_value(const ColumnReference& column,
  879. __unsafe_unretained id const value,
  880. __unsafe_unretained NSString *const err,
  881. __unsafe_unretained RLMObjectSchema *const objectSchema,
  882. __unsafe_unretained NSString *const keyPath) {
  883. RLMProperty *prop = column.property();
  884. if (prop.array) {
  885. RLMPrecondition([RLMObjectBaseObjectSchema(RLMDynamicCast<RLMObjectBase>(value)).className isEqualToString:prop.objectClassName],
  886. @"Invalid value", err, prop.objectClassName, keyPath, objectSchema.className, value);
  887. }
  888. else {
  889. RLMPrecondition(RLMIsObjectValidForProperty(value, prop),
  890. @"Invalid value", err, RLMTypeToString(prop.type), keyPath, objectSchema.className, value);
  891. }
  892. if (RLMObjectBase *obj = RLMDynamicCast<RLMObjectBase>(value)) {
  893. RLMPrecondition(!obj->_row.is_valid() || &column.group() == &obj->_realm.group,
  894. @"Invalid value origin", @"Object must be from the Realm being queried");
  895. }
  896. }
  897. template <typename RequestedType, CollectionOperation::Type OperationType>
  898. struct ValueOfTypeWithCollectionOperationHelper;
  899. template <>
  900. struct ValueOfTypeWithCollectionOperationHelper<Int, CollectionOperation::Count> {
  901. static auto convert(const CollectionOperation& operation)
  902. {
  903. assert(operation.type() == CollectionOperation::Count);
  904. return operation.link_column().resolve<Link>().count();
  905. }
  906. };
  907. #define VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(OperationType, function) \
  908. template <typename T> \
  909. struct ValueOfTypeWithCollectionOperationHelper<T, OperationType> { \
  910. static auto convert(const CollectionOperation& operation) \
  911. { \
  912. REALM_ASSERT(operation.type() == OperationType); \
  913. auto targetColumn = operation.link_column().resolve<Link>().template column<T>(operation.column().column()); \
  914. return targetColumn.function(); \
  915. } \
  916. } \
  917. VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Minimum, min);
  918. VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Maximum, max);
  919. VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Sum, sum);
  920. VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Average, average);
  921. #undef VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER
  922. template <typename Requested, CollectionOperation::Type OperationType, typename T>
  923. auto value_of_type_with_collection_operation(T&& value) {
  924. return value_of_type<Requested>(std::forward<T>(value));
  925. }
  926. template <typename Requested, CollectionOperation::Type OperationType>
  927. auto value_of_type_with_collection_operation(CollectionOperation operation) {
  928. using helper = ValueOfTypeWithCollectionOperationHelper<Requested, OperationType>;
  929. return helper::convert(operation);
  930. }
  931. template <CollectionOperation::Type Operation, typename... T>
  932. void QueryBuilder::add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values)
  933. {
  934. switch (propertyType) {
  935. case RLMPropertyTypeInt:
  936. add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation<Int, Operation>(values)...);
  937. break;
  938. case RLMPropertyTypeFloat:
  939. add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation<Float, Operation>(values)...);
  940. break;
  941. case RLMPropertyTypeDouble:
  942. add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation<Double, Operation>(values)...);
  943. break;
  944. default:
  945. REALM_ASSERT(false && "Only numeric property types should hit this path.");
  946. }
  947. }
  948. template <typename... T>
  949. void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType,
  950. CollectionOperation collectionOperation, T... values)
  951. {
  952. static_assert(sizeof...(T) == 2, "add_collection_operation_constraint accepts only two values as arguments");
  953. switch (collectionOperation.type()) {
  954. case CollectionOperation::Count:
  955. add_numeric_constraint(RLMPropertyTypeInt, operatorType,
  956. value_of_type_with_collection_operation<Int, CollectionOperation::Count>(values)...);
  957. break;
  958. case CollectionOperation::Minimum:
  959. add_collection_operation_constraint<CollectionOperation::Minimum>(collectionOperation.column().type(), operatorType, values...);
  960. break;
  961. case CollectionOperation::Maximum:
  962. add_collection_operation_constraint<CollectionOperation::Maximum>(collectionOperation.column().type(), operatorType, values...);
  963. break;
  964. case CollectionOperation::Sum:
  965. add_collection_operation_constraint<CollectionOperation::Sum>(collectionOperation.column().type(), operatorType, values...);
  966. break;
  967. case CollectionOperation::Average:
  968. add_collection_operation_constraint<CollectionOperation::Average>(collectionOperation.column().type(), operatorType, values...);
  969. break;
  970. }
  971. }
  972. bool key_path_contains_collection_operator(NSString *keyPath) {
  973. return [keyPath rangeOfString:@"@"].location != NSNotFound;
  974. }
  975. NSString *get_collection_operation_name_from_key_path(NSString *keyPath, NSString **leadingKeyPath,
  976. NSString **trailingKey) {
  977. NSRange at = [keyPath rangeOfString:@"@"];
  978. if (at.location == NSNotFound || at.location >= keyPath.length - 1) {
  979. @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath);
  980. }
  981. if (at.location == 0 || [keyPath characterAtIndex:at.location - 1] != '.') {
  982. @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath);
  983. }
  984. NSRange trailingKeyRange = [keyPath rangeOfString:@"." options:0 range:{at.location, keyPath.length - at.location} locale:nil];
  985. *leadingKeyPath = [keyPath substringToIndex:at.location - 1];
  986. if (trailingKeyRange.location == NSNotFound) {
  987. *trailingKey = nil;
  988. return [keyPath substringFromIndex:at.location];
  989. } else {
  990. *trailingKey = [keyPath substringFromIndex:trailingKeyRange.location + 1];
  991. return [keyPath substringWithRange:{at.location, trailingKeyRange.location - at.location}];
  992. }
  993. }
  994. CollectionOperation QueryBuilder::collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath) {
  995. NSString *leadingKeyPath;
  996. NSString *trailingKey;
  997. NSString *collectionOperationName = get_collection_operation_name_from_key_path(keyPath, &leadingKeyPath, &trailingKey);
  998. ColumnReference linkColumn = column_reference_from_key_path(desc, leadingKeyPath, true);
  999. util::Optional<ColumnReference> column;
  1000. if (trailingKey) {
  1001. RLMPrecondition([trailingKey rangeOfString:@"."].location == NSNotFound, @"Invalid key path",
  1002. @"Right side of collection operator may only have a single level key");
  1003. NSString *fullKeyPath = [leadingKeyPath stringByAppendingFormat:@".%@", trailingKey];
  1004. column = column_reference_from_key_path(desc, fullKeyPath, true);
  1005. }
  1006. return {collectionOperationName, std::move(linkColumn), std::move(column)};
  1007. }
  1008. void QueryBuilder::apply_collection_operator_expression(RLMObjectSchema *desc,
  1009. NSString *keyPath, id value,
  1010. NSComparisonPredicate *pred) {
  1011. CollectionOperation operation = collection_operation_from_key_path(desc, keyPath);
  1012. operation.validate_comparison(value);
  1013. if (pred.leftExpression.expressionType == NSKeyPathExpressionType) {
  1014. add_collection_operation_constraint(pred.predicateOperatorType, operation, operation, value);
  1015. } else {
  1016. add_collection_operation_constraint(pred.predicateOperatorType, operation, value, operation);
  1017. }
  1018. }
  1019. void QueryBuilder::apply_value_expression(RLMObjectSchema *desc,
  1020. NSString *keyPath, id value,
  1021. NSComparisonPredicate *pred)
  1022. {
  1023. if (key_path_contains_collection_operator(keyPath)) {
  1024. apply_collection_operator_expression(desc, keyPath, value, pred);
  1025. return;
  1026. }
  1027. bool isAny = pred.comparisonPredicateModifier == NSAnyPredicateModifier;
  1028. ColumnReference column = column_reference_from_key_path(desc, keyPath, isAny);
  1029. // check to see if this is a between query
  1030. if (pred.predicateOperatorType == NSBetweenPredicateOperatorType) {
  1031. add_between_constraint(std::move(column), value);
  1032. return;
  1033. }
  1034. // turn "key.path IN collection" into ored together ==. "collection IN key.path" is handled elsewhere.
  1035. if (pred.predicateOperatorType == NSInPredicateOperatorType) {
  1036. process_or_group(m_query, value, [&](id item) {
  1037. id normalized = value_from_constant_expression_or_value(item);
  1038. validate_property_value(column, normalized,
  1039. @"Expected object of type %@ in IN clause for property '%@' on object of type '%@', but received: %@", desc, keyPath);
  1040. add_constraint(column.type(), NSEqualToPredicateOperatorType, pred.options, column, normalized);
  1041. });
  1042. return;
  1043. }
  1044. validate_property_value(column, value, @"Expected object of type %@ for property '%@' on object of type '%@', but received: %@", desc, keyPath);
  1045. if (pred.leftExpression.expressionType == NSKeyPathExpressionType) {
  1046. add_constraint(column.type(), pred.predicateOperatorType, pred.options, std::move(column), value);
  1047. } else {
  1048. add_constraint(column.type(), pred.predicateOperatorType, pred.options, value, std::move(column));
  1049. }
  1050. }
  1051. void QueryBuilder::apply_column_expression(RLMObjectSchema *desc,
  1052. NSString *leftKeyPath, NSString *rightKeyPath,
  1053. NSComparisonPredicate *predicate)
  1054. {
  1055. bool left_key_path_contains_collection_operator = key_path_contains_collection_operator(leftKeyPath);
  1056. bool right_key_path_contains_collection_operator = key_path_contains_collection_operator(rightKeyPath);
  1057. if (left_key_path_contains_collection_operator && right_key_path_contains_collection_operator) {
  1058. @throw RLMPredicateException(@"Unsupported predicate", @"Key paths including aggregate operations cannot be compared with other aggregate operations.");
  1059. }
  1060. if (left_key_path_contains_collection_operator) {
  1061. CollectionOperation left = collection_operation_from_key_path(desc, leftKeyPath);
  1062. ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, false);
  1063. left.validate_comparison(right);
  1064. add_collection_operation_constraint(predicate.predicateOperatorType, left, left, std::move(right));
  1065. return;
  1066. }
  1067. if (right_key_path_contains_collection_operator) {
  1068. ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, false);
  1069. CollectionOperation right = collection_operation_from_key_path(desc, rightKeyPath);
  1070. right.validate_comparison(left);
  1071. add_collection_operation_constraint(predicate.predicateOperatorType, right, std::move(left), right);
  1072. return;
  1073. }
  1074. bool isAny = false;
  1075. ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, isAny);
  1076. ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, isAny);
  1077. // NOTE: It's assumed that column type must match and no automatic type conversion is supported.
  1078. RLMPrecondition(left.type() == right.type(),
  1079. RLMPropertiesComparisonTypeMismatchException,
  1080. RLMPropertiesComparisonTypeMismatchReason,
  1081. RLMTypeToString(left.type()),
  1082. RLMTypeToString(right.type()));
  1083. // TODO: Should we handle special case where left row is the same as right row (tautology)
  1084. add_constraint(left.type(), predicate.predicateOperatorType, predicate.options,
  1085. std::move(left), std::move(right));
  1086. }
  1087. // Identify expressions of the form [SELF valueForKeyPath:]
  1088. bool is_self_value_for_key_path_function_expression(NSExpression *expression)
  1089. {
  1090. if (expression.expressionType != NSFunctionExpressionType)
  1091. return false;
  1092. if (expression.operand.expressionType != NSEvaluatedObjectExpressionType)
  1093. return false;
  1094. return [expression.function isEqualToString:@"valueForKeyPath:"];
  1095. }
  1096. // -[NSPredicate predicateWithSubtitutionVariables:] results in function expressions of the form [SELF valueForKeyPath:]
  1097. // that apply_predicate cannot handle. Replace such expressions with equivalent NSKeyPathExpressionType expressions.
  1098. NSExpression *simplify_self_value_for_key_path_function_expression(NSExpression *expression) {
  1099. if (is_self_value_for_key_path_function_expression(expression)) {
  1100. if (NSString *keyPath = [expression.arguments.firstObject keyPath]) {
  1101. return [NSExpression expressionForKeyPath:keyPath];
  1102. }
  1103. }
  1104. return expression;
  1105. }
  1106. void QueryBuilder::apply_subquery_count_expression(RLMObjectSchema *objectSchema,
  1107. NSExpression *subqueryExpression, NSPredicateOperatorType operatorType, NSExpression *right) {
  1108. if (right.expressionType != NSConstantValueExpressionType || ![right.constantValue isKindOfClass:[NSNumber class]]) {
  1109. @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY(…).@count is only supported when compared with a constant number.");
  1110. }
  1111. int64_t value = [right.constantValue integerValue];
  1112. ColumnReference collectionColumn = column_reference_from_key_path(objectSchema, [subqueryExpression.collection keyPath], true);
  1113. RLMObjectSchema *collectionMemberObjectSchema = m_schema[collectionColumn.property().objectClassName];
  1114. // Eliminate references to the iteration variable in the subquery.
  1115. NSPredicate *subqueryPredicate = [subqueryExpression.predicate predicateWithSubstitutionVariables:@{ subqueryExpression.variable : [NSExpression expressionForEvaluatedObject] }];
  1116. subqueryPredicate = transformPredicate(subqueryPredicate, simplify_self_value_for_key_path_function_expression);
  1117. Query subquery = RLMPredicateToQuery(subqueryPredicate, collectionMemberObjectSchema, m_schema, m_group);
  1118. add_numeric_constraint(RLMPropertyTypeInt, operatorType,
  1119. collectionColumn.resolve<Link>(std::move(subquery)).count(), value);
  1120. }
  1121. void QueryBuilder::apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  1122. NSPredicateOperatorType operatorType, NSExpression *right) {
  1123. if (![functionExpression.function isEqualToString:@"valueForKeyPath:"] || functionExpression.arguments.count != 1) {
  1124. @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported on the result of a SUBQUERY.", functionExpression.function);
  1125. }
  1126. NSExpression *keyPathExpression = functionExpression.arguments.firstObject;
  1127. if ([keyPathExpression.keyPath isEqualToString:@"@count"]) {
  1128. apply_subquery_count_expression(objectSchema, functionExpression.operand, operatorType, right);
  1129. } else {
  1130. @throw RLMPredicateException(@"Invalid predicate", @"SUBQUERY is only supported when immediately followed by .@count that is compared with a constant number.");
  1131. }
  1132. }
  1133. void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression,
  1134. NSPredicateOperatorType operatorType, NSExpression *right) {
  1135. if (functionExpression.operand.expressionType == NSSubqueryExpressionType) {
  1136. apply_function_subquery_expression(objectSchema, functionExpression, operatorType, right);
  1137. } else {
  1138. @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported.", functionExpression.function);
  1139. }
  1140. }
  1141. void QueryBuilder::apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema)
  1142. {
  1143. // Compound predicates.
  1144. if ([predicate isMemberOfClass:[NSCompoundPredicate class]]) {
  1145. NSCompoundPredicate *comp = (NSCompoundPredicate *)predicate;
  1146. switch ([comp compoundPredicateType]) {
  1147. case NSAndPredicateType:
  1148. if (comp.subpredicates.count) {
  1149. // Add all of the subpredicates.
  1150. m_query.group();
  1151. for (NSPredicate *subp in comp.subpredicates) {
  1152. apply_predicate(subp, objectSchema);
  1153. }
  1154. m_query.end_group();
  1155. } else {
  1156. // NSCompoundPredicate's documentation states that an AND predicate with no subpredicates evaluates to TRUE.
  1157. m_query.and_query(std::unique_ptr<Expression>(new TrueExpression));
  1158. }
  1159. break;
  1160. case NSOrPredicateType: {
  1161. // Add all of the subpredicates with ors inbetween.
  1162. process_or_group(m_query, comp.subpredicates, [&](__unsafe_unretained NSPredicate *const subp) {
  1163. apply_predicate(subp, objectSchema);
  1164. });
  1165. break;
  1166. }
  1167. case NSNotPredicateType:
  1168. // Add the negated subpredicate
  1169. m_query.Not();
  1170. apply_predicate(comp.subpredicates.firstObject, objectSchema);
  1171. break;
  1172. default:
  1173. @throw RLMPredicateException(@"Invalid compound predicate type",
  1174. @"Only support AND, OR and NOT predicate types");
  1175. }
  1176. }
  1177. else if ([predicate isMemberOfClass:[NSComparisonPredicate class]]) {
  1178. NSComparisonPredicate *compp = (NSComparisonPredicate *)predicate;
  1179. // check modifier
  1180. RLMPrecondition(compp.comparisonPredicateModifier != NSAllPredicateModifier,
  1181. @"Invalid predicate", @"ALL modifier not supported");
  1182. NSExpressionType exp1Type = compp.leftExpression.expressionType;
  1183. NSExpressionType exp2Type = compp.rightExpression.expressionType;
  1184. if (compp.comparisonPredicateModifier == NSAnyPredicateModifier) {
  1185. // for ANY queries
  1186. RLMPrecondition(exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType,
  1187. @"Invalid predicate",
  1188. @"Predicate with ANY modifier must compare a KeyPath with RLMArray with a value");
  1189. }
  1190. if (compp.predicateOperatorType == NSBetweenPredicateOperatorType || compp.predicateOperatorType == NSInPredicateOperatorType) {
  1191. // Inserting an array via %@ gives NSConstantValueExpressionType, but including it directly gives NSAggregateExpressionType
  1192. if (exp1Type == NSKeyPathExpressionType && (exp2Type == NSAggregateExpressionType || exp2Type == NSConstantValueExpressionType)) {
  1193. // "key.path IN %@", "key.path IN {…}", "key.path BETWEEN %@", or "key.path BETWEEN {…}".
  1194. exp2Type = NSConstantValueExpressionType;
  1195. }
  1196. else if (compp.predicateOperatorType == NSInPredicateOperatorType && exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) {
  1197. // "%@ IN key.path" is equivalent to "ANY key.path IN %@". Rewrite the former into the latter.
  1198. compp = [NSComparisonPredicate predicateWithLeftExpression:compp.rightExpression rightExpression:compp.leftExpression
  1199. modifier:NSAnyPredicateModifier type:NSEqualToPredicateOperatorType options:0];
  1200. exp1Type = NSKeyPathExpressionType;
  1201. exp2Type = NSConstantValueExpressionType;
  1202. }
  1203. else {
  1204. if (compp.predicateOperatorType == NSBetweenPredicateOperatorType) {
  1205. @throw RLMPredicateException(@"Invalid predicate",
  1206. @"Predicate with BETWEEN operator must compare a KeyPath with an aggregate with two values");
  1207. }
  1208. else if (compp.predicateOperatorType == NSInPredicateOperatorType) {
  1209. @throw RLMPredicateException(@"Invalid predicate",
  1210. @"Predicate with IN operator must compare a KeyPath with an aggregate");
  1211. }
  1212. }
  1213. }
  1214. if (exp1Type == NSKeyPathExpressionType && exp2Type == NSKeyPathExpressionType) {
  1215. // both expression are KeyPaths
  1216. apply_column_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.keyPath, compp);
  1217. }
  1218. else if (exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType) {
  1219. // comparing keypath to value
  1220. apply_value_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.constantValue, compp);
  1221. }
  1222. else if (exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) {
  1223. // comparing value to keypath
  1224. apply_value_expression(objectSchema, compp.rightExpression.keyPath, compp.leftExpression.constantValue, compp);
  1225. }
  1226. else if (exp1Type == NSFunctionExpressionType) {
  1227. apply_function_expression(objectSchema, compp.leftExpression, compp.predicateOperatorType, compp.rightExpression);
  1228. }
  1229. else if (exp1Type == NSSubqueryExpressionType) {
  1230. // The subquery expressions that we support are handled by the NSFunctionExpressionType case above.
  1231. @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY is only supported when immediately followed by .@count.");
  1232. }
  1233. else {
  1234. @throw RLMPredicateException(@"Invalid predicate expressions",
  1235. @"Predicate expressions must compare a keypath and another keypath or a constant value");
  1236. }
  1237. }
  1238. else if ([predicate isEqual:[NSPredicate predicateWithValue:YES]]) {
  1239. m_query.and_query(std::unique_ptr<Expression>(new TrueExpression));
  1240. } else if ([predicate isEqual:[NSPredicate predicateWithValue:NO]]) {
  1241. m_query.and_query(std::unique_ptr<Expression>(new FalseExpression));
  1242. }
  1243. else {
  1244. // invalid predicate type
  1245. @throw RLMPredicateException(@"Invalid predicate",
  1246. @"Only support compound, comparison, and constant predicates");
  1247. }
  1248. }
  1249. } // namespace
  1250. realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema,
  1251. RLMSchema *schema, Group &group)
  1252. {
  1253. auto query = get_table(group, objectSchema).where();
  1254. // passing a nil predicate is a no-op
  1255. if (!predicate) {
  1256. return query;
  1257. }
  1258. @autoreleasepool {
  1259. QueryBuilder(query, group, schema).apply_predicate(predicate, objectSchema);
  1260. }
  1261. // Test the constructed query in core
  1262. std::string validateMessage = query.validate();
  1263. RLMPrecondition(validateMessage.empty(), @"Invalid query", @"%.*s",
  1264. (int)validateMessage.size(), validateMessage.c_str());
  1265. return query;
  1266. }