• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.2
/test/test_query.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 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

19
#include "testsettings.hpp"
20
#ifdef TEST_QUERY
21

22
#include <cstdlib> // itoa()
23
#include <limits>
24
#include <vector>
25
#include <chrono>
26

27
using namespace std::chrono;
28

29
#include <realm.hpp>
30
#include <realm/column_integer.hpp>
31
#include <realm/array_bool.hpp>
32
#include <realm/query_expression.hpp>
33
#include <realm/index_string.hpp>
34
#include <realm/query_expression.hpp>
35
#include "test.hpp"
36
#include "test_table_helper.hpp"
37
#include "test_types_helper.hpp"
38

39
using namespace realm;
40
using namespace realm::util;
41
using namespace realm::test_util;
42

43
// #include <valgrind/callgrind.h>
44

45
#ifndef CALLGRIND_START_INSTRUMENTATION
46
#define CALLGRIND_START_INSTRUMENTATION
47
#endif
48

49
#ifndef CALLGRIND_STOP_INSTRUMENTATION
50
#define CALLGRIND_STOP_INSTRUMENTATION
51
#endif
52

53
// valgrind --tool=callgrind --instr-atstart=no realm-tests
54

55
// Test independence and thread-safety
56
// -----------------------------------
57
//
58
// All tests must be thread safe and independent of each other. This
59
// is required because it allows for both shuffling of the execution
60
// order and for parallelized testing.
61
//
62
// In particular, avoid using std::rand() since it is not guaranteed
63
// to be thread safe. Instead use the API offered in
64
// `test/util/random.hpp`.
65
//
66
// All files created in tests must use the TEST_PATH macro (or one of
67
// its friends) to obtain a suitable file system path. See
68
// `test/util/test_path.hpp`.
69
//
70
//
71
// Debugging and the ONLY() macro
72
// ------------------------------
73
//
74
// A simple way of disabling all tests except one called `Foo`, is to
75
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
76
// test suite. Note that you can also use filtering by setting the
77
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
78
// this.
79
//
80
// Another way to debug a particular test, is to copy that test into
81
// `experiments/testcase.cpp` and then run `sh build.sh
82
// check-testcase` (or one of its friends) from the command line.
83

84
TEST(Query_NoConditions)
85
{
1✔
86
    Table table;
1✔
87
    table.add_column(type_Int, "i");
1✔
88
    {
1✔
89
        Query query(table.where());
1✔
90
        CHECK_EQUAL(null_key, query.find());
1✔
91
    }
1✔
92
    {
1✔
93
        Query query = table.where();
1✔
94
        CHECK_EQUAL(null_key, query.find());
1✔
95
    }
1✔
96
    table.create_object(ObjKey(5));
1✔
97
    {
1✔
98
        Query query(table.where());
1✔
99
        CHECK_EQUAL(5, query.find().value);
1✔
100
    }
1✔
101
    {
1✔
102
        Query query = table.where();
1✔
103
        CHECK_EQUAL(5, query.find().value);
1✔
104
    }
1✔
105
}
1✔
106

107

108
TEST(Query_Count)
109
{
1✔
110
    // Intended to test QueryState::match<pattern = true>(); which is only triggered if:
111
    // * Table size is large enough to have SSE-aligned or bithack-aligned rows (this requires
112
    //   REALM_MAX_BPNODE_SIZE > [some large number]!)
113
    // * You're doing a 'count' which is currently the only operation that uses 'pattern', and
114
    // * There exists exactly 1 condition (if there is 0 conditions, it will fallback to column::count
115
    //   and if there exists > 1 conditions, 'pattern' is currently not supported - but could easily be
116
    //   extended to support it)
117

118
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
119
    for (int j = 0; j < 100; j++) {
101✔
120
        Table table;
100✔
121
        auto col_ndx = table.add_column(type_Int, "i");
100✔
122

123
        size_t matching = 0;
100✔
124
        size_t not_matching = 0;
100✔
125
        size_t rows = random.draw_int_mod(5 * REALM_MAX_BPNODE_SIZE); // to cross some leaf boundaries
100✔
126

127
        for (size_t i = 0; i < rows; ++i) {
245,361✔
128
            int64_t val = random.draw_int_mod(5);
245,261✔
129
            table.create_object().set(col_ndx, val);
245,261✔
130
            if (val == 2)
245,261✔
131
                matching++;
48,802✔
132
            else
196,459✔
133
                not_matching++;
196,459✔
134
        }
245,261✔
135

136
        CHECK_EQUAL(matching, table.where().equal(col_ndx, 2).count());
100✔
137
        CHECK_EQUAL(not_matching, table.where().not_equal(col_ndx, 2).count());
100✔
138
    }
100✔
139
}
1✔
140

141
TEST(Query_Parser)
142
{
1✔
143
    Table books;
1✔
144
    books.add_column(type_String, "title");
1✔
145
    books.add_column(type_String, "author");
1✔
146
    books.add_column(type_Int, "pages");
1✔
147

148
    books.create_object().set_all("Computer Architecture and Organization", "B. Govindarajalu", 752);
1✔
149
    Obj obj2 = books.create_object().set_all("Introduction to Quantum Mechanics", "David Griffiths", 480);
1✔
150
    books.create_object().set_all("Biophysics: Searching for Principles", "William Bialek", 640);
1✔
151

152
    // Typed table:
153
    Query q = books.query("pages >= $0 && author == $1", std::vector<Mixed>{200, "David Griffiths"});
1✔
154
    auto match = q.find();
1✔
155
    CHECK_EQUAL(obj2.get_key(), match);
1✔
156
    // You don't need to create a query object first:
157
    match = books.query("pages >= 200 && author == \"David Griffiths\"").find();
1✔
158
    CHECK_EQUAL(obj2.get_key(), match);
1✔
159

160
    // Check that we handle unicode strings correctly
161
    std::string query_string = "author == \"جمعتs\"";
1✔
162
    q = books.query(query_string);
1✔
163
    CHECK_EQUAL(q.get_description(), query_string);
1✔
164
}
1✔
165

166

167
TEST(Query_NextGenSyntax)
168
{
1✔
169
    ObjKey match;
1✔
170

171
    // Setup untyped table
172
    Table untyped;
1✔
173
    auto c0 = untyped.add_column(type_Int, "firs1");
1✔
174
    auto c1 = untyped.add_column(type_Float, "second");
1✔
175
    untyped.add_column(type_Double, "third");
1✔
176
    auto c3 = untyped.add_column(type_Bool, "third2");
1✔
177
    auto c4 = untyped.add_column(type_String, "fourth");
1✔
178
    ObjKey k0 = untyped.create_object().set_all(20, 19.9f, 3.0, true, "hello").get_key();
1✔
179
    ObjKey k1 = untyped.create_object().set_all(20, 20.1f, 4.0, false, "world").get_key();
1✔
180

181
    match = (untyped.column<String>(c4) == "world").find();
1✔
182
    CHECK_EQUAL(match, k1);
1✔
183

184
    match = ("world" == untyped.column<String>(c4)).find();
1✔
185
    CHECK_EQUAL(match, k1);
1✔
186

187
    match = ("hello" != untyped.column<String>(c4)).find();
1✔
188
    CHECK_EQUAL(match, k1);
1✔
189

190
    match = (!("hello" == untyped.column<String>(c4))).find();
1✔
191
    CHECK_EQUAL(match, k1);
1✔
192

193
    match = (untyped.column<String>(c4) != StringData("hello")).find();
1✔
194
    CHECK_EQUAL(match, k1);
1✔
195

196
    match = (!(untyped.column<String>(c4) == StringData("hello"))).find();
1✔
197
    CHECK_EQUAL(match, k1);
1✔
198

199
    match = (!(!(untyped.column<String>(c4) != StringData("hello")))).find();
1✔
200
    CHECK_EQUAL(match, k1);
1✔
201

202

203
    // This is a demonstration of fallback to old query_engine for the specific cases where it's possible
204
    // because old engine is faster. This will return a ->less(...) query
205
    match = (untyped.column<int64_t>(c0) == untyped.column<int64_t>(c0)).find();
1✔
206
    CHECK_EQUAL(match, k0);
1✔
207

208

209
    match = (untyped.column<bool>(c3) == false).find();
1✔
210
    CHECK_EQUAL(match, k1);
1✔
211
    match = (false == untyped.column<bool>(c3)).find();
1✔
212
    CHECK_EQUAL(match, k1);
1✔
213

214
    match = untyped.query("10 + 10.3 > third + 2").find();
1✔
215
    CHECK_EQUAL(match, k0);
1✔
216

217

218
    match = (untyped.column<int64_t>(c0) > untyped.column<int64_t>(c0)).find();
1✔
219
    CHECK_EQUAL(match, realm::null_key);
1✔
220

221
    // Left condition makes first row non-match
222
    match = untyped.query("second + 1 > 21 && third > 1 + 1").find();
1✔
223
    CHECK_EQUAL(match, k1);
1✔
224

225
    // Right condition makes first row a non-match
226
    match = untyped.query("second > 10 && third > 1.5 + 1 * 2").find();
1✔
227
    CHECK_EQUAL(match, k1);
1✔
228

229
    // Both make first row match
230
    match = untyped.query("second < 20 && third > 2").find();
1✔
231
    CHECK_EQUAL(match, k0);
1✔
232

233
    // Both make first row non-match
234
    match = untyped.query("second > 20 && third > 3.5").find();
1✔
235
    CHECK_EQUAL(match, k1);
1✔
236

237
    // Left cond match 0, right match 1
238
    match = untyped.query("second < 20 && third > 3.5").find();
1✔
239
    CHECK_EQUAL(match, realm::null_key);
1✔
240

241
    // Left match 1, right match 0
242
    match = untyped.query("second > 20 && third < 3.5").find();
1✔
243
    CHECK_EQUAL(match, realm::null_key);
1✔
244

245
    // Untyped ||
246

247
    // Left match 0
248
    match = untyped.query("second < 20 || third < 3.5").find();
1✔
249
    CHECK_EQUAL(match, k0);
1✔
250

251
    // Right match 0
252
    match = untyped.query("second > 20 || third < 3.5").find();
1✔
253
    CHECK_EQUAL(match, k0);
1✔
254

255
    // Left match 1
256

257
    match = untyped.query("second > 20 || third > 9.5").find();
1✔
258

259
    CHECK_EQUAL(match, k1);
1✔
260

261
    Query q4 = untyped.query("second + firs1 > 40");
1✔
262

263

264
    Query q5 = untyped.query("20 < second");
1✔
265

266
    match = q4.and_query(q5).find();
1✔
267
    CHECK_EQUAL(match, k1);
1✔
268

269

270
    // Untyped, direct column addressing
271
    Value<int64_t> uv1(1);
1✔
272

273
    Columns<float> uc1 = untyped.column<float>(c1);
1✔
274

275
    Query q2 = uv1 <= uc1;
1✔
276
    match = q2.find();
1✔
277
    CHECK_EQUAL(match, k0);
1✔
278

279

280
    Query q0 = uv1 <= uc1;
1✔
281
    match = q0.find();
1✔
282
    CHECK_EQUAL(match, k0);
1✔
283

284
    Query q99 = uv1 <= untyped.column<float>(c1);
1✔
285
    match = q99.find();
1✔
286
    CHECK_EQUAL(match, k0);
1✔
287

288
    Query q8 = untyped.query("1 > second + 5");
1✔
289
    match = q8.find();
1✔
290
    CHECK_EQUAL(match, null_key);
1✔
291

292
    Query q3 = untyped.query("second + firs1 > 10 + firs1");
1✔
293
    match = q3.find();
1✔
294

295
    match = q2.find();
1✔
296
    CHECK_EQUAL(match, k0);
1✔
297

298
    match = untyped.query("firs1 + second > 40").find();
1✔
299
    CHECK_EQUAL(match, k1);
1✔
300

301
    match = untyped.query("firs1 + second < 40").find();
1✔
302
    CHECK_EQUAL(match, k0);
1✔
303

304
    match = untyped.query("second <= firs1").find();
1✔
305
    CHECK_EQUAL(match, k0);
1✔
306

307
    match = untyped.query("firs1 + second >= firs1 + second").find();
1✔
308
    CHECK_EQUAL(match, k0);
1✔
309

310
    match = untyped.query("firs1 + second > 40").find();
1✔
311
    CHECK_EQUAL(match, k1);
1✔
312
}
1✔
313

314
/*
315
This tests the new string conditions now available for the expression syntax.
316

317
Null behaviour (+ means concatenation):
318

319
If A + B == B, then A is a prefix of B, and B is a suffix of A. This is valid for any A and B, including null and
320
empty strings. Some examples:
321

322
1)    "" both begins with null and ends with null and contains null.
323
2)    "foobar" begins with null, ends with null and contains null.
324
3)    "foobar" begins with "", ends with "" and contains ""
325
4)    null does not contain, begin with, or end with ""
326
5)    null contains null, begins with null and ends with null
327

328
See TEST(StringData_Substrings) for more unit tests for null, isolated to using only StringData class with no
329
columns or queries involved
330
*/
331

332

333
TEST(Query_NextGen_StringConditions)
334
{
1✔
335
    Group group;
1✔
336
    TableRef table1 = group.add_table("table1");
1✔
337
    auto col_str1 = table1->add_column(type_String, "str1");
1✔
338
    auto col_str2 = table1->add_column(type_String, "str2");
1✔
339

340
    // add some rows
341
    ObjKey key_1_0 = table1->create_object().set_all("foo", "F").get_key();
1✔
342
    table1->create_object().set_all("!", "x").get_key();
1✔
343
    ObjKey key_1_2 = table1->create_object().set_all("bar", "r").get_key();
1✔
344

345
    ObjKey m;
1✔
346
    // Equal
347
    m = table1->column<String>(col_str1).equal("bar", false).find();
1✔
348
    CHECK_EQUAL(m, key_1_2);
1✔
349

350
    m = table1->column<String>(col_str1).equal("bar", true).find();
1✔
351
    CHECK_EQUAL(m, key_1_2);
1✔
352

353
    m = table1->column<String>(col_str1).equal("Bar", true).find();
1✔
354
    CHECK_EQUAL(m, null_key);
1✔
355

356
    m = table1->column<String>(col_str1).equal("Bar", false).find();
1✔
357
    CHECK_EQUAL(m, key_1_2);
1✔
358

359
    // Contains
360
    m = table1->column<String>(col_str1).contains("a", false).find();
1✔
361
    CHECK_EQUAL(m, key_1_2);
1✔
362

363
    m = table1->column<String>(col_str1).contains("a", true).find();
1✔
364
    CHECK_EQUAL(m, key_1_2);
1✔
365

366
    m = table1->column<String>(col_str1).contains("A", true).find();
1✔
367
    CHECK_EQUAL(m, null_key);
1✔
368

369
    m = table1->column<String>(col_str1).contains("A", false).find();
1✔
370
    CHECK_EQUAL(m, key_1_2);
1✔
371

372
    m = table1->column<String>(col_str1).contains(table1->column<String>(col_str2), false).find();
1✔
373
    CHECK_EQUAL(m, key_1_0);
1✔
374

375
    m = table1->column<String>(col_str1).contains(table1->column<String>(col_str2), true).find();
1✔
376
    CHECK_EQUAL(m, key_1_2);
1✔
377

378
    // Begins with
379
    m = table1->column<String>(col_str1).begins_with("b", false).find();
1✔
380
    CHECK_EQUAL(m, key_1_2);
1✔
381

382
    m = table1->column<String>(col_str1).begins_with("b", true).find();
1✔
383
    CHECK_EQUAL(m, key_1_2);
1✔
384

385
    m = table1->column<String>(col_str1).begins_with("B", true).find();
1✔
386
    CHECK_EQUAL(m, null_key);
1✔
387

388
    m = table1->column<String>(col_str1).begins_with("B", false).find();
1✔
389
    CHECK_EQUAL(m, key_1_2);
1✔
390

391
    m = table1->column<String>(col_str1).begins_with(table1->column<String>(col_str2), false).find();
1✔
392
    CHECK_EQUAL(m, key_1_0);
1✔
393

394
    m = table1->column<String>(col_str1).begins_with(table1->column<String>(col_str2), true).find();
1✔
395
    CHECK_EQUAL(m, null_key);
1✔
396

397
    // Ends with
398
    m = table1->column<String>(col_str1).ends_with("r", false).find();
1✔
399
    CHECK_EQUAL(m, key_1_2);
1✔
400

401
    m = table1->column<String>(col_str1).ends_with("r", true).find();
1✔
402
    CHECK_EQUAL(m, key_1_2);
1✔
403

404
    m = table1->column<String>(col_str1).ends_with("R", true).find();
1✔
405
    CHECK_EQUAL(m, null_key);
1✔
406

407
    m = table1->column<String>(col_str1).ends_with("R", false).find();
1✔
408
    CHECK_EQUAL(m, key_1_2);
1✔
409

410
    m = table1->column<String>(col_str1).ends_with(table1->column<String>(col_str2), false).find();
1✔
411
    CHECK_EQUAL(m, key_1_2);
1✔
412

413
    m = table1->column<String>(col_str1).ends_with(table1->column<String>(col_str2), true).find();
1✔
414
    CHECK_EQUAL(m, key_1_2);
1✔
415

416
    // Like (wildcard matching)
417
    m = table1->column<String>(col_str1).like("b*", true).find();
1✔
418
    CHECK_EQUAL(m, key_1_2);
1✔
419

420
    m = table1->column<String>(col_str1).like("b*", false).find();
1✔
421
    CHECK_EQUAL(m, key_1_2);
1✔
422

423
    m = table1->column<String>(col_str1).like("*r", false).find();
1✔
424
    CHECK_EQUAL(m, key_1_2);
1✔
425

426
    m = table1->column<String>(col_str1).like("f?o", false).find();
1✔
427
    CHECK_EQUAL(m, key_1_0);
1✔
428

429
    m = (table1->column<String>(col_str1).like("f*", false) && table1->column<String>(col_str1) == "foo").find();
1✔
430
    CHECK_EQUAL(m, key_1_0);
1✔
431

432
    m = table1->column<String>(col_str1).like(table1->column<String>(col_str2), true).find();
1✔
433
    CHECK_EQUAL(m, null_key);
1✔
434

435
    // Test various compare operations with null
436
    TableRef table2 = group.add_table("table2");
1✔
437
    auto col_str3 = table2->add_column(type_String, "str3", true);
1✔
438

439
    ObjKey key_2_0 = table2->create_object().set(col_str3, "foo").get_key();
1✔
440
    ObjKey key_2_1 = table2->create_object().set(col_str3, "!").get_key();
1✔
441
    ObjKey key_2_2 = table2->create_object().get_key(); // null
1✔
442
    ObjKey key_2_3 = table2->create_object().set(col_str3, "bar").get_key();
1✔
443
    ObjKey key_2_4 = table2->create_object().set(col_str3, "").get_key();
1✔
444

445
    size_t cnt;
1✔
446
    cnt = table2->column<String>(col_str3).contains(StringData("")).count();
1✔
447
    CHECK_EQUAL(cnt, 4);
1✔
448

449
    cnt = table2->column<String>(col_str3).begins_with(StringData("")).count();
1✔
450
    CHECK_EQUAL(cnt, 4);
1✔
451

452
    cnt = table2->column<String>(col_str3).ends_with(StringData("")).count();
1✔
453
    CHECK_EQUAL(cnt, 4);
1✔
454

455
    cnt = table2->column<String>(col_str3).equal(StringData("")).count();
1✔
456
    CHECK_EQUAL(cnt, 1);
1✔
457

458
    cnt = table2->column<String>(col_str3).not_equal(StringData("")).count();
1✔
459
    CHECK_EQUAL(cnt, 4);
1✔
460

461
    cnt = table2->column<String>(col_str3).equal(realm::null()).count();
1✔
462
    CHECK_EQUAL(cnt, 1);
1✔
463

464
    cnt = table2->column<String>(col_str3).not_equal(realm::null()).count();
1✔
465
    CHECK_EQUAL(cnt, 4);
1✔
466

467
    cnt = table2->column<String>(col_str3).contains(realm::null()).count();
1✔
468
    CHECK_EQUAL(cnt, 5);
1✔
469

470
    cnt = table2->column<String>(col_str3).like(realm::null()).count();
1✔
471
    CHECK_EQUAL(cnt, 1);
1✔
472

473
    cnt = table2->column<String>(col_str3).contains(StringData(""), false).count();
1✔
474
    CHECK_EQUAL(cnt, 4);
1✔
475

476
    cnt = table2->column<String>(col_str3).like(StringData(""), false).count();
1✔
477
    CHECK_EQUAL(cnt, 1);
1✔
478

479
    cnt = table2->column<String>(col_str3).begins_with(StringData(""), false).count();
1✔
480
    CHECK_EQUAL(cnt, 4);
1✔
481

482
    cnt = table2->column<String>(col_str3).ends_with(StringData(""), false).count();
1✔
483
    CHECK_EQUAL(cnt, 4);
1✔
484

485
    cnt = table2->column<String>(col_str3).equal(StringData(""), false).count();
1✔
486
    CHECK_EQUAL(cnt, 1);
1✔
487

488
    cnt = table2->column<String>(col_str3).not_equal(StringData(""), false).count();
1✔
489
    CHECK_EQUAL(cnt, 4);
1✔
490

491
    cnt = table2->column<String>(col_str3).equal(realm::null(), false).count();
1✔
492
    CHECK_EQUAL(cnt, 1);
1✔
493

494
    cnt = table2->column<String>(col_str3).not_equal(realm::null(), false).count();
1✔
495
    CHECK_EQUAL(cnt, 4);
1✔
496

497
    cnt = table2->column<String>(col_str3).contains(realm::null(), false).count();
1✔
498
    CHECK_EQUAL(cnt, 5);
1✔
499

500
    cnt = table2->column<String>(col_str3).like(realm::null(), false).count();
1✔
501
    CHECK_EQUAL(cnt, 1);
1✔
502

503
    auto check_results = [&](Query q, std::vector<StringData>&& matches) {
23✔
504
        TableView view = q.find_all();
23✔
505
        std::sort(matches.begin(), matches.end());
23✔
506
        std::vector<StringData> actual;
23✔
507
        for (size_t i = 0; i < view.size(); ++i) {
83✔
508
            actual.push_back(view.get_object(i).get<StringData>(col_str3));
60✔
509
        }
60✔
510
        std::sort(actual.begin(), actual.end());
23✔
511
        if (!CHECK_EQUAL(actual, matches)) {
23✔
512
            util::format(std::cout, "failed query '%1'\n", q.get_description());
×
513
        }
×
514
        TableView parsed_results = table2->query(q.get_description()).find_all();
23✔
515
        std::vector<StringData> parsed_matches;
23✔
516
        for (size_t i = 0; i < parsed_results.size(); ++i) {
83✔
517
            parsed_matches.push_back(parsed_results.get_object(i).get<StringData>(col_str3));
60✔
518
        }
60✔
519
        std::sort(parsed_matches.begin(), parsed_matches.end());
23✔
520
        if (!CHECK_EQUAL(parsed_matches, matches)) {
23✔
521
            util::format(std::cout, "failed parsed query '%1'\n", q.get_description());
×
522
        }
×
523
    };
23✔
524

525
    // greater
526
    check_results((table2->column<String>(col_str3) > StringData("")), {"foo", "bar", "!"});
1✔
527
    check_results((table2->column<String>(col_str3) > StringData("b")), {"foo", "bar"});
1✔
528
    check_results((table2->column<String>(col_str3) > StringData("bar")), {"foo"});
1✔
529
    check_results((table2->column<String>(col_str3) > StringData("barrr")), {"foo"});
1✔
530
    check_results((table2->column<String>(col_str3) > StringData("bb")), {"foo"});
1✔
531
    check_results((table2->column<String>(col_str3) > StringData("z")), {});
1✔
532

533
    // less
534
    check_results((table2->column<String>(col_str3) < StringData("")), {StringData()});
1✔
535
    check_results((table2->column<String>(col_str3) < StringData("b")), {"", "!", StringData()});
1✔
536
    check_results((table2->column<String>(col_str3) < StringData("bar")), {"", "!", StringData()});
1✔
537
    check_results((table2->column<String>(col_str3) < StringData("barrr")), {"bar", "", "!", StringData()});
1✔
538
    check_results((table2->column<String>(col_str3) < StringData("z")), {"foo", "bar", "", "!", StringData()});
1✔
539
    check_results((table2->column<String>(col_str3) < StringData("f")), {"bar", "", "!", StringData()});
1✔
540
    check_results((table2->column<String>(col_str3) < StringData("fp")), {"foo", "bar", "", "!", StringData()});
1✔
541

542
    // greater equal
543
    check_results((table2->column<String>(col_str3) >= StringData("")), {"foo", "bar", "!", ""});
1✔
544
    check_results((table2->column<String>(col_str3) >= StringData("b")), {"foo", "bar"});
1✔
545
    check_results((table2->column<String>(col_str3) >= StringData("bar")), {"foo", "bar"});
1✔
546
    check_results((table2->column<String>(col_str3) >= StringData("barrrr")), {"foo"});
1✔
547
    check_results((table2->column<String>(col_str3) >= StringData("z")), {});
1✔
548

549
    // less equal
550
    check_results((table2->column<String>(col_str3) <= StringData("")), {StringData(), ""});
1✔
551
    check_results((table2->column<String>(col_str3) <= StringData("b")), {"", "!", StringData()});
1✔
552
    check_results((table2->column<String>(col_str3) <= StringData("bar")), {"bar", "", "!", StringData()});
1✔
553
    check_results((table2->column<String>(col_str3) <= StringData("barrrr")), {"bar", "", "!", StringData()});
1✔
554
    check_results((table2->column<String>(col_str3) <= StringData("z")), {"foo", "bar", "", "!", StringData()});
1✔
555

556
    TableRef table3 = group.add_table(StringData("table3"));
1✔
557
    auto col_link1 = table3->add_column(*table2, "link1");
1✔
558

559
    table3->create_object().set(col_link1, key_2_0);
1✔
560
    table3->create_object().set(col_link1, key_2_1);
1✔
561
    table3->create_object().set(col_link1, key_2_2);
1✔
562
    table3->create_object().set(col_link1, key_2_3);
1✔
563
    table3->create_object().set(col_link1, key_2_4);
1✔
564

565
    cnt = table3->link(col_link1).column<String>(col_str3).contains(StringData("")).count();
1✔
566
    CHECK_EQUAL(cnt, 4);
1✔
567

568
    cnt = table3->link(col_link1).column<String>(col_str3).begins_with(StringData("")).count();
1✔
569
    CHECK_EQUAL(cnt, 4);
1✔
570

571
    cnt = table3->link(col_link1).column<String>(col_str3).ends_with(StringData("")).count();
1✔
572
    CHECK_EQUAL(cnt, 4);
1✔
573

574
    cnt = table3->link(col_link1).column<String>(col_str3).equal(StringData("")).count();
1✔
575
    CHECK_EQUAL(cnt, 1);
1✔
576

577
    cnt = table3->link(col_link1).column<String>(col_str3).not_equal(StringData("")).count();
1✔
578
    CHECK_EQUAL(cnt, 4);
1✔
579

580
    cnt = table3->link(col_link1).column<String>(col_str3).equal(realm::null()).count();
1✔
581
    CHECK_EQUAL(cnt, 1);
1✔
582

583
    cnt = table3->link(col_link1).column<String>(col_str3).not_equal(realm::null()).count();
1✔
584
    CHECK_EQUAL(cnt, 4);
1✔
585

586

587
    cnt = table3->link(col_link1).column<String>(col_str3).contains(StringData(""), false).count();
1✔
588
    CHECK_EQUAL(cnt, 4);
1✔
589

590
    cnt = table3->link(col_link1).column<String>(col_str3).like(StringData(""), false).count();
1✔
591
    CHECK_EQUAL(cnt, 1);
1✔
592

593
    cnt = table3->link(col_link1).column<String>(col_str3).begins_with(StringData(""), false).count();
1✔
594
    CHECK_EQUAL(cnt, 4);
1✔
595

596
    cnt = table3->link(col_link1).column<String>(col_str3).ends_with(StringData(""), false).count();
1✔
597
    CHECK_EQUAL(cnt, 4);
1✔
598

599
    cnt = table3->link(col_link1).column<String>(col_str3).equal(StringData(""), false).count();
1✔
600
    CHECK_EQUAL(cnt, 1);
1✔
601

602
    cnt = table3->link(col_link1).column<String>(col_str3).not_equal(StringData(""), false).count();
1✔
603
    CHECK_EQUAL(cnt, 4);
1✔
604

605
    cnt = table3->link(col_link1).column<String>(col_str3).equal(realm::null(), false).count();
1✔
606
    CHECK_EQUAL(cnt, 1);
1✔
607

608
    cnt = table3->link(col_link1).column<String>(col_str3).not_equal(realm::null(), false).count();
1✔
609
    CHECK_EQUAL(cnt, 4);
1✔
610

611
    cnt = table3->link(col_link1).column<String>(col_str3).contains(realm::null(), false).count();
1✔
612
    CHECK_EQUAL(cnt, 4);
1✔
613

614
    // Test long string contains search (where needle is longer than 255 chars)
615
    const char long_string[] = "This is a long search string that does not contain the word being searched for!, "
1✔
616
                               "This is a long search string that does not contain the word being searched for!, "
1✔
617
                               "This is a long search string that does not contain the word being searched for!, "
1✔
618
                               "This is a long search string that does not contain the word being searched for!, "
1✔
619
                               "This is a long search string that does not contain the word being searched for!, "
1✔
620
                               "This is a long search string that does not contain the word being searched for!, "
1✔
621
                               "This is a long search string that does not contain the word being searched for!, "
1✔
622
                               "This is a long search string that does not contain the word being searched for!, "
1✔
623
                               "This is a long search string that does not contain the word being searched for!, "
1✔
624
                               "needle, "
1✔
625
                               "This is a long search string that does not contain the word being searched for!, "
1✔
626
                               "This is a long search string that does not contain the word being searched for!";
1✔
627
    const char search_1[] = "This is a long search string that does not contain the word being searched for!, "
1✔
628
                            "This is a long search string that does not contain the word being searched for!, "
1✔
629
                            "This is a long search string that does not contain the word being searched for!, "
1✔
630
                            "This is a long search string that does not contain the word being searched for!, "
1✔
631
                            "This is a long search string that does not contain the word being searched for!, "
1✔
632
                            "This is a long search string that does not contain the word being searched for!, "
1✔
633
                            "NEEDLE";
1✔
634
    const char search_2[] = "This is a long search string that does not contain the word being searched for!, "
1✔
635
                            "This is a long search string that does not contain the word being searched for!, "
1✔
636
                            "This is a long search string that does not contain the word being searched for!, "
1✔
637
                            "This is a long search string that does not contain the word being searched for!, "
1✔
638
                            "This is a long search string that does not contain the word being searched for!, "
1✔
639
                            "This is a long search string that does not contain the word being searched for!, "
1✔
640
                            "needle";
1✔
641
    table2->create_object().set(col_str3, long_string).get_key();
1✔
642

643
    cnt = table2->column<String>(col_str3).contains(search_1, false).count();
1✔
644
    CHECK_EQUAL(cnt, 1);
1✔
645

646
    cnt = table2->column<String>(col_str3).contains(search_2, true).count();
1✔
647
    CHECK_EQUAL(cnt, 1);
1✔
648

649
    cnt = table3->link(col_link1).column<String>(col_str3).like(realm::null(), false).count();
1✔
650
    CHECK_EQUAL(cnt, 1);
1✔
651
}
1✔
652

653
TEST(Query_NextGenSyntaxMonkey0)
654
{
1✔
655
    // Intended to test eval() for columns in query_expression.hpp which fetch 8 values at a time. This test varies
656
    // table size to test out-of-bounds bugs.
657

658
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
659
    for (int iter = 1; iter < 10 + TEST_DURATION * 1000; iter++) {
10✔
660
        const size_t rows = 1 + random.draw_int_mod(2 * REALM_MAX_BPNODE_SIZE);
9✔
661
        Table table;
9✔
662

663
        // Two different row types prevents fallback to query_engine (good because we want to test query_expression)
664
        auto col_int = table.add_column(type_Int, "first");
9✔
665
        auto col_float = table.add_column(type_Float, "second");
9✔
666
        auto col_str = table.add_column(type_String, "third");
9✔
667

668
        for (size_t r = 0; r < rows; r++) {
7,441✔
669
            Obj obj = table.create_object();
7,432✔
670
            // using '% iter' tests different bitwidths
671
            obj.set(col_int, random.draw_int_mod(iter));
7,432✔
672
            obj.set(col_float, float(random.draw_int_mod(iter)));
7,432✔
673
            if (random.draw_bool())
7,432✔
674
                obj.set(col_str, "a");
3,702✔
675
            else
3,730✔
676
                obj.set(col_str, "b");
3,730✔
677
        }
7,432✔
678

679
        size_t tvpos;
9✔
680

681
        realm::Query q =
9✔
682
            table.column<Int>(col_int) > table.column<Float>(col_float) && table.column<String>(col_str) == "a";
9✔
683

684
        // without start or limit
685
        realm::TableView tv = q.find_all();
9✔
686
        tvpos = 0;
9✔
687
        for (Obj o : table) {
7,432✔
688
            if (o.get<Int>(col_int) > o.get<Float>(col_float) && o.get<String>(col_str) == "a") {
7,432✔
689
                tvpos++;
1,248✔
690
            }
1,248✔
691
        }
7,432✔
692
        CHECK_EQUAL(tvpos, tv.size());
9✔
693

694
        tvpos = 0;
9✔
695

696
        // with limit
697
        size_t limit = random.draw_int_mod(rows);
9✔
698
        tv = q.find_all(limit);
9✔
699
        tvpos = 0;
9✔
700
        for (Obj o : table) {
7,432✔
701
            if (tvpos < limit && o.get<Int>(col_int) > o.get<Float>(col_float) && o.get<String>(col_str) == "a") {
7,432✔
702
                tvpos++;
1,142✔
703
            }
1,142✔
704
        }
7,432✔
705
        CHECK_EQUAL(tvpos, tv.size());
9✔
706
    }
9✔
707
}
1✔
708

709
TEST_TYPES(Query_NextGenSyntaxMonkey, std::true_type, std::false_type)
710
{
2✔
711
    static const bool nullable = TEST_TYPE::value;
2✔
712
    Random random(random_int<unsigned long>()); // Seed from slow global generator
2✔
713
    for (int iter = 1; iter < 5 * (TEST_DURATION * TEST_DURATION * TEST_DURATION + 1); iter++) {
10✔
714
        // Set 'rows' to at least '* 20' else some tests will give 0 matches and bad coverage
715
        const size_t rows = 1 + random.draw_int_mod<size_t>(REALM_MAX_BPNODE_SIZE * 20 *
8✔
716
                                                            (TEST_DURATION * TEST_DURATION * TEST_DURATION + 1));
8✔
717
        Table table;
8✔
718
        auto col_int0 = table.add_column(type_Int, "first", nullable);
8✔
719
        auto col_int1 = table.add_column(type_Int, "second", nullable);
8✔
720
        auto col_int2 = table.add_column(type_Int, "third", nullable);
8✔
721

722
        for (size_t r = 0; r < rows; r++) {
71,370✔
723
            Obj obj = table.create_object();
71,362✔
724
            // using '% iter' tests different bitwidths
725
            obj.set(col_int0, random.draw_int_mod(iter));
71,362✔
726
            obj.set(col_int1, random.draw_int_mod(iter));
71,362✔
727
            obj.set(col_int2, random.draw_int_mod(iter));
71,362✔
728
        }
71,362✔
729

730
        size_t tvpos;
8✔
731

732
        // second == 1
733
        realm::Query q1_0 = table.where().equal(col_int1, 1);
8✔
734
        realm::Query q2_0 = table.column<int64_t>(col_int1) == 1;
8✔
735
        realm::TableView tv_0 = q2_0.find_all();
8✔
736
        tvpos = 0;
8✔
737
        for (Obj o : table) {
71,362✔
738
            if (o.get<Int>(col_int1) == 1) {
71,362✔
739
                CHECK_EQUAL(o.get_key(), tv_0.get_key(tvpos));
23,349✔
740
                tvpos++;
23,349✔
741
            }
23,349✔
742
        }
71,362✔
743
        CHECK_EQUAL(tvpos, tv_0.size());
8✔
744

745
        // (first == 0 || first == 1) && second == 1
746
        realm::Query q2_1 = (table.column<int64_t>(col_int0) == 0 || table.column<int64_t>(col_int0) == 1) &&
8✔
747
                            table.column<int64_t>(col_int1) == 1;
8✔
748
        realm::TableView tv_1 = q2_1.find_all();
8✔
749
        tvpos = 0;
8✔
750
        for (Obj o : table) {
71,362✔
751
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 1) && o.get<Int>(col_int1) == 1) {
71,362✔
752
                CHECK_EQUAL(o.get_key(), tv_1.get_key(tvpos));
18,524✔
753
                tvpos++;
18,524✔
754
            }
18,524✔
755
        }
71,362✔
756
        CHECK_EQUAL(tvpos, tv_1.size());
8✔
757

758
        // first == 0 || (first == 1 && second == 1)
759
        realm::Query q2_2 = table.column<int64_t>(col_int0) == 0 ||
8✔
760
                            (table.column<int64_t>(col_int0) == 1 && table.column<int64_t>(col_int1) == 1);
8✔
761
        realm::TableView tv_2 = q2_2.find_all();
8✔
762
        tvpos = 0;
8✔
763
        for (Obj o : table) {
71,362✔
764
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
71,362✔
765
                CHECK_EQUAL(o.get_key(), tv_2.get_key(tvpos));
39,914✔
766
                tvpos++;
39,914✔
767
            }
39,914✔
768
        }
71,362✔
769
        CHECK_EQUAL(tvpos, tv_2.size());
8✔
770

771

772
        // second == 0 && (first == 0 || first == 2)
773
        realm::Query q4_8 = table.column<int64_t>(col_int1) == 0 &&
8✔
774
                            (table.column<int64_t>(col_int0) == 0 || table.column<int64_t>(col_int0) == 2);
8✔
775
        realm::TableView tv_8 = q4_8.find_all();
8✔
776
        tvpos = 0;
8✔
777
        for (Obj o : table) {
71,362✔
778
            if (o.get<Int>(col_int1) == 0 && ((o.get<Int>(col_int0) == 0) || o.get<Int>(col_int0) == 2)) {
71,362✔
779
                CHECK_EQUAL(o.get_key(), tv_8.get_key(tvpos));
19,773✔
780
                tvpos++;
19,773✔
781
            }
19,773✔
782
        }
71,362✔
783
        CHECK_EQUAL(tvpos, tv_8.size());
8✔
784

785

786
        // (first == 0 || first == 2) && (first == 1 || second == 1)
787
        realm::Query q3_7 = (table.column<int64_t>(col_int0) == 0 || table.column<int64_t>(col_int0) == 2) &&
8✔
788
                            (table.column<int64_t>(col_int0) == 1 || table.column<int64_t>(col_int1) == 1);
8✔
789
        realm::TableView tv_7 = q3_7.find_all();
8✔
790
        tvpos = 0;
8✔
791
        for (Obj o : table) {
71,362✔
792
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 2) &&
71,362✔
793
                (o.get<Int>(col_int0) == 1 || o.get<Int>(col_int1) == 1)) {
71,362✔
794
                CHECK_EQUAL(o.get_key(), tv_7.get_key(tvpos));
12,600✔
795
                tvpos++;
12,600✔
796
            }
12,600✔
797
        }
71,362✔
798
        CHECK_EQUAL(tvpos, tv_7.size());
8✔
799

800

801
        // (first == 0 || first == 2) || (first == 1 || second == 1)
802
        realm::Query q4_7 = (table.column<int64_t>(col_int0) == 0 || table.column<int64_t>(col_int0) == 2) ||
8✔
803
                            (table.column<int64_t>(col_int0) == 1 || table.column<int64_t>(col_int1) == 1);
8✔
804
        realm::TableView tv_10 = q4_7.find_all();
8✔
805
        tvpos = 0;
8✔
806
        for (Obj o : table) {
71,362✔
807
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 2) ||
71,362✔
808
                (o.get<Int>(col_int0) == 1 || o.get<Int>(col_int1) == 1)) {
71,362✔
809
                CHECK_EQUAL(o.get_key(), tv_10.get_key(tvpos));
66,846✔
810
                tvpos++;
66,846✔
811
            }
66,846✔
812
        }
71,362✔
813
        CHECK_EQUAL(tvpos, tv_10.size());
8✔
814

815

816
        TableView tv;
8✔
817

818
        // first == 0 || first == 2 || first == 1 || second == 1
819
        realm::Query q20 = table.column<int64_t>(col_int0) == 0 || table.column<int64_t>(col_int0) == 2 ||
8✔
820
                           table.column<int64_t>(col_int0) == 1 || table.column<int64_t>(col_int1) == 1;
8✔
821
        tv = q20.find_all();
8✔
822
        tvpos = 0;
8✔
823
        for (Obj o : table) {
71,362✔
824
            if (o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 2 || o.get<Int>(col_int0) == 1 ||
71,362✔
825
                o.get<Int>(col_int1) == 1) {
71,362✔
826
                CHECK_EQUAL(o.get_key(), tv.get_key(tvpos));
66,846✔
827
                tvpos++;
66,846✔
828
            }
66,846✔
829
        }
71,362✔
830
        CHECK_EQUAL(tvpos, tv.size());
8✔
831

832

833
        realm::Query q21 = table.query("first * 2 > second / 2 + third + 1");
8✔
834
        tv = q21.find_all();
8✔
835
        tvpos = 0;
8✔
836
        for (Obj o : table) {
71,362✔
837
            if (o.get<Int>(col_int0) * 2 > o.get<Int>(col_int1) / 2 + o.get<Int>(col_int2) + 1) {
71,362✔
838
                CHECK_EQUAL(o.get_key(), tv.get_key(tvpos));
22,404✔
839
                tvpos++;
22,404✔
840
            }
22,404✔
841
        }
71,362✔
842
        CHECK_EQUAL(tvpos, tv.size());
8✔
843

844
        realm::Query q22 = table.query("first * 2 > second / 2 + third + 1 + third - third + third - third + third - "
8✔
845
                                       "third + third - third + third - third");
8✔
846
        tv = q22.find_all();
8✔
847
        tvpos = 0;
8✔
848
        for (Obj o : table) {
71,362✔
849
            if (o.get<Int>(col_int0) * 2 > o.get<Int>(col_int1) / 2 + o.get<Int>(col_int2) + 1) {
71,362✔
850
                CHECK_EQUAL(o.get_key(), tv.get_key(tvpos));
22,404✔
851
                tvpos++;
22,404✔
852
            }
22,404✔
853
        }
71,362✔
854
        CHECK_EQUAL(tvpos, tv.size());
8✔
855
    }
8✔
856
}
2✔
857

858

859
TEST(Query_MergeQueriesOverloads)
860
{
1✔
861
    // Tests && and || overloads of Query class
862
    Table table;
1✔
863
    auto col_int0 = table.add_column(type_Int, "first");
1✔
864
    auto col_int1 = table.add_column(type_Int, "second");
1✔
865

866
    table.create_object().set_all(20, 20);
1✔
867
    table.create_object().set_all(20, 30);
1✔
868
    table.create_object().set_all(30, 30);
1✔
869

870
    size_t c;
1✔
871

872
    // q1_0 && q2_0
873
    realm::Query q1_110 = table.where().equal(col_int0, 20);
1✔
874
    realm::Query q2_110 = table.where().equal(col_int1, 30);
1✔
875
    realm::Query q3_110 = q1_110.and_query(q2_110);
1✔
876
    c = q1_110.count();
1✔
877
    c = q2_110.count();
1✔
878
    c = q3_110.count();
1✔
879

880

881
    // The overloads must behave such as if each side of the operator is inside parentheses, that is,
882
    // (first == 1 || first == 20) operator&& (second == 30), regardless of order of operands
883

884
    // q1_0 && q2_0
885
    realm::Query q1_0 = table.where().equal(col_int0, 10).Or().equal(col_int0, 20);
1✔
886
    realm::Query q2_0 = table.where().equal(col_int1, 30);
1✔
887
    realm::Query q3_0 = q1_0 && q2_0;
1✔
888
    c = q3_0.count();
1✔
889
    CHECK_EQUAL(1, c);
1✔
890

891
    // q2_0 && q1_0 (reversed operand order)
892
    realm::Query q1_1 = table.where().equal(col_int0, 10).Or().equal(col_int0, 20);
1✔
893
    realm::Query q2_1 = table.where().equal(col_int1, 30);
1✔
894
    c = q1_1.count();
1✔
895

896
    realm::Query q3_1 = q2_1 && q1_1;
1✔
897
    c = q3_1.count();
1✔
898
    CHECK_EQUAL(1, c);
1✔
899

900
    // Short test for ||
901
    realm::Query q1_2 = table.where().equal(col_int0, 10);
1✔
902
    realm::Query q2_2 = table.where().equal(col_int1, 30);
1✔
903
    realm::Query q3_2 = q2_2 || q1_2;
1✔
904
    c = q3_2.count();
1✔
905
    CHECK_EQUAL(2, c);
1✔
906
}
1✔
907

908

909
TEST(Query_MergeQueries)
910
{
1✔
911
    // test OR vs AND precedence
912
    Table table;
1✔
913
    auto col_int0 = table.add_column(type_Int, "first");
1✔
914
    auto col_int1 = table.add_column(type_Int, "second");
1✔
915

916
    table.create_object().set_all(10, 20);
1✔
917
    table.create_object().set_all(20, 30);
1✔
918
    table.create_object().set_all(30, 20);
1✔
919

920
    // Must evaluate as if and_query is inside paranthesis, that is, (first == 10 || first == 20) && second == 30
921
    realm::Query q1_0 = table.where().equal(col_int0, 10).Or().equal(col_int0, 20);
1✔
922
    realm::Query q2_0 = table.where().and_query(q1_0).equal(col_int1, 30);
1✔
923

924
    size_t c = q2_0.count();
1✔
925
    CHECK_EQUAL(1, c);
1✔
926
}
1✔
927

928
TEST(Query_Not)
929
{
1✔
930
    // test Not vs And, Or, Groups.
931
    Table table;
1✔
932
    auto col_int0 = table.add_column(type_Int, "first");
1✔
933

934
    table.create_object().set(col_int0, 10);
1✔
935
    table.create_object().set(col_int0, 20);
1✔
936
    table.create_object().set(col_int0, 30);
1✔
937

938
    // Illegal queries
939
    auto q = table.where().group();
1✔
940
    CHECK_EQUAL(q.validate(), "Syntax error");
1✔
941
    q = table.where().end_group();
1✔
942
    CHECK_EQUAL(q.validate(), "Syntax error");
1✔
943
    q = table.where().Or();
1✔
944
    CHECK_EQUAL(q.validate(), "Missing both arguments of OR");
1✔
945
    q = table.where().Or().equal(col_int0, 1);
1✔
946
    CHECK_EQUAL(q.validate(), "Missing argument of OR");
1✔
947
    q = table.where().equal(col_int0, 1).Or();
1✔
948
    CHECK_EQUAL(q.validate(), "Missing argument of OR");
1✔
949

950
    // should apply not to single term, leading to query "not A" with two matching entries:
951
    realm::Query q0 = table.where().Not().equal(col_int0, 10);
1✔
952
    CHECK_EQUAL(2, q0.count());
1✔
953

954
    // grouping, after not
955
    realm::Query q0b = table.where().Not().group().equal(col_int0, 10).end_group();
1✔
956
    CHECK_EQUAL(q0b.validate(), "");
1✔
957
    CHECK_EQUAL(2, q0b.count());
1✔
958

959
    // grouping, surrounding not
960
    realm::Query q0c = table.where().group().Not().equal(col_int0, 10).end_group();
1✔
961
    CHECK_EQUAL(2, q0c.count());
1✔
962

963
    // nested nots (implicit grouping)
964
    realm::Query q0d = table.where().Not().Not().equal(col_int0, 10);
1✔
965
    CHECK_EQUAL(1, q0d.count()); // FAILS
1✔
966

967
    realm::Query q0e = table.where().Not().Not().Not().equal(col_int0, 10);
1✔
968
    CHECK_EQUAL(2, q0e.count()); // FAILS
1✔
969

970
    // just checking the above
971
    realm::Query q0f = table.where().Not().not_equal(col_int0, 10);
1✔
972
    CHECK_EQUAL(1, q0f.count());
1✔
973

974
    realm::Query q0g = table.where().Not().Not().not_equal(col_int0, 10);
1✔
975
    CHECK_EQUAL(2, q0g.count()); // FAILS
1✔
976

977
    realm::Query q0h = table.where().not_equal(col_int0, 10);
1✔
978
    CHECK_EQUAL(2, q0h.count());
1✔
979

980
    // should apply not to first term, leading to query "not A and A", which is obviously empty:
981
    realm::Query q1 = table.where().Not().equal(col_int0, 10).equal(col_int0, 10);
1✔
982
    CHECK_EQUAL(0, q1.count());
1✔
983

984
    // should apply not to first term, leading to query "not A and A", which is obviously empty:
985
    realm::Query q1b = table.where().group().Not().equal(col_int0, 10).end_group().equal(col_int0, 10);
1✔
986
    CHECK_EQUAL(0, q1b.count());
1✔
987

988
    // should apply not to first term, leading to query "not A and A", which is obviously empty:
989
    realm::Query q1c = table.where().Not().group().equal(col_int0, 10).end_group().equal(col_int0, 10);
1✔
990
    CHECK_EQUAL(0, q1c.count());
1✔
991

992

993
    // should apply not to second term, leading to query "A and not A", which is obviously empty:
994
    realm::Query q2 = table.where().equal(col_int0, 10).Not().equal(col_int0, 10);
1✔
995
    CHECK_EQUAL(0, q2.count()); // FAILS
1✔
996

997
    // should apply not to second term, leading to query "A and not A", which is obviously empty:
998
    realm::Query q2b = table.where().equal(col_int0, 10).group().Not().equal(col_int0, 10).end_group();
1✔
999
    CHECK_EQUAL(0, q2b.count());
1✔
1000

1001
    // should apply not to second term, leading to query "A and not A", which is obviously empty:
1002
    realm::Query q2c = table.where().equal(col_int0, 10).Not().group().equal(col_int0, 10).end_group();
1✔
1003
    CHECK_EQUAL(0, q2c.count()); // FAILS
1✔
1004

1005

1006
    // should apply not to both terms, leading to query "not A and not A", which has 2 members
1007
    realm::Query q3 = table.where().Not().equal(col_int0, 10).Not().equal(col_int0, 10);
1✔
1008
    CHECK_EQUAL(2, q3.count()); // FAILS
1✔
1009

1010
    // applying not to an empty query is forbidden
1011
    realm::Query q4 = table.where();
1✔
1012
    CHECK_THROW(!q4, realm::Exception);
1✔
1013
}
1✔
1014

1015

1016
TEST(Query_MergeQueriesMonkey)
1017
{
1✔
1018
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
1019
    for (int iter = 0; iter < 5; iter++) {
6✔
1020
        const size_t rows = REALM_MAX_BPNODE_SIZE * 4;
5✔
1021
        Table table;
5✔
1022
        auto col_int0 = table.add_column(type_Int, "first");
5✔
1023
        auto col_int1 = table.add_column(type_Int, "second");
5✔
1024
        auto col_int2 = table.add_column(type_Int, "third");
5✔
1025

1026
        for (size_t r = 0; r < rows; r++) {
20,005✔
1027
            Obj obj = table.create_object();
20,000✔
1028
            obj.set(col_int0, random.draw_int_mod(3));
20,000✔
1029
            obj.set(col_int1, random.draw_int_mod(3));
20,000✔
1030
            obj.set(col_int2, random.draw_int_mod(3));
20,000✔
1031
        }
20,000✔
1032

1033
        size_t tvpos;
5✔
1034

1035
        // and_query(second == 1)
1036
        realm::Query q1_0 = table.where().equal(col_int1, 1);
5✔
1037
        realm::Query q2_0 = table.where().and_query(q1_0);
5✔
1038
        realm::TableView tv_0 = q2_0.find_all();
5✔
1039
        tvpos = 0;
5✔
1040
        for (Obj o : table) {
20,000✔
1041
            if (o.get<Int>(col_int1) == 1) {
20,000✔
1042
                CHECK_EQUAL(o.get_key(), tv_0.get_key(tvpos));
6,727✔
1043
                tvpos++;
6,727✔
1044
            }
6,727✔
1045
        }
20,000✔
1046

1047
        // (first == 0 || first == 1) && and_query(second == 1)
1048
        realm::Query q1_1 = table.where().equal(col_int1, 1);
5✔
1049
        realm::Query q2_1 =
5✔
1050
            table.where().group().equal(col_int0, 0).Or().equal(col_int0, 1).end_group().and_query(q1_1);
5✔
1051
        realm::TableView tv_1 = q2_1.find_all();
5✔
1052
        tvpos = 0;
5✔
1053
        for (Obj o : table) {
20,000✔
1054
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 1) && o.get<Int>(col_int1) == 1) {
20,000✔
1055
                CHECK_EQUAL(o.get_key(), tv_1.get_key(tvpos));
4,445✔
1056
                tvpos++;
4,445✔
1057
            }
4,445✔
1058
        }
20,000✔
1059

1060
        // first == 0 || (first == 1 && and_query(second == 1))
1061
        realm::Query q1_2 = table.where().equal(col_int1, 1);
5✔
1062
        realm::Query q2_2 = table.where().equal(col_int0, 0).Or().equal(col_int0, 1).and_query(q1_2);
5✔
1063
        realm::TableView tv_2 = q2_2.find_all();
5✔
1064
        tvpos = 0;
5✔
1065
        for (Obj o : table) {
20,000✔
1066
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1067
                CHECK_EQUAL(o.get_key(), tv_2.get_key(tvpos));
8,870✔
1068
                tvpos++;
8,870✔
1069
            }
8,870✔
1070
        }
20,000✔
1071

1072
        // and_query(first == 0) || (first == 1 && second == 1)
1073
        realm::Query q1_3 = table.where().equal(col_int0, 0);
5✔
1074
        realm::Query q2_3 = table.where().and_query(q1_3).Or().equal(col_int0, 1).equal(col_int1, 1);
5✔
1075
        realm::TableView tv_3 = q2_3.find_all();
5✔
1076
        tvpos = 0;
5✔
1077
        for (Obj o : table) {
20,000✔
1078
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1079
                CHECK_EQUAL(o.get_key(), tv_3.get_key(tvpos));
8,870✔
1080
                tvpos++;
8,870✔
1081
            }
8,870✔
1082
        }
20,000✔
1083

1084

1085
        // first == 0 || and_query(first == 1 && second == 1)
1086
        realm::Query q2_4 = table.where().equal(col_int0, 1).equal(col_int1, 1);
5✔
1087
        realm::Query q1_4 = table.where().equal(col_int0, 0).Or().and_query(q2_4);
5✔
1088
        realm::TableView tv_4 = q1_4.find_all();
5✔
1089
        tvpos = 0;
5✔
1090
        for (Obj o : table) {
20,000✔
1091
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1092
                CHECK_EQUAL(o.get_key(), tv_4.get_key(tvpos));
8,870✔
1093
                tvpos++;
8,870✔
1094
            }
8,870✔
1095
        }
20,000✔
1096

1097

1098
        // and_query(first == 0 || first == 2) || and_query(first == 1 && second == 1)
1099
        realm::Query q2_5 = table.where().equal(col_int0, 0).Or().equal(col_int0, 2);
5✔
1100
        realm::Query q1_5 = table.where().equal(col_int0, 1).equal(col_int1, 1);
5✔
1101
        realm::Query q3_5 = table.where().and_query(q2_5).Or().and_query(q1_5);
5✔
1102
        realm::TableView tv_5 = q3_5.find_all();
5✔
1103
        tvpos = 0;
5✔
1104
        for (Obj o : table) {
20,000✔
1105
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 2) ||
20,000✔
1106
                (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1107
                CHECK_EQUAL(o.get_key(), tv_5.get_key(tvpos));
15,509✔
1108
                tvpos++;
15,509✔
1109
            }
15,509✔
1110
        }
20,000✔
1111

1112

1113
        // and_query(first == 0) && and_query(second == 1)
1114
        realm::Query q1_6 = table.where().equal(col_int0, 0);
5✔
1115
        realm::Query q2_6 = table.where().equal(col_int1, 1);
5✔
1116
        realm::Query q3_6 = table.where().and_query(q1_6).and_query(q2_6);
5✔
1117
        realm::TableView tv_6 = q3_6.find_all();
5✔
1118
        tvpos = 0;
5✔
1119
        for (Obj o : table) {
20,000✔
1120
            if (o.get<Int>(col_int0) == 0 && o.get<Int>(col_int1) == 1) {
20,000✔
1121
                CHECK_EQUAL(o.get_key(), tv_6.get_key(tvpos));
2,169✔
1122
                tvpos++;
2,169✔
1123
            }
2,169✔
1124
        }
20,000✔
1125

1126
        // and_query(first == 0 || first == 2) && and_query(first == 1 || second == 1)
1127
        realm::Query q2_7 = table.where().equal(col_int0, 0).Or().equal(col_int0, 2);
5✔
1128
        realm::Query q1_7 = table.where().equal(col_int0, 1).equal(col_int0, 1).Or().equal(col_int1, 1);
5✔
1129
        realm::Query q3_7 = table.where().and_query(q2_7).and_query(q1_7);
5✔
1130
        realm::TableView tv_7 = q3_7.find_all();
5✔
1131
        tvpos = 0;
5✔
1132
        for (Obj o : table) {
20,000✔
1133
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 2) &&
20,000✔
1134
                (o.get<Int>(col_int0) == 1 || o.get<Int>(col_int1) == 1)) {
20,000✔
1135
                CHECK_EQUAL(o.get_key(), tv_7.get_key(tvpos));
4,451✔
1136
                tvpos++;
4,451✔
1137
            }
4,451✔
1138
        }
20,000✔
1139

1140
        // Nested and_query
1141

1142
        // second == 0 && and_query(first == 0 || and_query(first == 2))
1143
        realm::Query q2_8 = table.where().equal(col_int0, 2);
5✔
1144
        realm::Query q3_8 = table.where().equal(col_int0, 0).Or().and_query(q2_8);
5✔
1145
        realm::Query q4_8 = table.where().equal(col_int1, 0).and_query(q3_8);
5✔
1146
        realm::TableView tv_8 = q4_8.find_all();
5✔
1147
        tvpos = 0;
5✔
1148
        for (Obj o : table) {
20,000✔
1149
            if (o.get<Int>(col_int1) == 0 && ((o.get<Int>(col_int0) == 0) || o.get<Int>(col_int0) == 2)) {
20,000✔
1150
                CHECK_EQUAL(o.get_key(), tv_8.get_key(tvpos));
4,423✔
1151
                tvpos++;
4,423✔
1152
            }
4,423✔
1153
        }
20,000✔
1154

1155

1156
        // Nested as above but constructed differently
1157

1158
        // second == 0 && and_query(first == 0 || and_query(first == 2))
1159
        realm::Query q2_9 = table.where().equal(col_int0, 2);
5✔
1160
        realm::Query q5_9 = table.where().equal(col_int0, 0);
5✔
1161
        realm::Query q3_9 = table.where().and_query(q5_9).Or().and_query(q2_9);
5✔
1162
        realm::Query q4_9 = table.where().equal(col_int1, 0).and_query(q3_9);
5✔
1163
        realm::TableView tv_9 = q4_9.find_all();
5✔
1164
        tvpos = 0;
5✔
1165
        for (Obj o : table) {
20,000✔
1166
            if (o.get<Int>(col_int1) == 0 && ((o.get<Int>(col_int0) == 0) || o.get<Int>(col_int0) == 2)) {
20,000✔
1167
                CHECK_EQUAL(o.get_key(), tv_9.get_key(tvpos));
4,423✔
1168
                tvpos++;
4,423✔
1169
            }
4,423✔
1170
        }
20,000✔
1171

1172

1173
        // Nested
1174

1175
        // and_query(and_query(and_query(first == 0)))
1176
        realm::Query q2_10 = table.where().equal(col_int0, 0);
5✔
1177
        realm::Query q5_10 = table.where().and_query(q2_10);
5✔
1178
        realm::Query q3_10 = table.where().and_query(q5_10);
5✔
1179
        realm::Query q4_10 = table.where().and_query(q3_10);
5✔
1180
        realm::TableView tv_10 = q4_10.find_all();
5✔
1181
        tvpos = 0;
5✔
1182
        for (Obj o : table) {
20,000✔
1183
            if (o.get<Int>(col_int0) == 0) {
20,000✔
1184
                CHECK_EQUAL(o.get_key(), tv_10.get_key(tvpos));
6,594✔
1185
                tvpos++;
6,594✔
1186
            }
6,594✔
1187
        }
20,000✔
1188
    }
5✔
1189
}
1✔
1190

1191
TEST(Query_MergeQueriesMonkeyOverloads)
1192
{
1✔
1193
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
1194
    for (int iter = 0; iter < 5; iter++) {
6✔
1195
        const size_t rows = REALM_MAX_BPNODE_SIZE * 4;
5✔
1196
        Table table;
5✔
1197
        auto col_int0 = table.add_column(type_Int, "first");
5✔
1198
        auto col_int1 = table.add_column(type_Int, "second");
5✔
1199
        auto col_int2 = table.add_column(type_Int, "third");
5✔
1200

1201
        for (size_t r = 0; r < rows; r++) {
20,005✔
1202
            Obj obj = table.create_object();
20,000✔
1203
            obj.set(col_int0, random.draw_int_mod(3));
20,000✔
1204
            obj.set(col_int1, random.draw_int_mod(3));
20,000✔
1205
            obj.set(col_int2, random.draw_int_mod(3));
20,000✔
1206
        }
20,000✔
1207

1208
        size_t tvpos;
5✔
1209

1210
        // Left side of operator&& is empty query
1211
        // and_query(second == 1)
1212
        realm::Query q1_0 = table.where().equal(col_int1, 1);
5✔
1213
        realm::Query q2_0 = table.where() && q1_0;
5✔
1214
        realm::TableView tv_0 = q2_0.find_all();
5✔
1215
        tvpos = 0;
5✔
1216
        for (Obj o : table) {
20,000✔
1217
            if (o.get<Int>(col_int1) == 1) {
20,000✔
1218
                CHECK_EQUAL(o.get_key(), tv_0.get_key(tvpos));
6,606✔
1219
                tvpos++;
6,606✔
1220
            }
6,606✔
1221
        }
20,000✔
1222

1223
        // Right side of operator&& is empty query
1224
        // and_query(second == 1)
1225
        realm::Query q1_10 = table.where().equal(col_int1, 1);
5✔
1226
        realm::Query q2_10 = q1_10 && table.where();
5✔
1227
        realm::TableView tv_10 = q2_10.find_all();
5✔
1228
        tvpos = 0;
5✔
1229
        for (Obj o : table) {
20,000✔
1230
            if (o.get<Int>(col_int1) == 1) {
20,000✔
1231
                CHECK_EQUAL(o.get_key(), tv_10.get_key(tvpos));
6,606✔
1232
                tvpos++;
6,606✔
1233
            }
6,606✔
1234
        }
20,000✔
1235

1236
        // (first == 0 || first == 1) && and_query(second == 1)
1237
        realm::Query q1_1 = table.where().equal(col_int0, 0);
5✔
1238
        realm::Query q2_1 = table.where().equal(col_int0, 1);
5✔
1239
        realm::Query q3_1 = q1_1 || q2_1;
5✔
1240
        realm::Query q4_1 = table.where().equal(col_int1, 1);
5✔
1241
        realm::Query q5_1 = q3_1 && q4_1;
5✔
1242

1243
        realm::TableView tv_1 = q5_1.find_all();
5✔
1244
        tvpos = 0;
5✔
1245
        for (Obj o : table) {
20,000✔
1246
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 1) && o.get<Int>(col_int1) == 1) {
20,000✔
1247
                CHECK_EQUAL(o.get_key(), tv_1.get_key(tvpos));
4,418✔
1248
                tvpos++;
4,418✔
1249
            }
4,418✔
1250
        }
20,000✔
1251

1252
        // (first == 0 || first == 1) && and_query(second == 1) as above, written in another way
1253
        realm::Query q1_20 =
5✔
1254
            table.where().equal(col_int0, 0).Or().equal(col_int0, 1) && table.where().equal(col_int1, 1);
5✔
1255
        realm::TableView tv_20 = q1_20.find_all();
5✔
1256
        tvpos = 0;
5✔
1257
        for (Obj o : table) {
20,000✔
1258
            if ((o.get<Int>(col_int0) == 0 || o.get<Int>(col_int0) == 1) && o.get<Int>(col_int1) == 1) {
20,000✔
1259
                CHECK_EQUAL(o.get_key(), tv_20.get_key(tvpos));
4,418✔
1260
                tvpos++;
4,418✔
1261
            }
4,418✔
1262
        }
20,000✔
1263

1264
        // and_query(first == 0) || (first == 1 && second == 1)
1265
        realm::Query q1_3 = table.where().equal(col_int0, 0);
5✔
1266
        realm::Query q2_3 = table.where().equal(col_int0, 1);
5✔
1267
        realm::Query q3_3 = table.where().equal(col_int1, 1);
5✔
1268
        realm::Query q4_3 = q1_3 || (q2_3 && q3_3);
5✔
1269
        realm::TableView tv_3 = q4_3.find_all();
5✔
1270
        tvpos = 0;
5✔
1271
        for (Obj o : table) {
20,000✔
1272
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1273
                CHECK_EQUAL(o.get_key(), tv_3.get_key(tvpos));
8,856✔
1274
                tvpos++;
8,856✔
1275
            }
8,856✔
1276
        }
20,000✔
1277

1278

1279
        // and_query(first == 0) || (first == 1 && second == 1) written in another way
1280
        realm::Query q1_30 = table.where().equal(col_int0, 0);
5✔
1281
        realm::Query q3_30 = table.where().equal(col_int1, 1);
5✔
1282
        realm::Query q4_30 = table.where().equal(col_int0, 0) || (table.where().equal(col_int0, 1) && q3_30);
5✔
1283
        realm::TableView tv_30 = q4_30.find_all();
5✔
1284
        tvpos = 0;
5✔
1285
        for (Obj o : table) {
20,000✔
1286
            if (o.get<Int>(col_int0) == 0 || (o.get<Int>(col_int0) == 1 && o.get<Int>(col_int1) == 1)) {
20,000✔
1287
                CHECK_EQUAL(o.get_key(), tv_30.get_key(tvpos));
8,856✔
1288
                tvpos++;
8,856✔
1289
            }
8,856✔
1290
        }
20,000✔
1291
    }
5✔
1292
}
1✔
1293

1294
TEST(Query_Expressions0)
1295
{
1✔
1296
    /*
1297
    We have following variables to vary in the tests:
1298

1299
    left        right
1300
    +           -           *           /          pow
1301
    Subexpr    Column       Value
1302
    >           <           ==          !=          >=          <=
1303
    float       int         double      int64_t
1304

1305
    Many of them are combined and tested together in equality classes below
1306
    */
1307
    Table table;
1✔
1308
    auto col_int = table.add_column(type_Int, "first");
1✔
1309
    auto col_float = table.add_column(type_Float, "second");
1✔
1310
    auto col_double = table.add_column(type_Double, "third");
1✔
1311

1312

1313
    ObjKey match;
1✔
1314

1315
    Columns<int64_t> first = table.column<int64_t>(col_int);
1✔
1316
    Columns<float> second = table.column<float>(col_float);
1✔
1317
    Columns<double> third = table.column<double>(col_double);
1✔
1318

1319
    ObjKey key0 = table.create_object().set_all(20, 19.9f, 3.0).get_key();
1✔
1320
    ObjKey key1 = table.create_object().set_all(20, 20.1f, 4.0).get_key();
1✔
1321

1322
    /**
1323
    Conversion / promotion
1324
    **/
1325

1326
    // 20 must convert to float
1327
    match = table.query("second + 0.2 > 20").find();
1✔
1328
    CHECK_EQUAL(match, key0);
1✔
1329

1330
    match = (first >= 20.0f).find();
1✔
1331
    CHECK_EQUAL(match, key0);
1✔
1332

1333
    // 20.1f must remain float
1334
    match = (first >= 20.1f).find();
1✔
1335
    CHECK_EQUAL(match, null_key);
1✔
1336

1337
    // first must convert to float
1338
    match = (second >= first).find();
1✔
1339
    CHECK_EQUAL(match, key1);
1✔
1340

1341
    // 20 and 40 must convert to float
1342
    match = table.query("second + 20 > 40").find();
1✔
1343
    CHECK_EQUAL(match, key1);
1✔
1344

1345
    // first and 40 must convert to float
1346
    match = table.query("second + first >= 40").find();
1✔
1347
    CHECK_EQUAL(match, key1);
1✔
1348

1349
    // 20 must convert to float
1350
    match = table.query("0.2f + second > 20").find();
1✔
1351
    CHECK_EQUAL(match, key0);
1✔
1352

1353
    /**
1354
    Permutations of types (Subexpr, Value, Column) of left/right side
1355
    **/
1356

1357
    // Compare, left = Subexpr, right = Value
1358
    match = table.query("second + first >= 40").find();
1✔
1359
    CHECK_EQUAL(match, key1);
1✔
1360

1361
    match = table.query("second + first > 40").find();
1✔
1362
    CHECK_EQUAL(match, key1);
1✔
1363

1364
    match = table.query("first - second < 0").find();
1✔
1365
    CHECK_EQUAL(match, key1);
1✔
1366

1367
    match = table.query("second - second == 0").find();
1✔
1368
    CHECK_EQUAL(match, key0);
1✔
1369

1370
    match = table.query("first - second <= 0").find();
1✔
1371
    CHECK_EQUAL(match, key1);
1✔
1372

1373
    match = table.query("first * first != 400").find();
1✔
1374
    CHECK_EQUAL(match, null_key);
1✔
1375

1376
    // Compare, left = Column, right = Value
1377
    match = (second >= 20).find();
1✔
1378
    CHECK_EQUAL(match, key1);
1✔
1379

1380
    match = (second > 20).find();
1✔
1381
    CHECK_EQUAL(match, key1);
1✔
1382

1383
    match = (second < 20).find();
1✔
1384
    CHECK_EQUAL(match, key0);
1✔
1385

1386
    match = (second == 20.1f).find();
1✔
1387
    CHECK_EQUAL(match, key1);
1✔
1388

1389
    match = (second != 19.9f).find();
1✔
1390
    CHECK_EQUAL(match, key1);
1✔
1391

1392
    match = (second <= 21).find();
1✔
1393
    CHECK_EQUAL(match, key0);
1✔
1394

1395
    // Compare, left = Column, right = Value
1396
    match = (20 <= second).find();
1✔
1397
    CHECK_EQUAL(match, key1);
1✔
1398

1399
    match = (20 < second).find();
1✔
1400
    CHECK_EQUAL(match, key1);
1✔
1401

1402
    match = (20 > second).find();
1✔
1403
    CHECK_EQUAL(match, key0);
1✔
1404

1405
    match = (20.1f == second).find();
1✔
1406
    CHECK_EQUAL(match, key1);
1✔
1407

1408
    match = (19.9f != second).find();
1✔
1409
    CHECK_EQUAL(match, key1);
1✔
1410

1411
    match = (21 >= second).find();
1✔
1412
    CHECK_EQUAL(match, key0);
1✔
1413

1414
    // Compare, left = Subexpr, right = Value
1415
    match = table.query("40 <= second + first").find();
1✔
1416
    CHECK_EQUAL(match, key1);
1✔
1417

1418
    match = table.query("40 < second + first").find();
1✔
1419
    CHECK_EQUAL(match, key1);
1✔
1420

1421
    match = table.query("0 > first - second").find();
1✔
1422
    CHECK_EQUAL(match, key1);
1✔
1423

1424
    match = table.query("0 == second - second").find();
1✔
1425
    CHECK_EQUAL(match, key0);
1✔
1426

1427
    match = table.query("0 >= first - second").find();
1✔
1428
    CHECK_EQUAL(match, key1);
1✔
1429

1430
    match = table.query("400 != first * first").find();
1✔
1431
    CHECK_EQUAL(match, null_key);
1✔
1432

1433
    // Col compare Col
1434
    match = (second > first).find();
1✔
1435
    CHECK_EQUAL(match, key1);
1✔
1436

1437
    match = (second >= first).find();
1✔
1438
    CHECK_EQUAL(match, key1);
1✔
1439

1440
    match = (second == first).find();
1✔
1441
    CHECK_EQUAL(match, null_key);
1✔
1442

1443
    match = (second != second).find();
1✔
1444
    CHECK_EQUAL(match, null_key);
1✔
1445

1446
    match = (first < second).find();
1✔
1447
    CHECK_EQUAL(match, key1);
1✔
1448

1449
    match = (first <= second).find();
1✔
1450
    CHECK_EQUAL(match, key1);
1✔
1451

1452
    // Subexpr compare Subexpr
1453
    match = table.query("second + 0 > first + 0").find();
1✔
1454
    CHECK_EQUAL(match, key1);
1✔
1455

1456
    match = table.query("second + 0 >= first + 0").find();
1✔
1457
    CHECK_EQUAL(match, key1);
1✔
1458

1459
    match = table.query("second + 0 == first + 0").find();
1✔
1460
    CHECK_EQUAL(match, null_key);
1✔
1461

1462
    match = table.query("second + 0 != second + 0").find();
1✔
1463
    CHECK_EQUAL(match, null_key);
1✔
1464

1465
    match = table.query("first + 0 < second + 0").find();
1✔
1466
    CHECK_EQUAL(match, key1);
1✔
1467

1468
    match = table.query("first + 0 <= second + 0").find();
1✔
1469
    CHECK_EQUAL(match, key1);
1✔
1470

1471
    // Conversions, again
1472
    table.clear();
1✔
1473
    key0 = table.create_object().set_all(17, 3.0f, 3.0).get_key();
1✔
1474

1475
    match = table.query("1 / second == 1 / second").find();
1✔
1476
    CHECK_EQUAL(match, key0);
1✔
1477

1478
    match = table.query("1 / third == 1 / third").find();
1✔
1479
    CHECK_EQUAL(match, key0);
1✔
1480

1481
    // Nifty test: Compare operator must preserve precision of each side, hence NO match; if double accidentially
1482
    // was truncated to float, or float was rounded to nearest double, then this test would fail.
1483
    match = table.query("1 / second == 1 / third").find();
1✔
1484
    CHECK_EQUAL(match, null_key);
1✔
1485

1486
    match = table.query("first / 2 == 8").find();
1✔
1487
    CHECK_EQUAL(match, key0);
1✔
1488

1489
    // For `float < int_column` we had a bug where int was promoted to float instead of double.
1490
    table.clear();
1✔
1491
    auto k = table.create_object().set(col_int, 1000000001).get_key();
1✔
1492

1493
    match = (1000000000.f < first).find();
1✔
1494
    CHECK_EQUAL(match, k);
1✔
1495

1496
    match = (first > 1000000000.f).find();
1✔
1497
    CHECK_EQUAL(match, k);
1✔
1498
}
1✔
1499

1500
TEST(Query_StrIndexCrash)
1501
{
1✔
1502
    // Rasmus "8" index crash
1503
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
1504

1505
    for (int iter = 0; iter < 5; ++iter) {
6✔
1506
        Group group;
5✔
1507
        TableRef table = group.add_table("test");
5✔
1508
        auto col = table->add_column(type_String, "first");
5✔
1509

1510
        size_t eights = 0;
5✔
1511

1512
        for (int i = 0; i < REALM_MAX_BPNODE_SIZE * 2; ++i) {
10,005✔
1513
            int v = random.draw_int_mod(10);
10,000✔
1514
            if (v == 8) {
10,000✔
1515
                eights++;
1,003✔
1516
            }
1,003✔
1517
            table->create_object().set(col, util::to_string(v));
10,000✔
1518
        }
10,000✔
1519

1520
        table->add_search_index(col);
5✔
1521
        TableView v = table->where().equal(col, StringData("8")).find_all();
5✔
1522
        CHECK_EQUAL(eights, v.size());
5✔
1523

1524
        v = table->where().equal(col, StringData("10")).find_all();
5✔
1525

1526
        v = table->where().equal(col, StringData("8")).find_all();
5✔
1527
        CHECK_EQUAL(eights, v.size());
5✔
1528
    }
5✔
1529
}
1✔
1530

1531
TEST(Query_IntIndex)
1532
{
1✔
1533
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
1534
    Group group;
1✔
1535
    TableRef table = group.add_table("test");
1✔
1536
    auto col = table->add_column(type_Int, "first", true);
1✔
1537
    table->add_search_index(col);
1✔
1538

1539
    size_t eights = 0;
1✔
1540
    size_t nulls = 0;
1✔
1541

1542
    for (int i = 0; i < REALM_MAX_BPNODE_SIZE * 2; ++i) {
2,001✔
1543
        int v = random.draw_int_mod(10);
2,000✔
1544
        if (v == 8) {
2,000✔
1545
            eights++;
192✔
1546
        }
192✔
1547
        auto obj = table->create_object();
2,000✔
1548
        if (v == 5) {
2,000✔
1549
            nulls++;
197✔
1550
        }
197✔
1551
        else {
1,803✔
1552
            obj.set(col, v);
1,803✔
1553
        }
1,803✔
1554
    }
2,000✔
1555

1556
    // This will use IntegerNode
1557
    auto q = table->column<Int>(col) == 8;
1✔
1558
    auto cnt = q.count();
1✔
1559
    CHECK_EQUAL(cnt, eights);
1✔
1560

1561
    // Uses Compare expression
1562
    q = table->column<Int>(col) == 8.0;
1✔
1563
    cnt = q.count();
1✔
1564
    CHECK_EQUAL(cnt, eights);
1✔
1565

1566
    TableRef origin = group.add_table("origin");
1✔
1567
    auto col_link = origin->add_column(*table, "link");
1✔
1568
    for (auto&& o : *table) {
2,000✔
1569
        origin->create_object().set(col_link, o.get_key());
2,000✔
1570
    }
2,000✔
1571
    // Querying over links makes sure we will not use IntegerNode
1572
    q = origin->link(col_link).column<Int>(col) == 8;
1✔
1573
    cnt = q.count();
1✔
1574
    CHECK_EQUAL(cnt, eights);
1✔
1575

1576
    q = origin->link(col_link).column<Int>(col) == realm::null();
1✔
1577
    cnt = q.count();
1✔
1578
    CHECK_EQUAL(cnt, nulls);
1✔
1579
}
1✔
1580

1581
TEST(Query_StringIndexNull)
1582
{
1✔
1583
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
1584
    Group group;
1✔
1585
    TableRef table = group.add_table("test");
1✔
1586
    auto col = table->add_column(type_String, "first", true);
1✔
1587
    table->add_search_index(col);
1✔
1588

1589
    size_t nulls = 0;
1✔
1590

1591
    for (int i = 0; i < REALM_MAX_BPNODE_SIZE * 2; ++i) {
2,001✔
1592
        int v = random.draw_int_mod(10);
2,000✔
1593
        auto obj = table->create_object();
2,000✔
1594
        if (v == 8) {
2,000✔
1595
            nulls++;
211✔
1596
        }
211✔
1597
        else {
1,789✔
1598
            obj.set(col, util::to_string(v));
1,789✔
1599
        }
1,789✔
1600
    }
2,000✔
1601

1602
    TableRef origin = group.add_table("origin");
1✔
1603
    auto col_link = origin->add_column(*table, "link");
1✔
1604
    for (auto&& o : *table) {
2,000✔
1605
        origin->create_object().set(col_link, o.get_key());
2,000✔
1606
    }
2,000✔
1607

1608
    auto q = origin->link(col_link).column<String>(col) == realm::null();
1✔
1609
    auto cnt = q.count();
1✔
1610
    CHECK_EQUAL(cnt, nulls);
1✔
1611
}
1✔
1612

1613
TEST(Query_Links)
1614
{
1✔
1615
    Group g;
1✔
1616

1617
    TableRef origin = g.add_table("origin");
1✔
1618
    TableRef target1 = g.add_table("target1");
1✔
1619
    TableRef target2 = g.add_table("target2");
1✔
1620

1621
    auto int_col = target2->add_column(type_Int, "integers");
1✔
1622
    auto str_col = target1->add_column(type_String, "strings");
1✔
1623
    auto linklist_col = target1->add_column_list(*target2, "linklist");
1✔
1624
    auto link_col = origin->add_column(*target1, "link");
1✔
1625
    auto double_col = origin->add_column(type_Double, "doubles");
1✔
1626

1627
    std::vector<ObjKey> origin_keys;
1✔
1628
    origin->create_objects(10, origin_keys);
1✔
1629
    std::vector<ObjKey> target1_keys;
1✔
1630
    target1->create_objects(10, target1_keys);
1✔
1631
    std::vector<ObjKey> target2_keys;
1✔
1632
    target2->create_objects(10, target2_keys);
1✔
1633

1634
    for (int i = 0; i < 10; i++) {
11✔
1635
        target2->get_object(target2_keys[i]).set(int_col, i);
10✔
1636
    }
10✔
1637

1638
    for (unsigned i = 0; i < 10; i++) {
11✔
1639
        Obj obj = target1->get_object(target1_keys[i]);
10✔
1640
        std::string str = "Str";
10✔
1641
        str += util::to_string(i);
10✔
1642
        obj.set(str_col, StringData(str));
10✔
1643
        auto lv = obj.get_linklist(linklist_col);
10✔
1644
        for (unsigned j = 0; j < i % 5; j++) {
30✔
1645
            lv.add(target2_keys[j]);
20✔
1646
        }
20✔
1647
    }
10✔
1648

1649
    for (unsigned i = 0; i < 10; i++) {
11✔
1650
        Obj obj = origin->get_object(origin_keys[i]);
10✔
1651
        obj.set(link_col, target1_keys[i]);
10✔
1652
        obj.set(double_col, 1.5 * i);
10✔
1653
    }
10✔
1654

1655
    Query q = target1->link(linklist_col).column<Int>(int_col) == 2;
1✔
1656
    auto tv = q.find_all();
1✔
1657
    CHECK_EQUAL(tv.size(), 4);
1✔
1658

1659
    q = origin->link(link_col).link(linklist_col).column<Int>(int_col) == 2;
1✔
1660
    tv = q.find_all();
1✔
1661
    CHECK_EQUAL(tv.size(), 4);
1✔
1662

1663
    q = target2->backlink(*target1, linklist_col).column<String>(str_col) == StringData("Str3");
1✔
1664
    tv = q.find_all();
1✔
1665
    CHECK_EQUAL(tv.size(), 3);
1✔
1666

1667
    q = target2->backlink(*target1, linklist_col).backlink(*origin, link_col).column<double>(double_col) > 12.0;
1✔
1668
    tv = q.find_all();
1✔
1669
    CHECK_EQUAL(tv.size(), 4);
1✔
1670
}
1✔
1671

1672
TEST(Query_size)
1673
{
1✔
1674
    Group g;
1✔
1675

1676
    TableRef table1 = g.add_table("primary");
1✔
1677
    TableRef table2 = g.add_table("secondary");
1✔
1678
    TableRef table3 = g.add_table("top");
1✔
1679

1680
    auto string_col = table1->add_column(type_String, "strings");
1✔
1681
    auto bin_col = table1->add_column(type_Binary, "binaries", true);
1✔
1682
    auto int_list_col = table1->add_column_list(type_Int, "intlist");
1✔
1683
    auto linklist_col = table1->add_column_list(*table2, "linklist");
1✔
1684

1685
    auto int_col = table2->add_column(type_Int, "integers");
1✔
1686

1687
    auto link_col = table3->add_column(*table1, "link");
1✔
1688
    auto linklist_col1 = table3->add_column_list(*table1, "linklist");
1✔
1689

1690
    std::vector<ObjKey> table1_keys;
1✔
1691
    table1->create_objects(10, table1_keys);
1✔
1692
    std::vector<ObjKey> table2_keys;
1✔
1693
    table2->create_objects(10, table2_keys);
1✔
1694
    std::vector<ObjKey> table3_keys;
1✔
1695
    table3->create_objects(10, table3_keys);
1✔
1696

1697
    auto strings = table1->column<String>(string_col);
1✔
1698
    auto binaries = table1->column<Binary>(bin_col);
1✔
1699
    auto intlist = table1->column<Lst<Int>>(int_list_col);
1✔
1700
    auto linklist = table1->column<Lst<ObjKey>>(linklist_col);
1✔
1701

1702
    for (int i = 0; i < 10; i++) {
11✔
1703
        table2->get_object(table2_keys[i]).set(int_col, i);
10✔
1704
    }
10✔
1705

1706
    // Leave the last one null
1707
    for (unsigned i = 0; i < 9; i++) {
10✔
1708
        table3->get_object(table3_keys[i]).set(link_col, table1_keys[i % 4]);
9✔
1709
    }
9✔
1710

1711
    for (unsigned i = 0; i < 10; i++) {
11✔
1712
        auto lv = table3->get_object(table3_keys[i]).get_linklist(linklist_col1);
10✔
1713
        for (unsigned j = 0; j < i % 5; j++) {
30✔
1714
            lv.add(table1_keys[j]);
20✔
1715
        }
20✔
1716
    }
10✔
1717

1718
    std::string bin1(100, 'a');
1✔
1719
    std::string bin2(500, '5');
1✔
1720
    table1->get_object(table1_keys[0]).set(string_col, "Hi").set(bin_col, BinaryData(bin1));
1✔
1721
    table1->get_object(table1_keys[1]).set(string_col, "world").set(bin_col, BinaryData(bin2));
1✔
1722

1723
    auto set_list = [](LstPtr<Int> list, const std::vector<int64_t>& value_list) {
4✔
1724
        size_t sz = value_list.size();
4✔
1725
        list->clear();
4✔
1726
        for (size_t i = 0; i < sz; i++) {
26✔
1727
            list->add(value_list[i]);
22✔
1728
        }
22✔
1729
    };
4✔
1730
    set_list(table1->get_object(table1_keys[0]).get_list_ptr<Int>(int_list_col),
1✔
1731
             std::vector<Int>({100, 200, 300, 400, 500}));
1✔
1732
    set_list(table1->get_object(table1_keys[1]).get_list_ptr<Int>(int_list_col), std::vector<Int>({1, 2, 3}));
1✔
1733
    set_list(table1->get_object(table1_keys[2]).get_list_ptr<Int>(int_list_col), std::vector<Int>({1, 2, 3, 4, 5}));
1✔
1734
    set_list(table1->get_object(table1_keys[3]).get_list_ptr<Int>(int_list_col),
1✔
1735
             std::vector<Int>({1, 2, 3, 4, 5, 6, 7, 8, 9}));
1✔
1736

1737
    auto set_links = [&table2_keys](LnkLstPtr lv, const std::vector<int>& value_list) {
2✔
1738
        for (auto v : value_list) {
10✔
1739
            lv->add(table2_keys[v]);
10✔
1740
        }
10✔
1741
    };
2✔
1742
    set_links(table1->get_object(table1_keys[0]).get_linklist_ptr(linklist_col),
1✔
1743
              std::vector<int>({0, 1, 2, 3, 4, 5}));
1✔
1744
    set_links(table1->get_object(table1_keys[1]).get_linklist_ptr(linklist_col), std::vector<int>({6, 7, 8, 9}));
1✔
1745

1746
    Query q;
1✔
1747
    Query q1;
1✔
1748
    ObjKey match;
1✔
1749
    TableView tv;
1✔
1750

1751
    q = strings.size() == 5;
1✔
1752
    q1 = table1->where().size_equal(string_col, 5);
1✔
1753
    match = q.find();
1✔
1754
    CHECK_EQUAL(table1_keys[1], match);
1✔
1755
    match = q1.find();
1✔
1756
    CHECK_EQUAL(table1_keys[1], match);
1✔
1757

1758
    // Check that the null values are handled correctly
1759
    q = binaries.size() == realm::null();
1✔
1760
    tv = q.find_all();
1✔
1761
    CHECK_EQUAL(tv.size(), 8);
1✔
1762
    CHECK_EQUAL(tv.get_key(0), table1_keys[2]);
1✔
1763

1764
    // Here the null values should not be included in the search
1765
    q = binaries.size() < 500;
1✔
1766
    q1 = table1->where().size_less(bin_col, 500);
1✔
1767
    tv = q.find_all();
1✔
1768
    CHECK_EQUAL(tv.size(), 1);
1✔
1769
    tv = q1.find_all();
1✔
1770
    CHECK_EQUAL(tv.size(), 1);
1✔
1771

1772
    q = intlist.size() > 3;
1✔
1773
    q1 = table1->where().size_greater(int_list_col, 3);
1✔
1774
    tv = q.find_all();
1✔
1775
    CHECK_EQUAL(3, tv.size());
1✔
1776
    tv = q1.find_all();
1✔
1777
    CHECK_EQUAL(3, tv.size());
1✔
1778
    q1 = table1->where().size_between(int_list_col, 3, 7);
1✔
1779
    tv = q1.find_all();
1✔
1780
    CHECK_EQUAL(3, tv.size());
1✔
1781

1782
    q = intlist.size() == 3;
1✔
1783
    match = q.find();
1✔
1784
    CHECK_EQUAL(table1_keys[1], match);
1✔
1785

1786
    q1 = table1->where().size_not_equal(linklist_col, 6);
1✔
1787
    match = q1.find();
1✔
1788
    CHECK_EQUAL(table1_keys[1], match);
1✔
1789

1790
    q = intlist.size() > strings.size();
1✔
1791
    tv = q.find_all();
1✔
1792
    CHECK_EQUAL(3, tv.size());
1✔
1793
    CHECK_EQUAL(table1_keys[0], tv.get_key(0));
1✔
1794

1795
    // Single links
1796
    q = table3->link(link_col).column<Lst<Int>>(int_list_col).size() == 5;
1✔
1797
    tv = q.find_all();
1✔
1798
    CHECK_EQUAL(5, tv.size());
1✔
1799

1800
    // Multiple links
1801
    q = table3->link(linklist_col1).column<Lst<Int>>(int_list_col).size() == 3;
1✔
1802
    tv = q.find_all();
1✔
1803
    CHECK_EQUAL(6, tv.size());
1✔
1804
}
1✔
1805

1806
TEST(Query_ListOfPrimitives)
1807
{
1✔
1808
    Group g;
1✔
1809

1810
    TableRef table = g.add_table("foo");
1✔
1811

1812
    auto col_int_list = table->add_column_list(type_Int, "integers");
1✔
1813
    auto col_string_list = table->add_column_list(type_String, "strings");
1✔
1814
    auto col_string = table->add_column(type_String, "other");
1✔
1815
    std::vector<ObjKey> keys;
1✔
1816

1817
    table->create_objects(4, keys);
1✔
1818

1819
    {
1✔
1820
        auto set_string_list = [](Lst<String> list, const std::vector<int64_t>& value_list) {
3✔
1821
            size_t sz = value_list.size();
3✔
1822
            list.clear();
3✔
1823
            for (size_t i = 0; i < sz; i++) {
14✔
1824
                if (value_list[i] < 100) {
11✔
1825
                    std::string str("Str_");
10✔
1826
                    str += util::to_string(value_list[i]);
10✔
1827
                    list.add(str);
10✔
1828
                }
10✔
1829
            }
11✔
1830
        };
3✔
1831

1832
        set_string_list(table->get_object(keys[0]).get_list<String>(col_string_list), std::vector<Int>({0, 1}));
1✔
1833
        set_string_list(table->get_object(keys[1]).get_list<String>(col_string_list), std::vector<Int>({2, 3, 4, 5}));
1✔
1834
        set_string_list(table->get_object(keys[2]).get_list<String>(col_string_list),
1✔
1835
                        std::vector<Int>({6, 7, 100, 8, 9}));
1✔
1836
    }
1✔
1837

1838
    table->get_object(keys[0]).set_list_values(col_int_list, std::vector<Int>({0, 1}));
1✔
1839
    table->get_object(keys[1]).set_list_values(col_int_list, std::vector<Int>({2, 3, 4, 5}));
1✔
1840
    table->get_object(keys[2]).set_list_values(col_int_list, std::vector<Int>({6, 7, 8, 9}));
1✔
1841
    table->get_object(keys[3]).set_list_values(col_int_list, std::vector<Int>({}));
1✔
1842

1843
    table->get_object(keys[0]).set<String>(col_string, StringData("foo"));
1✔
1844
    table->get_object(keys[1]).set<String>(col_string, StringData("str"));
1✔
1845
    table->get_object(keys[2]).set<String>(col_string, StringData("str_9_baa"));
1✔
1846

1847
    Query q;
1✔
1848
    TableView tv;
1✔
1849
    q = table->column<Lst<Int>>(col_int_list) == 5;
1✔
1850
    tv = q.find_all();
1✔
1851
    CHECK_EQUAL(tv.size(), 1);
1✔
1852
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1853
    q = table->column<Lst<String>>(col_string_list) == "Str_5";
1✔
1854
    tv = q.find_all();
1✔
1855
    CHECK_EQUAL(tv.size(), 1);
1✔
1856
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1857

1858
    q = table->column<Lst<String>>(col_string_list).begins_with("Str");
1✔
1859
    tv = q.find_all();
1✔
1860
    CHECK_EQUAL(tv.size(), 3);
1✔
1861
    q = table->column<Lst<String>>(col_string_list).ends_with("_8");
1✔
1862
    tv = q.find_all();
1✔
1863
    CHECK_EQUAL(tv.size(), 1);
1✔
1864
    CHECK_EQUAL(tv.get_key(0), keys[2]);
1✔
1865
    q = table->column<Lst<String>>(col_string_list).begins_with(table->column<String>(col_string), false);
1✔
1866
    tv = q.find_all();
1✔
1867
    CHECK_EQUAL(tv.size(), 1);
1✔
1868
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1869
    q = table->column<String>(col_string).begins_with(table->column<Lst<String>>(col_string_list), false);
1✔
1870
    tv = q.find_all();
1✔
1871
    CHECK_EQUAL(tv.size(), 1);
1✔
1872
    CHECK_EQUAL(tv.get_key(0), keys[2]);
1✔
1873

1874
    q = table->column<Lst<Int>>(col_int_list).min() >= 2;
1✔
1875
    tv = q.find_all();
1✔
1876
    CHECK_EQUAL(tv.size(), 2);
1✔
1877
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1878
    CHECK_EQUAL(tv.get_key(1), keys[2]);
1✔
1879
    q = table->column<Lst<Int>>(col_int_list).max() > 6;
1✔
1880
    tv = q.find_all();
1✔
1881
    CHECK_EQUAL(tv.size(), 1);
1✔
1882
    CHECK_EQUAL(tv.get_key(0), keys[2]);
1✔
1883
    q = table->column<Lst<Int>>(col_int_list).sum() == 14;
1✔
1884
    tv = q.find_all();
1✔
1885
    CHECK_EQUAL(tv.size(), 1);
1✔
1886
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1887
    q = table->column<Lst<Int>>(col_int_list).average() < 4;
1✔
1888
    tv = q.find_all();
1✔
1889
    CHECK_EQUAL(tv.size(), 2);
1✔
1890
    CHECK_EQUAL(tv.get_key(0), keys[0]);
1✔
1891
    CHECK_EQUAL(tv.get_key(1), keys[1]);
1✔
1892

1893
    TableRef baa = g.add_table("baa");
1✔
1894
    auto col_link = baa->add_column(*table, "link");
1✔
1895
    auto col_linklist = baa->add_column_list(*table, "linklist");
1✔
1896
    Obj obj0 = baa->create_object().set(col_link, keys[1]);
1✔
1897
    Obj obj1 = baa->create_object().set(col_link, keys[0]);
1✔
1898

1899
    auto lv = obj0.get_linklist_ptr(col_linklist);
1✔
1900
    lv->add(keys[0]);
1✔
1901
    lv->add(keys[1]);
1✔
1902
    lv = obj1.get_linklist_ptr(col_linklist);
1✔
1903
    lv->add(keys[1]);
1✔
1904
    lv->add(keys[2]);
1✔
1905
    lv->add(keys[3]);
1✔
1906

1907
    q = baa->link(col_link).column<Lst<Int>>(col_int_list) == 5;
1✔
1908
    tv = q.find_all();
1✔
1909
    CHECK_EQUAL(tv.size(), 1);
1✔
1910
    CHECK_EQUAL(tv.get_key(0), keys[0]);
1✔
1911

1912
    q = baa->link(col_linklist).column<Lst<String>>(col_string_list) == "Str_5";
1✔
1913
    tv = q.find_all();
1✔
1914
    CHECK_EQUAL(tv.size(), 2);
1✔
1915

1916
    q = baa->link(col_linklist).column<Lst<Int>>(col_int_list).average() >= 3.0;
1✔
1917
    tv = q.find_all();
1✔
1918
    CHECK_EQUAL(tv.size(), 2);
1✔
1919
    table->get_object(keys[1]).get_list<Int>(col_int_list).set(3, -10); // {2, 3, 4, -10}
1✔
1920
    // Now, one less object will have average bigger than 3
1921
    tv.sync_if_needed();
1✔
1922
    CHECK_EQUAL(tv.size(), 1);
1✔
1923
}
1✔
1924

1925
TEST(Query_SetOfPrimitives)
1926
{
1✔
1927
    Group g;
1✔
1928

1929
    TableRef table = g.add_table("foo");
1✔
1930

1931
    auto col_int_set = table->add_column_set(type_Int, "integers");
1✔
1932
    std::vector<ObjKey> keys;
1✔
1933

1934
    table->create_objects(4, keys);
1✔
1935

1936
    auto set_values = [](Set<Int> set, const std::vector<Int>& value_list) {
4✔
1937
        for (auto val : value_list)
4✔
1938
            set.insert(val);
14✔
1939
    };
4✔
1940

1941
    set_values(table->get_object(keys[0]).get_set<Int>(col_int_set), {0, 1});
1✔
1942
    set_values(table->get_object(keys[1]).get_set<Int>(col_int_set), {2, 3, 4, 5});
1✔
1943
    set_values(table->get_object(keys[2]).get_set<Int>(col_int_set), {6, 7, 100, 8, 9});
1✔
1944
    set_values(table->get_object(keys[3]).get_set<Int>(col_int_set), {3, 11, 7});
1✔
1945

1946
    Query q = table->column<Set<Int>>(col_int_set) == 3;
1✔
1947
    auto tv = q.find_all();
1✔
1948
    CHECK_EQUAL(tv.size(), 2);
1✔
1949
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1950
    CHECK_EQUAL(tv.get_key(1), keys[3]);
1✔
1951

1952
    q = table->column<Set<Int>>(col_int_set).size() == 4;
1✔
1953
    tv = q.find_all();
1✔
1954
    CHECK_EQUAL(tv.size(), 1);
1✔
1955
    CHECK_EQUAL(tv.get_key(0), keys[1]);
1✔
1956

1957
    q = table->column<Set<Int>>(col_int_set).max() == 100;
1✔
1958
    tv = q.find_all();
1✔
1959
    CHECK_EQUAL(tv.size(), 1);
1✔
1960
    CHECK_EQUAL(tv.get_key(0), keys[2]);
1✔
1961
}
1✔
1962

1963
TEST(Query_SetOfObjects)
1964
{
1✔
1965
    Group g;
1✔
1966

1967
    TableRef table = g.add_table("foo");
1✔
1968
    TableRef table_bar = g.add_table("bar");
1✔
1969

1970
    std::vector<ObjKey> bar_keys;
1✔
1971
    auto col_string = table_bar->add_column(type_String, "name");
1✔
1972
    table_bar->create_objects(3, bar_keys);
1✔
1973
    table_bar->get_object(bar_keys[0]).set(col_string, "zero");
1✔
1974
    table_bar->get_object(bar_keys[1]).set(col_string, "one");
1✔
1975
    table_bar->get_object(bar_keys[2]).set(col_string, "two");
1✔
1976

1977
    auto col_obj_set = table->add_column_set(*table_bar, "objects");
1✔
1978
    std::vector<ObjKey> keys;
1✔
1979

1980
    table->create_objects(4, keys);
1✔
1981

1982
    auto set_values = [](Set<ObjKey> set, const std::vector<ObjKey>& value_list) {
3✔
1983
        for (auto val : value_list)
3✔
1984
            set.insert(val);
6✔
1985
    };
3✔
1986

1987
    set_values(table->get_object(keys[0]).get_set<ObjKey>(col_obj_set), {bar_keys[0], bar_keys[1]});
1✔
1988
    set_values(table->get_object(keys[1]).get_set<ObjKey>(col_obj_set), {bar_keys[2]});
1✔
1989
    set_values(table->get_object(keys[2]).get_set<ObjKey>(col_obj_set), {bar_keys[0], bar_keys[1], bar_keys[2]});
1✔
1990

1991
    Query q = table->where().links_to(col_obj_set, bar_keys[0]);
1✔
1992
    auto tv = q.find_all();
1✔
1993
    CHECK_EQUAL(tv.size(), 2);
1✔
1994
    CHECK_EQUAL(tv.get_key(0), keys[0]);
1✔
1995
    CHECK_EQUAL(tv.get_key(1), keys[2]);
1✔
1996

1997
    q = table->where().links_to(col_obj_set, {bar_keys[0], bar_keys[2]});
1✔
1998
    tv = q.find_all();
1✔
1999
    CHECK_EQUAL(tv.size(), 3);
1✔
2000
    CHECK_EQUAL(tv.get_key(0), keys[0]);
1✔
2001
    CHECK_EQUAL(tv.get_key(1), keys[1]);
1✔
2002
    CHECK_EQUAL(tv.get_key(2), keys[2]);
1✔
2003

2004
    q = table->column<Set<ObjKey>>(col_obj_set).size() == 3;
1✔
2005
    tv = q.find_all();
1✔
2006
    CHECK_EQUAL(tv.size(), 1);
1✔
2007
    CHECK_EQUAL(tv.get_key(0), keys[2]);
1✔
2008
}
1✔
2009

2010
template <typename T>
2011
struct AggregateValues {
2012
    static std::vector<T> values()
2013
    {
6✔
2014
        std::vector<T> values = {std::numeric_limits<T>::lowest(), T{-1}, T{0}, T{1}, std::numeric_limits<T>::max()};
6✔
2015
        if (std::numeric_limits<T>::has_quiet_NaN) {
6✔
2016
            values.push_back(std::numeric_limits<T>::quiet_NaN());
4✔
2017
        }
4✔
2018
        return values;
6✔
2019
    }
6✔
2020
    using OptionalT = util::Optional<T>;
2021
    static const constexpr util::None null = util::none;
2022
};
2023

2024
template <>
2025
struct AggregateValues<Decimal128> {
2026
    static std::vector<Decimal128> values()
2027
    {
2✔
2028
        return {std::numeric_limits<Decimal128>::lowest(), Decimal128{-1}, Decimal128{0}, Decimal128{1},
2✔
2029
                std::numeric_limits<Decimal128>::max()};
2✔
2030
    }
2✔
2031
    using OptionalT = Decimal128;
2032
    static const constexpr realm::null null = realm::null();
2033
};
2034

2035
template <>
2036
struct AggregateValues<Timestamp> {
2037
    static std::vector<Timestamp> values()
2038
    {
2✔
2039
        return {std::numeric_limits<Timestamp>::lowest(), Timestamp{-1, 0}, Timestamp{0, 0}, Timestamp{1, 0},
2✔
2040
                std::numeric_limits<Timestamp>::max()};
2✔
2041
    }
2✔
2042
    using OptionalT = Timestamp;
2043
    static const constexpr realm::null null = realm::null();
2044
};
2045

2046
template <typename T>
2047
ColKey generate_all_combinations(Table& table)
2048
{
5✔
2049
    using OptionalT = typename AggregateValues<T>::OptionalT;
5✔
2050
    auto values = AggregateValues<T>::values();
5✔
2051
    size_t n = values.size() + 1;
5✔
2052
    auto col = table.add_column_list(ColumnTypeTraits<T>::id, "col", true);
5✔
2053

2054
    // Add a row for each permutation of k=1..n values
2055
    for (size_t k = 1; k <= n; ++k) {
37✔
2056
        // Loop over each possible selection of k different values
2057
        std::vector<bool> selector(n);
32✔
2058
        std::fill(selector.begin(), selector.begin() + k, true);
32✔
2059
        do {
443✔
2060
            std::vector<OptionalT> selected_values;
443✔
2061
            for (size_t i = 0; i < n; i++) {
3,355✔
2062
                if (selector[i]) {
2,912✔
2063
                    if (i == 0)
1,472✔
2064
                        selected_values.push_back(AggregateValues<T>::null);
224✔
2065
                    else
1,248✔
2066
                        selected_values.push_back(values[i - 1]);
1,248✔
2067
                }
1,472✔
2068
            }
2,912✔
2069

2070
            // Loop over each permutation of the selected values
2071
            REALM_ASSERT(std::is_sorted(selected_values.begin(), selected_values.end()));
443✔
2072
            do {
10,990✔
2073
                auto list = table.create_object().get_list<OptionalT>(col);
10,990✔
2074
                for (auto value : selected_values)
10,990✔
2075
                    list.add(value);
55,582✔
2076
            } while (std::next_permutation(selected_values.begin(), selected_values.end()));
10,990✔
2077
        } while (std::prev_permutation(selector.begin(), selector.end()));
443✔
2078
    }
32✔
2079
    return col;
5✔
2080
}
5✔
2081

2082
template <typename T, typename Value, typename Getter>
2083
void validate_aggregate_results(unit_test::TestContext& test_context, Table& table, ColKey col, Value value,
2084
                                Getter getter)
2085
{
64✔
2086
    auto tv = (getter(table.column<Lst<T>>(col)) == value).find_all();
64✔
2087
    auto not_tv = (getter(table.column<Lst<T>>(col)) != value).find_all();
64✔
2088

2089
    // Verify that all rows are present in one of the TVs and that each row in
2090
    // the TV should have matched the query
2091
    using OptionalT = typename AggregateValues<T>::OptionalT;
64✔
2092
    CHECK_EQUAL(tv.size() + not_tv.size(), table.size());
64✔
2093
    for (size_t i = 0; i < tv.size(); ++i) {
22,044✔
2094
        auto result = getter(tv.get_object(i).template get_list<OptionalT>(col));
21,980✔
2095
        CHECK(result);
21,980✔
2096
        CHECK_EQUAL(*result, Mixed(value));
21,980✔
2097
    }
21,980✔
2098
    for (size_t i = 0; i < not_tv.size(); ++i) {
120,208✔
2099
        auto result = getter(not_tv.get_object(i).template get_list<OptionalT>(col));
120,144✔
2100
        CHECK(result);
120,144✔
2101
        CHECK_NOT_EQUAL(*result, Mixed(value));
120,144✔
2102
    }
120,144✔
2103
}
64✔
2104

2105
TEST_TYPES(Query_ListOfPrimitives_MinMax, int64_t, float, double, Decimal128, Timestamp)
2106
{
5✔
2107
    using T = TEST_TYPE;
5✔
2108
    auto values = AggregateValues<T>::values();
5✔
2109
    Table table;
5✔
2110
    auto col = generate_all_combinations<T>(table);
5✔
2111

2112
    auto min = [](auto&& list) {
71,126✔
2113
        return list.min();
71,126✔
2114
    };
71,126✔
2115
    validate_aggregate_results<T>(test_context, table, col, null(), min);
5✔
2116
    for (auto value : values)
5✔
2117
        validate_aggregate_results<T>(test_context, table, col, value, min);
27✔
2118

2119
    auto max = [](auto&& list) {
71,126✔
2120
        return list.max();
71,126✔
2121
    };
71,126✔
2122
    validate_aggregate_results<T>(test_context, table, col, null(), max);
5✔
2123
    for (auto value : values)
5✔
2124
        validate_aggregate_results<T>(test_context, table, col, value, max);
27✔
2125
}
5✔
2126

2127
TEST_TYPES(Query_StringIndexCommonPrefix, std::true_type, std::false_type)
2128
{
2✔
2129
    Group group;
2✔
2130
    TableRef table = group.add_table("test");
2✔
2131
    auto col_str = table->add_column(type_String, "first");
2✔
2132
    table->add_search_index(col_str);
2✔
2133
    if (TEST_TYPE::value == true) {
2✔
2134
        table->enumerate_string_column(col_str);
1✔
2135
    }
1✔
2136

2137
    auto test_prefix_find = [&](std::string prefix) {
6✔
2138
        std::string prefix_b = prefix + "b";
6✔
2139
        std::string prefix_c = prefix + "c";
6✔
2140
        std::string prefix_d = prefix + "d";
6✔
2141
        std::string prefix_e = prefix + "e";
6✔
2142
        StringData spb(prefix_b);
6✔
2143
        StringData spc(prefix_c);
6✔
2144
        StringData spd(prefix_d);
6✔
2145
        StringData spe(prefix_e);
6✔
2146

2147
        std::vector<ObjKey> keys;
6✔
2148
        table->create_objects(6, keys);
6✔
2149
        table->get_object(keys[0]).set(col_str, spb);
6✔
2150
        table->get_object(keys[1]).set(col_str, spc);
6✔
2151
        table->get_object(keys[2]).set(col_str, spc);
6✔
2152
        table->get_object(keys[3]).set(col_str, spe);
6✔
2153
        table->get_object(keys[4]).set(col_str, spe);
6✔
2154
        table->get_object(keys[5]).set(col_str, spe);
6✔
2155

2156
        TableView v = table->where().equal(col_str, spb).find_all();
6✔
2157
        CHECK_EQUAL(v.size(), 1);
6✔
2158
        CHECK_EQUAL(v.get_object(0).get_key(), keys[0]);
6✔
2159

2160
        v = table->where().equal(col_str, spc).find_all();
6✔
2161
        CHECK_EQUAL(v.size(), 2);
6✔
2162
        CHECK_EQUAL(v.get_object(0).get_key(), keys[1]);
6✔
2163
        CHECK_EQUAL(v.get_object(1).get_key(), keys[2]);
6✔
2164

2165
        v = table->where().equal(col_str, spd).find_all();
6✔
2166
        CHECK_EQUAL(v.size(), 0);
6✔
2167

2168
        v = table->where().equal(col_str, spe).find_all();
6✔
2169
        CHECK_EQUAL(v.size(), 3);
6✔
2170
        CHECK_EQUAL(v.get_object(0).get_key(), keys[3]);
6✔
2171
        CHECK_EQUAL(v.get_object(1).get_key(), keys[4]);
6✔
2172
        CHECK_EQUAL(v.get_object(2).get_key(), keys[5]);
6✔
2173
    };
6✔
2174

2175
    std::string std_max(StringIndex::s_max_offset, 'a');
2✔
2176
    std::string std_over_max = std_max + "a";
2✔
2177
    std::string std_under_max(StringIndex::s_max_offset >> 1, 'a');
2✔
2178

2179
    test_prefix_find(std_max);
2✔
2180
    test_prefix_find(std_over_max);
2✔
2181
    test_prefix_find(std_under_max);
2✔
2182
}
2✔
2183

2184

2185
TEST(Query_TwoColsEqualVaryWidthAndValues)
2186
{
1✔
2187
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
2188

2189
    std::vector<ObjKey> ints1;
1✔
2190
    std::vector<ObjKey> ints2;
1✔
2191
    std::vector<ObjKey> ints3;
1✔
2192

2193
    std::vector<ObjKey> floats;
1✔
2194
    std::vector<ObjKey> doubles;
1✔
2195

2196
    Table table;
1✔
2197
    auto col_int0 = table.add_column(type_Int, "first1");
1✔
2198
    auto col_int1 = table.add_column(type_Int, "second1");
1✔
2199

2200
    auto col_int2 = table.add_column(type_Int, "first2");
1✔
2201
    auto col_int3 = table.add_column(type_Int, "second2");
1✔
2202

2203
    auto col_int4 = table.add_column(type_Int, "first3");
1✔
2204
    auto col_int5 = table.add_column(type_Int, "second3");
1✔
2205

2206
    auto col_float6 = table.add_column(type_Float, "third");
1✔
2207
    auto col_float7 = table.add_column(type_Float, "fourth");
1✔
2208
    auto col_double8 = table.add_column(type_Double, "fifth");
1✔
2209
    auto col_double9 = table.add_column(type_Double, "sixth");
1✔
2210

2211
#ifdef REALM_DEBUG
1✔
2212
    for (int i = 0; i < REALM_MAX_BPNODE_SIZE * 5; i++) {
5,001✔
2213
#else
2214
    for (int i = 0; i < 50000; i++) {
2215
#endif
2216
        Obj obj = table.create_object();
5,000✔
2217
        ObjKey key = obj.get_key();
5,000✔
2218

2219
        // Important thing to test is different bitwidths because we might use SSE and/or bithacks on 64-bit blocks
2220

2221
        // Both are bytes
2222
        obj.set(col_int0, random.draw_int_mod(100));
5,000✔
2223
        obj.set(col_int1, random.draw_int_mod(100));
5,000✔
2224

2225
        // Second column widest
2226
        obj.set(col_int2, random.draw_int_mod(10));
5,000✔
2227
        obj.set(col_int3, random.draw_int_mod(100));
5,000✔
2228

2229
        // First column widest
2230
        obj.set(col_int4, random.draw_int_mod(100));
5,000✔
2231
        obj.set(col_int5, random.draw_int_mod(10));
5,000✔
2232

2233
        obj.set(col_float6, float(random.draw_int_mod(10)));
5,000✔
2234
        obj.set(col_float7, float(random.draw_int_mod(10)));
5,000✔
2235

2236
        obj.set(col_double8, double(random.draw_int_mod(10)));
5,000✔
2237
        obj.set(col_double9, double(random.draw_int_mod(10)));
5,000✔
2238

2239
        if (obj.get<Int>(col_int0) == obj.get<Int>(col_int1))
5,000✔
2240
            ints1.push_back(key);
49✔
2241

2242
        if (obj.get<Int>(col_int2) == obj.get<Int>(col_int3))
5,000✔
2243
            ints2.push_back(key);
53✔
2244

2245
        if (obj.get<Int>(col_int4) == obj.get<Int>(col_int5))
5,000✔
2246
            ints3.push_back(key);
57✔
2247

2248
        if (obj.get<Float>(col_float6) == obj.get<Float>(col_float7))
5,000✔
2249
            floats.push_back(key);
535✔
2250

2251
        if (obj.get<Double>(col_double8) == obj.get<Double>(col_double9))
5,000✔
2252
            doubles.push_back(key);
521✔
2253
    }
5,000✔
2254

2255
    realm::TableView t1 = table.where().equal(col_int0, col_int1).find_all();
1✔
2256
    realm::TableView t2 = table.where().equal(col_int2, col_int3).find_all();
1✔
2257
    realm::TableView t3 = table.where().equal(col_int4, col_int5).find_all();
1✔
2258

2259
    realm::TableView t4 = table.where().equal(col_float6, col_float7).find_all();
1✔
2260
    realm::TableView t5 = table.where().equal(col_double8, col_double9).find_all();
1✔
2261

2262

2263
    CHECK_EQUAL(ints1.size(), t1.size());
1✔
2264
    for (size_t t = 0; t < ints1.size(); t++)
50✔
2265
        CHECK_EQUAL(ints1[t], t1.get_key(t));
49✔
2266

2267
    CHECK_EQUAL(ints2.size(), t2.size());
1✔
2268
    for (size_t t = 0; t < ints2.size(); t++)
54✔
2269
        CHECK_EQUAL(ints2[t], t2.get_key(t));
53✔
2270

2271
    CHECK_EQUAL(ints3.size(), t3.size());
1✔
2272
    for (size_t t = 0; t < ints3.size(); t++)
58✔
2273
        CHECK_EQUAL(ints3[t], t3.get_key(t));
57✔
2274

2275
    CHECK_EQUAL(floats.size(), t4.size());
1✔
2276
    for (size_t t = 0; t < floats.size(); t++)
536✔
2277
        CHECK_EQUAL(floats[t], t4.get_key(t));
535✔
2278

2279
    CHECK_EQUAL(doubles.size(), t5.size());
1✔
2280
    for (size_t t = 0; t < doubles.size(); t++)
522✔
2281
        CHECK_EQUAL(doubles[t], t5.get_key(t));
521✔
2282
}
1✔
2283

2284
TEST(Query_TwoColsVaryOperators)
2285
{
1✔
2286
    std::vector<size_t> ints1;
1✔
2287
    std::vector<size_t> floats;
1✔
2288
    std::vector<size_t> doubles;
1✔
2289

2290
    Table table;
1✔
2291
    auto col_int0 = table.add_column(type_Int, "first1");
1✔
2292
    auto col_int1 = table.add_column(type_Int, "second1");
1✔
2293

2294
    auto col_float2 = table.add_column(type_Float, "third");
1✔
2295
    auto col_float3 = table.add_column(type_Float, "fourth");
1✔
2296
    auto col_double4 = table.add_column(type_Double, "fifth");
1✔
2297
    auto col_double5 = table.add_column(type_Double, "sixth");
1✔
2298

2299
    Obj obj0 = table.create_object().set_all(5, 10, 5.0f, 10.0f, 5.0, 10.0);
1✔
2300
    Obj obj1 = table.create_object().set_all(10, 5, 10.0f, 5.0f, 10.0, 5.0);
1✔
2301
    table.create_object().set_all(-10, -5, -10.0f, -5.0f, -10.0, -5.0);
1✔
2302

2303
    CHECK_EQUAL(null_key, table.where().equal(col_int0, col_int1).find());
1✔
2304
    CHECK_EQUAL(obj0.get_key(), table.where().not_equal(col_int0, col_int1).find());
1✔
2305
    CHECK_EQUAL(obj0.get_key(), table.where().less(col_int0, col_int1).find());
1✔
2306
    CHECK_EQUAL(obj1.get_key(), table.where().greater(col_int0, col_int1).find());
1✔
2307
    CHECK_EQUAL(obj1.get_key(), table.where().greater_equal(col_int0, col_int1).find());
1✔
2308
    CHECK_EQUAL(obj0.get_key(), table.where().less_equal(col_int0, col_int1).find());
1✔
2309

2310
    CHECK_EQUAL(null_key, table.where().equal(col_float2, col_float3).find());
1✔
2311
    CHECK_EQUAL(obj0.get_key(), table.where().not_equal(col_float2, col_float3).find());
1✔
2312
    CHECK_EQUAL(obj0.get_key(), table.where().less(col_float2, col_float3).find());
1✔
2313
    CHECK_EQUAL(obj1.get_key(), table.where().greater(col_float2, col_float3).find());
1✔
2314
    CHECK_EQUAL(obj1.get_key(), table.where().greater_equal(col_float2, col_float3).find());
1✔
2315
    CHECK_EQUAL(obj0.get_key(), table.where().less_equal(col_float2, col_float3).find());
1✔
2316

2317
    CHECK_EQUAL(null_key, table.where().equal(col_double4, col_double5).find());
1✔
2318
    CHECK_EQUAL(obj0.get_key(), table.where().not_equal(col_double4, col_double5).find());
1✔
2319
    CHECK_EQUAL(obj0.get_key(), table.where().less(col_double4, col_double5).find());
1✔
2320
    CHECK_EQUAL(obj1.get_key(), table.where().greater(col_double4, col_double5).find());
1✔
2321
    CHECK_EQUAL(obj1.get_key(), table.where().greater_equal(col_double4, col_double5).find());
1✔
2322
    CHECK_EQUAL(obj0.get_key(), table.where().less_equal(col_double4, col_double5).find());
1✔
2323
}
1✔
2324

2325

2326
TEST(Query_TwoCols0)
2327
{
1✔
2328
    Table table;
1✔
2329
    auto col0 = table.add_column(type_Int, "first1");
1✔
2330
    auto col1 = table.add_column(type_Int, "second1");
1✔
2331

2332

2333
    for (int i = 0; i < 50; i++) {
51✔
2334
        table.create_object();
50✔
2335
    }
50✔
2336

2337
    realm::TableView t1 = table.where().equal(col0, col1).find_all();
1✔
2338
    CHECK_EQUAL(50, t1.size());
1✔
2339

2340
    realm::TableView t2 = table.where().less(col0, col1).find_all();
1✔
2341
    CHECK_EQUAL(0, t2.size());
1✔
2342
}
1✔
2343

2344

2345
TEST(Query_TwoSameCols)
2346
{
1✔
2347
    Group g;
1✔
2348
    Table& table = *g.add_table("table");
1✔
2349
    auto col_bool0 = table.add_column(type_Bool, "first1");
1✔
2350
    auto col_bool1 = table.add_column(type_Bool, "first2");
1✔
2351
    auto col_date2 = table.add_column(type_Timestamp, "second1");
1✔
2352
    auto col_date3 = table.add_column(type_Timestamp, "second2");
1✔
2353
    auto col_str4 = table.add_column(type_String, "third1");
1✔
2354
    auto col_str5 = table.add_column(type_String, "third2");
1✔
2355
    auto col_obj1 = table.add_column(table, "obj1");
1✔
2356
    auto col_obj2 = table.add_column(table, "obj2");
1✔
2357

2358
    Timestamp d1(200, 0);
1✔
2359
    Timestamp d2(300, 0);
1✔
2360
    Obj obj0 = table.create_object();
1✔
2361
    ObjKey key0 = obj0.get_key();
1✔
2362
    obj0.set_all(false, true, d1, d2, "a", "b", key0);
1✔
2363
    ObjKey key1 = table.create_object().set_all(true, true, d2, d2, "b", "b", key0, key0).get_key();
1✔
2364
    table.create_object().set_all(false, true, d1, d2, "a", "b", key0, key1).get_key();
1✔
2365

2366
    Query q1 = table.column<Bool>(col_bool0) == table.column<Bool>(col_bool1);
1✔
2367
    Query q2 = table.column<Timestamp>(col_date2) == table.column<Timestamp>(col_date3);
1✔
2368
    Query q3 = table.column<String>(col_str4) == table.column<String>(col_str5);
1✔
2369
    Query q4 = table.column<Link>(col_obj1) == table.column<Link>(col_obj2);
1✔
2370

2371
    CHECK_EQUAL(key1, q1.find());
1✔
2372
    CHECK_EQUAL(key1, q2.find());
1✔
2373
    CHECK_EQUAL(key1, q3.find());
1✔
2374
    CHECK_EQUAL(key1, q4.find());
1✔
2375
    CHECK_EQUAL(1, q1.count());
1✔
2376
    CHECK_EQUAL(1, q2.count());
1✔
2377
    CHECK_EQUAL(1, q3.count());
1✔
2378
    CHECK_EQUAL(1, q4.count());
1✔
2379

2380
    Query q5 = table.column<Bool>(col_bool0) != table.column<Bool>(col_bool1);
1✔
2381
    Query q6 = table.column<Timestamp>(col_date2) != table.column<Timestamp>(col_date3);
1✔
2382
    Query q7 = table.column<String>(col_str4) != table.column<String>(col_str5);
1✔
2383
    Query q8 = table.column<Link>(col_obj1) != table.column<Link>(col_obj2);
1✔
2384

2385
    CHECK_EQUAL(key0, q5.find());
1✔
2386
    CHECK_EQUAL(key0, q6.find());
1✔
2387
    CHECK_EQUAL(key0, q7.find());
1✔
2388
    CHECK_EQUAL(key0, q8.find());
1✔
2389
    CHECK_EQUAL(2, q5.count());
1✔
2390
    CHECK_EQUAL(2, q6.count());
1✔
2391
    CHECK_EQUAL(2, q7.count());
1✔
2392
    CHECK_EQUAL(2, q8.count());
1✔
2393
}
1✔
2394

2395
static void construct_all_types_table(Table& table)
2396
{
2✔
2397
    table.add_column(type_Int, "int");
2✔
2398
    table.add_column(type_Float, "float");
2✔
2399
    table.add_column(type_Double, "double");
2✔
2400
    table.add_column(type_Decimal, "decimal128");
2✔
2401
    table.add_column(type_Mixed, "mixed");
2✔
2402
    table.add_column(type_String, "string");
2✔
2403

2404
    table.add_column(type_Int, "int?", true);
2✔
2405
    table.add_column(type_Float, "float?", true);
2✔
2406
    table.add_column(type_Double, "double?", true);
2✔
2407
    table.add_column(type_Decimal, "decimal128?", true);
2✔
2408
    table.add_column(type_Mixed, "mixed?", true);
2✔
2409
    table.add_column(type_String, "string?", true);
2✔
2410
}
2✔
2411

2412
TEST(Query_TwoColumnsNumeric)
2413
{
1✔
2414
    Table table;
1✔
2415
    construct_all_types_table(table);
1✔
2416
    TestValueGenerator gen;
1✔
2417

2418
    std::vector<int64_t> ints = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
1✔
2419
    std::vector<float> floats = gen.values_from_int<float>(ints);
1✔
2420
    std::vector<double> doubles = gen.values_from_int<double>(ints);
1✔
2421
    std::vector<Decimal128> decimals = gen.values_from_int<Decimal128>(ints);
1✔
2422
    std::vector<Mixed> mixeds = gen.values_from_int<Mixed>(ints);
1✔
2423
    std::vector<StringData> strings = gen.values_from_int<StringData>(ints);
1✔
2424
    size_t num_rows = ints.size();
1✔
2425
    for (size_t i = 0; i < num_rows; ++i) {
13✔
2426
        table.create_object().set_all(ints[i], floats[i], doubles[i], decimals[i], mixeds[i], strings[i], ints[i],
12✔
2427
                                      floats[i], doubles[i], decimals[i], mixeds[i], strings[i]);
12✔
2428
    }
12✔
2429

2430
    ColKeys columns = table.get_column_keys();
1✔
2431
    for (size_t i = 0; i < columns.size(); ++i) {
13✔
2432
        for (size_t j = 0; j < columns.size(); ++j) {
156✔
2433
            ColKey lhs = columns[i];
144✔
2434
            ColKey rhs = columns[j];
144✔
2435
            DataType lhs_type = DataType(lhs.get_type());
144✔
2436
            DataType rhs_type = DataType(rhs.get_type());
144✔
2437
            size_t num_expected_matches = num_rows;
144✔
2438
            if ((lhs_type == type_Mixed) != (rhs_type == type_Mixed)) {
144✔
2439
                // Only one prop is mixed
2440
                // see convert_for_test<Mixed>():
2441
                // matches on numerics: 1(int), 3(double), 5(decimal128), 9(int), 11(double)
2442
                num_expected_matches = 5;
40✔
2443
            }
40✔
2444
            if ((lhs_type == type_String) != (rhs_type == type_String)) {
144✔
2445
                // Only one prop is string
2446
                num_expected_matches = 0;
40✔
2447
                if ((lhs_type == type_Mixed) || (rhs_type == type_Mixed)) {
40✔
2448
                    // matches on "string 2" and "string 10"
2449
                    num_expected_matches = 2;
8✔
2450
                }
8✔
2451
            }
40✔
2452
            {
144✔
2453
                size_t actual_matches = table.where().equal(lhs, rhs).count();
144✔
2454
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2455
                if (actual_matches != num_expected_matches) {
144✔
2456
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2457
                              << " == " << table.get_column_name(rhs) << std::endl;
×
2458
                }
×
2459
            }
144✔
2460
            // select some typed query expressions to test as well
2461
            if (lhs_type == type_Int && rhs_type == type_Double) {
144✔
2462
                size_t actual_matches = (table.column<Int>(lhs) == table.column<Double>(rhs)).count();
4✔
2463
                CHECK_EQUAL(num_expected_matches, actual_matches);
4✔
2464
            }
4✔
2465
            {
144✔
2466
                size_t actual_matches = table.where().not_equal(lhs, rhs).count();
144✔
2467
                CHECK_EQUAL(num_rows - num_expected_matches, actual_matches);
144✔
2468
                if (actual_matches != num_rows - num_expected_matches) {
144✔
2469
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2470
                              << " != " << table.get_column_name(rhs) << std::endl;
×
2471
                }
×
2472
            }
144✔
2473
            {
144✔
2474
                size_t actual_matches = table.where().greater_equal(lhs, rhs).count();
144✔
2475
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2476
                if (actual_matches != num_expected_matches) {
144✔
2477
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2478
                              << " >= " << table.get_column_name(rhs) << std::endl;
×
2479
                }
×
2480
            }
144✔
2481
            {
144✔
2482
                size_t actual_matches = table.where().less_equal(lhs, rhs).count();
144✔
2483
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2484
                if (actual_matches != num_expected_matches) {
144✔
2485
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2486
                              << " <= " << table.get_column_name(rhs) << std::endl;
×
2487
                }
×
2488
            }
144✔
2489
            {
144✔
2490
                num_expected_matches = 0;
144✔
2491
                size_t actual_matches = table.where().greater(lhs, rhs).count();
144✔
2492
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2493
                if (actual_matches != num_expected_matches) {
144✔
2494
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs) << " > "
×
2495
                              << table.get_column_name(rhs) << std::endl;
×
2496
                }
×
2497
            }
144✔
2498
            {
144✔
2499
                num_expected_matches = 0;
144✔
2500
                size_t actual_matches = table.where().less(lhs, rhs).count();
144✔
2501
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2502
                if (actual_matches != num_expected_matches) {
144✔
2503
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs) << " < "
×
2504
                              << table.get_column_name(rhs) << std::endl;
×
2505
                }
×
2506
            }
144✔
2507
        }
144✔
2508
    }
12✔
2509
}
1✔
2510

2511
TEST(Query_TwoColumnsCrossTypesNullability)
2512
{
1✔
2513
    Table table;
1✔
2514
    construct_all_types_table(table);
1✔
2515
    TestValueGenerator gen;
1✔
2516

2517
    constexpr size_t num_rows = 1;
1✔
2518
    table.create_object(); // add one row of default values, null or zero
1✔
2519

2520
    ColKeys columns = table.get_column_keys();
1✔
2521
    for (size_t i = 0; i < columns.size(); ++i) {
13✔
2522
        for (size_t j = 0; j < columns.size(); ++j) {
156✔
2523
            ColKey lhs = columns[i];
144✔
2524
            ColKey rhs = columns[j];
144✔
2525
            DataType lhs_type = DataType(lhs.get_type());
144✔
2526
            DataType rhs_type = DataType(rhs.get_type());
144✔
2527
            bool both_non_nullable = !lhs.is_nullable() && !rhs.is_nullable();
144✔
2528
            bool are_comparable = Mixed::data_types_are_comparable(lhs_type, rhs_type);
144✔
2529
            size_t num_expected_matches = 0;
144✔
2530
            if (lhs.is_nullable() && rhs.is_nullable()) {
144✔
2531
                num_expected_matches = 1; // both default to null
49✔
2532
            }
49✔
2533
            else if (!lhs.is_nullable() && !rhs.is_nullable()) {
95✔
2534
                if (are_comparable) {
25✔
2535
                    num_expected_matches = 1; // numerics are 0
17✔
2536
                }
17✔
2537
            }
25✔
2538
            {
144✔
2539
                size_t actual_matches = table.where().equal(lhs, rhs).count();
144✔
2540
                CHECK_EQUAL(num_expected_matches, actual_matches);
144✔
2541
                if (actual_matches != num_expected_matches) {
144✔
2542
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2543
                              << " == " << table.get_column_name(rhs) << std::endl;
×
2544
                }
×
2545
            }
144✔
2546
            // select some typed query expressions to test as well
2547
            if (lhs_type == type_Int && rhs_type == type_Double) {
144✔
2548
                size_t actual_matches = (table.column<Int>(lhs) == table.column<Double>(rhs)).count();
4✔
2549
                CHECK_EQUAL(num_expected_matches, actual_matches);
4✔
2550
            }
4✔
2551
            if (lhs_type == type_String && rhs_type == type_Binary) {
144✔
2552
                size_t actual_matches = (table.column<String>(lhs) == table.column<Binary>(rhs)).count();
×
2553
                CHECK_EQUAL(num_expected_matches, actual_matches);
×
2554
            }
×
2555
            {
144✔
2556
                size_t actual_matches = table.where().not_equal(lhs, rhs).count();
144✔
2557
                CHECK_EQUAL(num_rows - num_expected_matches, actual_matches);
144✔
2558
                if (actual_matches != num_rows - num_expected_matches) {
144✔
2559
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2560
                              << " != " << table.get_column_name(rhs) << std::endl;
×
2561
                }
×
2562
            }
144✔
2563
            {
144✔
2564
                size_t expected_gte = num_expected_matches;
144✔
2565
                if (both_non_nullable && lhs_type == type_String && rhs_type == type_Binary) {
144✔
2566
                    expected_gte = num_rows;
×
2567
                }
×
2568
                size_t actual_matches = table.where().greater_equal(lhs, rhs).count();
144✔
2569
                CHECK_EQUAL(expected_gte, actual_matches);
144✔
2570
                if (actual_matches != expected_gte) {
144✔
2571
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2572
                              << " >= " << table.get_column_name(rhs) << std::endl;
×
2573
                }
×
2574
            }
144✔
2575
            {
144✔
2576
                size_t expected_le = num_expected_matches;
144✔
2577
                if (both_non_nullable && lhs_type == type_Binary && rhs_type == type_String) {
144!
2578
                    expected_le = num_rows;
×
2579
                }
×
2580
                size_t actual_matches = table.where().less_equal(lhs, rhs).count();
144✔
2581
                CHECK_EQUAL(expected_le, actual_matches);
144✔
2582
                if (actual_matches != expected_le) {
144✔
2583
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2584
                              << " <= " << table.get_column_name(rhs) << std::endl;
×
2585
                }
×
2586
            }
144✔
2587
            {
144✔
2588
                size_t expected_greater = 0;
144✔
2589
                size_t actual_matches = table.where().greater(lhs, rhs).count();
144✔
2590
                CHECK_EQUAL(expected_greater, actual_matches);
144✔
2591
                if (actual_matches != expected_greater) {
144✔
2592
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs) << " > "
×
2593
                              << table.get_column_name(rhs) << std::endl;
×
2594
                }
×
2595
            }
144✔
2596
            {
144✔
2597
                size_t expected_less = 0;
144✔
2598
                size_t actual_matches = table.where().less(lhs, rhs).count();
144✔
2599
                CHECK_EQUAL(expected_less, actual_matches);
144✔
2600
                if (actual_matches != expected_less) {
144✔
2601
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs) << " < "
×
2602
                              << table.get_column_name(rhs) << std::endl;
×
2603
                }
×
2604
            }
144✔
2605
        }
144✔
2606
    }
12✔
2607
}
1✔
2608

2609
TEST(Query_TwoColumnsCrossTypesNaN)
2610
{
1✔
2611
    // across double/float nullable/non-nullable combinations
2612
    // verify query comparisons for: NaN == NaN, null == null, NaN != null
2613
    Table table;
1✔
2614
    table.add_column(type_Float, "float");
1✔
2615
    table.add_column(type_Double, "double");
1✔
2616
    table.add_column(type_Float, "float?", true);
1✔
2617
    table.add_column(type_Double, "double?", true);
1✔
2618

2619
    CHECK(std::numeric_limits<double>::has_quiet_NaN);
1✔
2620
    CHECK(std::numeric_limits<float>::has_quiet_NaN);
1✔
2621
    double nan_d = std::numeric_limits<double>::quiet_NaN();
1✔
2622
    float nan_f = std::numeric_limits<float>::quiet_NaN();
1✔
2623
    util::Optional<double> null_d;
1✔
2624
    util::Optional<float> null_f;
1✔
2625
    table.create_object().set_all(nan_f, nan_d, null_f, null_d);
1✔
2626
    table.create_object().set_all(nan_f, nan_d, nan_f, nan_d);
1✔
2627
    ColKeys columns = table.get_column_keys();
1✔
2628
    for (size_t i = 0; i < columns.size(); ++i) {
5✔
2629
        for (size_t j = 0; j < columns.size(); ++j) {
20✔
2630
            ColKey lhs = columns[i];
16✔
2631
            ColKey rhs = columns[j];
16✔
2632
            bool same_nullablity = lhs.is_nullable() == rhs.is_nullable();
16✔
2633
            size_t num_expected_matches = same_nullablity ? 2 : 1;
16✔
2634
            {
16✔
2635
                size_t actual_matches = table.where().equal(lhs, rhs).count();
16✔
2636
                CHECK_EQUAL(num_expected_matches, actual_matches);
16✔
2637
                if (actual_matches != num_expected_matches) {
16✔
2638
                    std::cout << "failure comparing columns: " << table.get_column_name(lhs)
×
2639
                              << " == " << table.get_column_name(rhs) << std::endl;
×
2640
                }
×
2641
            }
16✔
2642
        }
16✔
2643
    }
4✔
2644
}
1✔
2645

2646
TEST(Query_TwoColumnsDifferentTables)
2647
{
1✔
2648
    Group g;
1✔
2649
    auto table_a = g.add_table("table a");
1✔
2650
    auto table_b = g.add_table("table b");
1✔
2651
    ColKey col_a = table_a->add_column(type_Float, "float");
1✔
2652
    ColKey col_b = table_b->add_column(type_Float, "float");
1✔
2653
    ColKey col_c = table_b->add_column(type_Float, "another float");
1✔
2654
    table_a->create_object();
1✔
2655
    table_a->create_object();
1✔
2656
    table_b->create_object();
1✔
2657

2658
    CHECK_THROW_ANY(table_a->where().equal(col_a, col_b).count());
1✔
2659
    CHECK_THROW_ANY(table_a->where().equal(col_b, col_c).count());
1✔
2660
    CHECK_THROW_ANY((table_a->column<Float>(col_a) == table_b->column<Float>(col_b)).count());
1✔
2661
}
1✔
2662

2663
TEST(Query_DateTest)
2664
{
1✔
2665
    Table table;
1✔
2666
    auto col_date = table.add_column(type_Timestamp, "second1");
1✔
2667

2668
    for (int i = 0; i < 9; i++) {
10✔
2669
        table.create_object().set(col_date, Timestamp(i * 1000, i));
9✔
2670
    }
9✔
2671

2672
    Query q = table.where().equal(col_date, Timestamp(5000, 5));
1✔
2673
    CHECK_EQUAL(1, q.count());
1✔
2674
    TableView tv = q.find_all();
1✔
2675
    CHECK_EQUAL(1, tv.size());
1✔
2676
}
1✔
2677

2678
TEST(Query_TwoColsNoRows)
2679
{
1✔
2680
    Table table;
1✔
2681
    auto col0 = table.add_column(type_Int, "first1");
1✔
2682
    auto col1 = table.add_column(type_Int, "second1");
1✔
2683

2684
    CHECK_EQUAL(null_key, table.where().equal(col0, col1).find());
1✔
2685
    CHECK_EQUAL(null_key, table.where().not_equal(col0, col1).find());
1✔
2686
}
1✔
2687

2688

2689
TEST(Query_Huge)
2690
{
1✔
2691
    Random random;
1✔
2692

2693
#if TEST_DURATION == 0
1✔
2694
    for (int N = 0; N < 1; N++) {
2✔
2695
#elif TEST_DURATION == 1
2696
    for (int N = 0; N < 100; N++) {
2697
#elif TEST_DURATION == 2
2698
    for (int N = 0; N < 1000; N++) {
2699
#elif TEST_DURATION == 3
2700
    for (int N = 0; N < 10000; N++) {
2701
#endif
2702

2703
        // Makes you reproduce a bug in a certain run, without having
2704
        // to run all successive runs
2705
        random.seed(N + 123);
1✔
2706

2707
        Table tt;
1✔
2708
        auto col_str0 = tt.add_column(type_String, "1");
1✔
2709
        auto col_str1 = tt.add_column(type_String, "2");
1✔
2710
        auto col_int2 = tt.add_column(type_Int, "3");
1✔
2711

2712
        TableView v;
1✔
2713
        bool long1 = false;
1✔
2714
        bool long2 = false;
1✔
2715

2716
        size_t mdist1 = 1;
1✔
2717
        size_t mdist2 = 1;
1✔
2718
        size_t mdist3 = 1;
1✔
2719

2720
        std::string first;
1✔
2721
        std::string second;
1✔
2722
        int64_t third;
1✔
2723

2724
        size_t res1 = 0;
1✔
2725
        size_t res2 = 0;
1✔
2726
        size_t res3 = 0;
1✔
2727
        size_t res4 = 0;
1✔
2728
        size_t res5 = 0;
1✔
2729
        size_t res6 = 0;
1✔
2730
        size_t res7 = 0;
1✔
2731
        size_t res8 = 0;
1✔
2732

2733
        size_t limit;
1✔
2734
        if (random.draw_bool())
1✔
2735
            limit = random.draw_int_mod(5000);
1✔
2736
        else
×
2737
            limit = size_t(-1);
×
2738

2739

2740
        size_t blocksize = random.draw_int_mod(800) + 1;
1✔
2741

2742
        for (size_t row = 0; row < 3000; row++) {
3,001✔
2743

2744
            if (row % blocksize == 0) {
3,000✔
2745
                long1 = random.draw_bool();
14✔
2746
                long2 = random.draw_bool();
14✔
2747

2748
                if (random.draw_bool()) {
14✔
2749
                    mdist1 = random.draw_int(1, 500);
5✔
2750
                    mdist2 = random.draw_int(1, 500);
5✔
2751
                    mdist3 = random.draw_int(1, 500);
5✔
2752
                }
5✔
2753
                else {
9✔
2754
                    mdist1 = random.draw_int(1, 5);
9✔
2755
                    mdist2 = random.draw_int(1, 5);
9✔
2756
                    mdist3 = random.draw_int(1, 5);
9✔
2757
                }
9✔
2758
            }
14✔
2759

2760
            Obj obj = tt.create_object();
3,000✔
2761

2762
            if (long1) {
3,000✔
2763
                if (random.draw_int_mod(mdist1) == 0)
1,374✔
2764
                    first = "longlonglonglonglonglonglong A";
355✔
2765
                else
1,019✔
2766
                    first = "longlonglonglonglonglonglong B";
1,019✔
2767
            }
1,374✔
2768
            else {
1,626✔
2769
                if (random.draw_int_mod(mdist1) == 0)
1,626✔
2770
                    first = "A";
665✔
2771
                else
961✔
2772
                    first = "B";
961✔
2773
            }
1,626✔
2774

2775
            if (long2) {
3,000✔
2776
                if (random.draw_int_mod(mdist2) == 0)
1,603✔
2777
                    second = "longlonglonglonglonglonglong A";
356✔
2778
                else
1,247✔
2779
                    second = "longlonglonglonglonglonglong B";
1,247✔
2780
            }
1,603✔
2781
            else {
1,397✔
2782
                if (random.draw_int_mod(mdist2) == 0)
1,397✔
2783
                    second = "A";
402✔
2784
                else
995✔
2785
                    second = "B";
995✔
2786
            }
1,397✔
2787

2788
            if (random.draw_int_mod(mdist3) == 0)
3,000✔
2789
                third = 1;
799✔
2790
            else
2,201✔
2791
                third = 2;
2,201✔
2792

2793
            obj.set(col_str0, StringData(first));
3,000✔
2794
            obj.set(col_str1, StringData(second));
3,000✔
2795
            obj.set(col_int2, third);
3,000✔
2796

2797
            if ((limit > res1) && (first == "A" && second == "A" && third == 1))
3,000✔
2798
                res1++;
59✔
2799

2800
            if ((limit > res2) && ((first == "A" || second == "A") && third == 1))
3,000✔
2801
                res2++;
418✔
2802

2803
            if ((limit > res3) && (first == "A" && (second == "A" || third == 1)))
3,000✔
2804
                res3++;
280✔
2805

2806
            if ((limit > res4) && (second == "A" && (first == "A" || third == 1)))
3,000✔
2807
                res4++;
341✔
2808

2809
            if ((limit > res5) && (first == "A" || second == "A" || third == 1))
3,000✔
2810
                res5++;
1,317✔
2811

2812
            if ((limit > res6) && (first != "A" && second == "A" && third == 1))
3,000✔
2813
                res6++;
210✔
2814

2815
            if ((limit > res7) && (first != "longlonglonglonglonglonglong A" && second == "A" && third == 1))
3,000✔
2816
                res7++;
250✔
2817

2818
            if ((limit > res8) && (first != "longlonglonglonglonglonglong A" && second == "A" && third == 2))
3,000✔
2819
                res8++;
73✔
2820
        }
3,000✔
2821

2822
        for (size_t t = 0; t < 4; t++) {
5✔
2823

2824
            if (t == 1) {
4✔
2825
                tt.enumerate_string_column(col_str0);
1✔
2826
                tt.enumerate_string_column(col_str1);
1✔
2827
            }
1✔
2828
            else if (t == 2) {
3✔
2829
                tt.add_search_index(col_str0);
1✔
2830
            }
1✔
2831
            else if (t == 3) {
2✔
2832
                tt.add_search_index(col_str1);
1✔
2833
            }
1✔
2834

2835
            v = tt.where().equal(col_str0, "A").equal(col_str1, "A").equal(col_int2, 1).find_all(limit);
4✔
2836
            CHECK_EQUAL(res1, v.size());
4✔
2837

2838
            v = tt.where().equal(col_str1, "A").equal(col_str0, "A").equal(col_int2, 1).find_all(limit);
4✔
2839
            CHECK_EQUAL(res1, v.size());
4✔
2840

2841
            v = tt.where().equal(col_int2, 1).equal(col_str1, "A").equal(col_str0, "A").find_all(limit);
4✔
2842
            CHECK_EQUAL(res1, v.size());
4✔
2843

2844
            v = tt.where()
4✔
2845
                    .group()
4✔
2846
                    .equal(col_str0, "A")
4✔
2847
                    .Or()
4✔
2848
                    .equal(col_str1, "A")
4✔
2849
                    .end_group()
4✔
2850
                    .equal(col_int2, 1)
4✔
2851
                    .find_all(limit);
4✔
2852
            CHECK_EQUAL(res2, v.size());
4✔
2853

2854
            v = tt.where()
4✔
2855
                    .equal(col_str0, "A")
4✔
2856
                    .group()
4✔
2857
                    .equal(col_str1, "A")
4✔
2858
                    .Or()
4✔
2859
                    .equal(col_int2, 1)
4✔
2860
                    .end_group()
4✔
2861
                    .find_all(limit);
4✔
2862
            CHECK_EQUAL(res3, v.size());
4✔
2863

2864
            Query q =
4✔
2865
                tt.where().group().equal(col_str0, "A").Or().equal(col_int2, 1).end_group().equal(col_str1, "A");
4✔
2866
            v = q.find_all(limit);
4✔
2867
            CHECK_EQUAL(res4, v.size());
4✔
2868

2869
            v = tt.where()
4✔
2870
                    .group()
4✔
2871
                    .equal(col_str0, "A")
4✔
2872
                    .Or()
4✔
2873
                    .equal(col_int2, 1)
4✔
2874
                    .end_group()
4✔
2875
                    .equal(col_str1, "A")
4✔
2876
                    .find_all(limit);
4✔
2877
            CHECK_EQUAL(res4, v.size());
4✔
2878

2879
            v = tt.where().equal(col_str0, "A").Or().equal(col_str1, "A").Or().equal(col_int2, 1).find_all(limit);
4✔
2880
            CHECK_EQUAL(res5, v.size());
4✔
2881

2882
            v = tt.where().not_equal(col_str0, "A").equal(col_str1, "A").equal(col_int2, 1).find_all(limit);
4✔
2883
            CHECK_EQUAL(res6, v.size());
4✔
2884

2885
            v = tt.where()
4✔
2886
                    .not_equal(col_str0, "longlonglonglonglonglonglong A")
4✔
2887
                    .equal(col_str1, "A")
4✔
2888
                    .equal(col_int2, 1)
4✔
2889
                    .find_all(limit);
4✔
2890
            CHECK_EQUAL(res7, v.size());
4✔
2891

2892
            v = tt.where()
4✔
2893
                    .not_equal(col_str0, "longlonglonglonglonglonglong A")
4✔
2894
                    .equal(col_str1, "A")
4✔
2895
                    .equal(col_int2, 2)
4✔
2896
                    .find_all(limit);
4✔
2897
            CHECK_EQUAL(res8, v.size());
4✔
2898
        }
4✔
2899
    }
1✔
2900
}
1✔
2901

2902

2903
TEST(Query_OnTableView_where)
2904
{
1✔
2905
    Random random;
1✔
2906

2907
    for (int iter = 0; iter < 50 * (1 + TEST_DURATION * TEST_DURATION); iter++) {
51✔
2908
        random.seed(164);
50✔
2909
        Table oti;
50✔
2910
        auto col = oti.add_column(type_Int, "1");
50✔
2911

2912
        size_t cnt1 = 0;
50✔
2913
        size_t cnt0 = 0;
50✔
2914
        size_t limit = random.draw_int_max(REALM_MAX_BPNODE_SIZE * 10);
50✔
2915

2916
        for (size_t i = 0; i < REALM_MAX_BPNODE_SIZE * 10; i++) {
500,050✔
2917
            int v = random.draw_int_mod(3);
500,000✔
2918

2919
            if (v == 1 && cnt0 < limit)
500,000✔
2920
                cnt1++;
89,000✔
2921

2922
            if (v != 0)
500,000✔
2923
                cnt0++;
331,700✔
2924

2925
            oti.create_object().set(col, v);
500,000✔
2926
        }
500,000✔
2927

2928
        TableView v = oti.where().not_equal(col, 0).find_all(limit);
50✔
2929
        size_t cnt2 = oti.where(&v).equal(col, 1).count();
50✔
2930

2931
        CHECK_EQUAL(cnt1, cnt2);
50✔
2932
    }
50✔
2933
}
1✔
2934

2935
TEST_IF(Query_StrIndex3, TEST_DURATION > 0)
2936
{
×
2937
    // Create two columns where query match-density varies alot throughout the rows. This forces the query engine to
2938
    // jump back and forth between the two conditions and test edge cases in these transitions. Tests combinations of
2939
    // linear scan, enum and index
2940

2941
    Random random(random_int<unsigned long>()); // Seed from slow global generator
×
2942

2943
#if REALM_MAX_BPNODE_SIZE > 256
×
2944
    constexpr int node_size = 256;
×
2945
#else
2946
    constexpr int node_size = 4;
2947
#endif
2948

2949
#if defined REALM_DEBUG || REALM_ANDROID
×
2950
    for (int N = 0; N < 4; N++) {
×
2951
#else
2952
    for (int N = 0; N < 20; N++) {
2953
#endif
2954
        Table ttt;
×
2955
        auto col_int = ttt.add_column(type_Int, "1");
×
2956
        auto col_str = ttt.add_column(type_String, "2");
×
2957

2958
        std::vector<ObjKey> vec;
×
2959

2960
#if defined REALM_DEBUG || REALM_ANDROID
×
2961
        for (int i = 0; i < 4; i++) {
×
2962
#else
2963
        for (int i = 0; i < 20; i++) {
2964
#endif
2965
            // 1/128 match probability because we want possibility for a 256 sized leaf to contain 0 matches
2966
            // (important edge case)
2967
            int f1 = random.draw_int_mod(node_size) / 2 + 1;
×
2968
            int f2 = random.draw_int_mod(node_size) / 2 + 1;
×
2969
            bool longstrings = random.chance(1, 5);
×
2970

2971
            // 576 entries with that probability to fill out two concecutive 256 sized leaves with above
2972
            // probability, plus a remainder (edge case)
2973
            for (int j = 0; j < node_size * 2 + node_size / 4; j++) {
×
2974
                if (random.chance(1, f1)) {
×
2975
                    if (random.chance(1, f2)) {
×
2976
                        ObjKey key =
×
2977
                            ttt.create_object().set_all(0, longstrings ? "AAAAAAAAAAAAAAAAAAAAAAAA" : "AA").get_key();
×
2978
                        if (!longstrings) {
×
2979
                            vec.push_back(key);
×
2980
                        }
×
2981
                    }
×
2982
                    else {
×
2983
                        ttt.create_object().set_all(0, "BB");
×
2984
                    }
×
2985
                }
×
2986
                else {
×
2987
                    if (random.chance(1, f2)) {
×
2988
                        ttt.create_object().set_all(1, "AA");
×
2989
                    }
×
2990
                    else {
×
2991
                        ttt.create_object().set_all(1, "BB");
×
2992
                    }
×
2993
                }
×
2994
            }
×
2995
        }
×
2996

2997
        TableView v;
×
2998

2999
        // Both linear scans
3000
        v = ttt.where().equal(col_str, "AA").equal(col_int, 0).find_all();
×
3001
        CHECK_EQUAL(vec.size(), v.size());
×
3002
        for (size_t t = 0; t < vec.size(); t++)
×
3003
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3004

3005
        v = ttt.where().equal(col_int, 0).equal(col_str, "AA").find_all();
×
3006
        CHECK_EQUAL(vec.size(), v.size());
×
3007
        for (size_t t = 0; t < vec.size(); t++)
×
3008
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3009

3010
        ttt.enumerate_string_column(col_str);
×
3011

3012
        // Linear scan over enum, plus linear integer column scan
3013
        v = ttt.where().equal(col_str, "AA").equal(col_int, 0).find_all();
×
3014
        CHECK_EQUAL(vec.size(), v.size());
×
3015
        for (size_t t = 0; t < vec.size(); t++)
×
3016
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3017

3018
        v = ttt.where().equal(col_int, 0).equal(col_str, "AA").find_all();
×
3019
        CHECK_EQUAL(vec.size(), v.size());
×
3020
        for (size_t t = 0; t < vec.size(); t++)
×
3021
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3022

3023
        ttt.add_search_index(col_str);
×
3024

3025
        // Index lookup, plus linear integer column scan
3026
        v = ttt.where().equal(col_str, "AA").equal(col_int, 0).find_all();
×
3027
        CHECK_EQUAL(vec.size(), v.size());
×
3028
        for (size_t t = 0; t < vec.size(); t++)
×
3029
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3030

3031
        v = ttt.where().equal(col_int, 0).equal(col_str, "AA").find_all();
×
3032
        CHECK_EQUAL(vec.size(), v.size());
×
3033
        for (size_t t = 0; t < vec.size(); t++)
×
3034
            CHECK_EQUAL(vec[t], v.get_key(t));
×
3035
    }
×
3036
}
×
3037

3038
TEST(Query_StrIndex2)
3039
{
1✔
3040
    Table ttt;
1✔
3041
    ttt.add_column(type_Int, "1");
1✔
3042
    auto col_str = ttt.add_column(type_String, "2");
1✔
3043

3044
    int64_t s;
1✔
3045

3046
    for (int i = 0; i < 100; ++i) {
101✔
3047
        ttt.create_object().set_all(1, "AA");
100✔
3048
    }
100✔
3049
    ttt.create_object().set_all(1, "BB");
1✔
3050
    ttt.add_search_index(col_str);
1✔
3051

3052
    s = ttt.where().equal(col_str, "AA").count();
1✔
3053
    CHECK_EQUAL(100, s);
1✔
3054

3055
    s = ttt.where().equal(col_str, "BB").count();
1✔
3056
    CHECK_EQUAL(1, s);
1✔
3057

3058
    s = ttt.where().equal(col_str, "CC").count();
1✔
3059
    CHECK_EQUAL(0, s);
1✔
3060
}
1✔
3061

3062
TEST(Query_StrEnum)
3063
{
1✔
3064
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
3065
    Table ttt;
1✔
3066
    ttt.add_column(type_Int, "1");
1✔
3067
    auto col_str = ttt.add_column(type_String, "2");
1✔
3068

3069
    int aa;
1✔
3070
    int64_t s;
1✔
3071

3072
    for (int i = 0; i < 100; ++i) {
101✔
3073
        ttt.clear();
100✔
3074
        aa = 0;
100✔
3075
        for (size_t t = 0; t < REALM_MAX_BPNODE_SIZE * 2; ++t) {
200,100✔
3076
            if (random.chance(1, 3)) {
200,000✔
3077
                ttt.create_object().set_all(1, "AA");
66,796✔
3078
                ++aa;
66,796✔
3079
            }
66,796✔
3080
            else {
133,204✔
3081
                ttt.create_object().set_all(1, "BB");
133,204✔
3082
            }
133,204✔
3083
        }
200,000✔
3084
        ttt.enumerate_string_column(col_str);
100✔
3085
        s = ttt.where().equal(col_str, "AA").count();
100✔
3086
        CHECK_EQUAL(aa, s);
100✔
3087
    }
100✔
3088
}
1✔
3089

3090
TEST(Query_StrIndex)
3091
{
1✔
3092
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
3093

3094
#ifdef REALM_DEBUG
1✔
3095
    size_t itera = 4;
1✔
3096
    size_t iterb = 100;
1✔
3097
#else
3098
    size_t itera = 100;
3099
    size_t iterb = 2000;
3100
#endif
3101

3102
    int aa;
1✔
3103
    int64_t s;
1✔
3104

3105
    for (size_t i = 0; i < itera; i++) {
5✔
3106
        Table ttt;
4✔
3107
        ttt.add_column(type_Int, "1");
4✔
3108
        auto str_col = ttt.add_column(type_String, "2");
4✔
3109

3110
        aa = 0;
4✔
3111
        for (size_t t = 0; t < iterb; t++) {
404✔
3112
            if (random.chance(1, 3)) {
400✔
3113
                ttt.create_object().set_all(1, "AA");
146✔
3114
                aa++;
146✔
3115
            }
146✔
3116
            else {
254✔
3117
                ttt.create_object().set_all(1, "BB");
254✔
3118
            }
254✔
3119
        }
400✔
3120

3121
        s = ttt.where().equal(str_col, "AA").count();
4✔
3122
        CHECK_EQUAL(aa, s);
4✔
3123

3124
        ttt.enumerate_string_column(str_col);
4✔
3125
        s = ttt.where().equal(str_col, "AA").count();
4✔
3126
        CHECK_EQUAL(aa, s);
4✔
3127

3128
        ttt.add_search_index(str_col);
4✔
3129
        s = ttt.where().equal(str_col, "AA").count();
4✔
3130
        CHECK_EQUAL(aa, s);
4✔
3131
    }
4✔
3132
}
1✔
3133

3134
TEST(Query_StrIndexUpdating)
3135
{
1✔
3136
    SHARED_GROUP_TEST_PATH(path);
1✔
3137
    std::unique_ptr<Replication> hist(make_in_realm_history());
1✔
3138
    auto sg = DB::create(*hist, path, DBOptions(crypt_key()));
1✔
3139
    auto group = sg->start_write();
1✔
3140

3141
    auto t = group->add_table("table");
1✔
3142
    auto col = t->add_column(type_String, "value");
1✔
3143
    t->add_search_index(col);
1✔
3144
    TableView tv = t->where().equal(col, "").find_all();
1✔
3145
    TableView tv_ins = t->where().equal(col, "", false).find_all();
1✔
3146
    CHECK_EQUAL(tv.size(), 0);
1✔
3147
    CHECK_EQUAL(tv_ins.size(), 0);
1✔
3148
    group->commit_and_continue_as_read();
1✔
3149

3150
    // Queries on indexes have different codepaths for 0, 1, and multiple results,
3151
    // so check each of the 6 possible transitions. The write transactions are
3152
    // required here because otherwise the Query will be using the StringIndex
3153
    // being mutated and will happen to give correct results even if it fails
3154
    // to update all of its internal state.
3155

3156
    // 0 -> 1 result
3157
    group->promote_to_write();
1✔
3158
    t->create_object();
1✔
3159
    group->commit_and_continue_as_read();
1✔
3160
    tv.sync_if_needed();
1✔
3161
    tv_ins.sync_if_needed();
1✔
3162
    CHECK_EQUAL(tv.size(), 1);
1✔
3163
    CHECK_EQUAL(tv_ins.size(), 1);
1✔
3164

3165
    // 1 -> multiple results
3166
    group->promote_to_write();
1✔
3167
    t->create_object();
1✔
3168
    t->create_object();
1✔
3169
    group->commit_and_continue_as_read();
1✔
3170
    tv.sync_if_needed();
1✔
3171
    tv_ins.sync_if_needed();
1✔
3172
    CHECK_EQUAL(tv.size(), 3);
1✔
3173
    CHECK_EQUAL(tv_ins.size(), 3);
1✔
3174

3175
    // multiple -> 1
3176
    group->promote_to_write();
1✔
3177
    t->remove_object(tv.get_key(0));
1✔
3178
    t->remove_object(tv.get_key(1));
1✔
3179
    group->commit_and_continue_as_read();
1✔
3180
    tv.sync_if_needed();
1✔
3181
    tv_ins.sync_if_needed();
1✔
3182
    CHECK_EQUAL(tv.size(), 1);
1✔
3183
    CHECK_EQUAL(tv_ins.size(), 1);
1✔
3184

3185
    // 1 -> 0
3186
    group->promote_to_write();
1✔
3187
    t->remove_object(tv.get_key(0));
1✔
3188
    group->commit_and_continue_as_read();
1✔
3189
    tv.sync_if_needed();
1✔
3190
    tv_ins.sync_if_needed();
1✔
3191
    CHECK_EQUAL(tv.size(), 0);
1✔
3192
    CHECK_EQUAL(tv_ins.size(), 0);
1✔
3193

3194
    // 0 -> multiple
3195
    group->promote_to_write();
1✔
3196
    t->create_object();
1✔
3197
    t->create_object();
1✔
3198
    group->commit_and_continue_as_read();
1✔
3199
    tv.sync_if_needed();
1✔
3200
    tv_ins.sync_if_needed();
1✔
3201
    CHECK_EQUAL(tv.size(), 2);
1✔
3202
    CHECK_EQUAL(tv_ins.size(), 2);
1✔
3203

3204
    // multiple -> 0
3205
    group->promote_to_write();
1✔
3206
    t->remove_object(tv.get_key(0));
1✔
3207
    t->remove_object(tv.get_key(1));
1✔
3208
    group->commit_and_continue_as_read();
1✔
3209
    tv.sync_if_needed();
1✔
3210
    tv_ins.sync_if_needed();
1✔
3211
    CHECK_EQUAL(tv.size(), 0);
1✔
3212
    CHECK_EQUAL(tv_ins.size(), 0);
1✔
3213
}
1✔
3214

3215
TEST(Query_GA_Crash)
3216
{
1✔
3217
    GROUP_TEST_PATH(path);
1✔
3218
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
3219
    {
1✔
3220
        Group g;
1✔
3221
        TableRef t = g.add_table("firstevents");
1✔
3222
        auto col_str0 = t->add_column(type_String, "1");
1✔
3223
        auto col_str1 = t->add_column(type_String, "2");
1✔
3224
        auto col_str2 = t->add_column(type_String, "3");
1✔
3225
        t->add_column(type_Int, "4");
1✔
3226
        t->add_column(type_Int, "5");
1✔
3227

3228
        for (size_t i = 0; i < 100; ++i) {
101✔
3229
            int64_t r1 = random.draw_int_mod(100);
100✔
3230
            int64_t r2 = random.draw_int_mod(100);
100✔
3231

3232
            t->create_object().set_all("10", "US", "1.0", r1, r2);
100✔
3233
        }
100✔
3234
        t->enumerate_string_column(col_str0);
1✔
3235
        t->enumerate_string_column(col_str1);
1✔
3236
        t->enumerate_string_column(col_str2);
1✔
3237
        g.write(path);
1✔
3238
    }
1✔
3239

3240
    Group g(path);
1✔
3241
    TableRef t = g.get_table("firstevents");
1✔
3242
    auto col_str1 = t->get_column_key("2");
1✔
3243

3244
    Query q = t->where().equal(col_str1, "US");
1✔
3245

3246
    size_t c1 = 0;
1✔
3247
    for (size_t i = 0; i < 100; ++i)
101✔
3248
        c1 += t->count_string(col_str1, "US");
100✔
3249

3250
    size_t c2 = 0;
1✔
3251
    for (size_t i = 0; i < 100; ++i)
101✔
3252
        c2 += q.count();
100✔
3253

3254
    CHECK_EQUAL(c1, t->size() * 100);
1✔
3255
    CHECK_EQUAL(c1, c2);
1✔
3256
}
1✔
3257

3258
TEST(Query_Float3)
3259
{
1✔
3260
    Table t;
1✔
3261
    auto col_float = t.add_column(type_Float, "1", true);
1✔
3262
    auto col_double = t.add_column(type_Double, "2");
1✔
3263
    auto col_int = t.add_column(type_Int, "3");
1✔
3264

3265
    t.create_object().set_all(float(1.1), double(2.1), 1);
1✔
3266
    t.create_object().set_all(float(1.2), double(2.2), 2);
1✔
3267
    t.create_object().set_all(float(1.3), double(2.3), 3);
1✔
3268
    t.create_object().set_all(float(1.4), double(2.4), 4); // match
1✔
3269
    t.create_object().set_all(float(1.5), double(2.5), 5); // match
1✔
3270
    t.create_object().set_all(float(1.6), double(2.6), 6); // match
1✔
3271
    t.create_object().set_all(float(1.7), double(2.7), 7);
1✔
3272
    t.create_object().set_all(nanf("7"), double(2.8), 8);
1✔
3273
    t.create_object().set_all(float(1.9), double(2.9), 9);
1✔
3274

3275
    Query q1 = t.where().greater(col_float, 1.35f).less(col_double, 2.65);
1✔
3276
    auto a1 = q1.sum(col_int);
1✔
3277
    CHECK_EQUAL(15, a1);
1✔
3278

3279
    Query q2 = t.where().less(col_double, 2.65).greater(col_float, 1.35f);
1✔
3280
    auto a2 = q2.sum(col_int);
1✔
3281
    CHECK_EQUAL(15, a2);
1✔
3282

3283
    Query q3 = t.where().less(col_double, 2.65).greater(col_float, 1.35f);
1✔
3284
    auto a3 = q3.sum(col_float);
1✔
3285
    double sum3 = double(1.4f) + double(1.5f) + double(1.6f);
1✔
3286
    CHECK_EQUAL(sum3, a3);
1✔
3287

3288
    Query q4 = t.where().greater(col_float, 1.35f).less(col_double, 2.65);
1✔
3289
    auto a4 = q4.sum(col_float);
1✔
3290
    CHECK_EQUAL(sum3, a4);
1✔
3291

3292
    Query q5 = t.where().greater_equal(col_int, 4).less(col_double, 2.65);
1✔
3293
    auto a5 = q5.sum(col_float);
1✔
3294
    CHECK_EQUAL(sum3, a5);
1✔
3295

3296
    Query q6 = t.where().less(col_double, 2.65).greater_equal(col_int, 4);
1✔
3297
    auto a6 = q6.sum(col_float);
1✔
3298
    CHECK_EQUAL(sum3, a6);
1✔
3299

3300
    Query q7 = t.where().greater(col_int, 3).less(col_int, 7);
1✔
3301
    auto a7 = q7.sum(col_int);
1✔
3302
    CHECK_EQUAL(15, a7);
1✔
3303
    Query q8 = t.where().greater(col_int, 3).less(col_int, 7);
1✔
3304
    auto a8 = q8.sum(col_int);
1✔
3305
    CHECK_EQUAL(15, a8);
1✔
3306

3307
    q8 = t.where().greater(col_int, 3);
1✔
3308
    float f = float(q8.sum(col_float)->get_double());
1✔
3309
    CHECK_EQUAL(8.1f, f);
1✔
3310
}
1✔
3311

3312

3313
TEST(Query_Float3_where)
3314
{
1✔
3315
    // Sum on query on tableview
3316
    Table t;
1✔
3317
    auto col_float = t.add_column(type_Float, "1");
1✔
3318
    auto col_double = t.add_column(type_Double, "2");
1✔
3319
    auto col_int = t.add_column(type_Int, "3");
1✔
3320

3321
    t.create_object().set_all(float(1.1), double(2.1), 1);
1✔
3322
    t.create_object().set_all(float(1.2), double(2.2), 2);
1✔
3323
    t.create_object().set_all(float(1.3), double(2.3), 3);
1✔
3324
    t.create_object().set_all(float(1.4), double(2.4), 4);                     // match
1✔
3325
    t.create_object().set_all(float(1.5), double(2.5), 5);                     // match
1✔
3326
    t.create_object(ObjKey(0xc001ede1b0)).set_all(float(1.6), double(2.6), 6); // match
1✔
3327
    t.create_object().set_all(float(1.7), double(2.7), 7);
1✔
3328
    t.create_object().set_all(float(1.8), double(2.8), 8);
1✔
3329
    t.create_object().set_all(float(1.9), double(2.9), 9);
1✔
3330

3331
    TableView v = t.where().find_all();
1✔
3332

3333
    Query q1 = t.where(&v).greater(col_float, 1.35f).less(col_double, 2.65);
1✔
3334
    auto a1 = q1.sum(col_int);
1✔
3335
    CHECK_EQUAL(15, a1);
1✔
3336

3337
    ObjKey k;
1✔
3338
    a1 = q1.max(col_int, &k);
1✔
3339
    CHECK_EQUAL(k.value, 0xc001ede1b0);
1✔
3340
    CHECK_EQUAL(a1, 6);
1✔
3341

3342
    Query q2 = t.where(&v).less(col_double, 2.65).greater(col_float, 1.35f);
1✔
3343
    auto a2 = q2.sum(col_int);
1✔
3344
    CHECK_EQUAL(15, a2);
1✔
3345

3346
    Query q3 = t.where(&v).less(col_double, 2.65).greater(col_float, 1.35f);
1✔
3347
    auto a3 = q3.sum(col_float)->get_double();
1✔
3348
    double sum3 = double(1.4f) + double(1.5f) + double(1.6f);
1✔
3349
    CHECK_EQUAL(sum3, a3);
1✔
3350

3351
    Query q4 = t.where(&v).greater(col_float, 1.35f).less(col_double, 2.65);
1✔
3352
    auto a4 = q4.sum(col_float)->get_double();
1✔
3353
    CHECK_EQUAL(sum3, a4);
1✔
3354

3355
    Query q5 = t.where(&v).greater_equal(col_int, 4).less(col_double, 2.65);
1✔
3356
    auto a5 = q5.sum(col_float)->get_double();
1✔
3357
    CHECK_EQUAL(sum3, a5);
1✔
3358

3359
    Query q6 = t.where(&v).less(col_double, 2.65).greater_equal(col_int, 4);
1✔
3360
    auto a6 = q6.sum(col_float)->get_double();
1✔
3361
    CHECK_EQUAL(sum3, a6);
1✔
3362

3363
    Query q7 = t.where(&v).greater(col_int, 3).less(col_int, 7);
1✔
3364
    auto a7 = q7.sum(col_int);
1✔
3365
    CHECK_EQUAL(15, a7);
1✔
3366
    Query q8 = t.where(&v).greater(col_int, 3).less(col_int, 7);
1✔
3367
    auto a8 = q8.sum(col_int);
1✔
3368
    CHECK_EQUAL(15, a8);
1✔
3369
}
1✔
3370

3371
TEST(Query_TableViewSum)
3372
{
1✔
3373
    Table t;
1✔
3374

3375
    auto col_int = t.add_column(type_Int, "3");
1✔
3376

3377
    for (int i = 0; i < 10; i++) {
11✔
3378
        t.create_object().set(col_int, i + 1);
10✔
3379
    }
10✔
3380

3381
    Query q1 = t.where().between(col_int, 5, 9);
1✔
3382
    TableView tv1 = q1.find_all();
1✔
3383
    CHECK_EQUAL(5 + 6 + 7 + 8 + 9, *tv1.sum(col_int));
1✔
3384
}
1✔
3385

3386
TEST(Query_JavaMinimumCrash)
3387
{
1✔
3388
    // Test that triggers a bug that was discovered through Java interface and has been fixed
3389
    Table ttt;
1✔
3390

3391
    auto col_str = ttt.add_column(type_String, "1");
1✔
3392
    ttt.add_column(type_String, "2");
1✔
3393
    auto col_int = ttt.add_column(type_Int, "3");
1✔
3394

3395
    ttt.create_object().set_all("Joe", "John", 1);
1✔
3396
    ttt.create_object().set_all("Jane", "Doe", 2);
1✔
3397
    ttt.create_object().set_all("Bob", "Hanson", 3);
1✔
3398

3399
    Query q1 = ttt.where().equal(col_str, "Joe").Or().equal(col_str, "Bob");
1✔
3400
    CHECK_EQUAL(1, *q1.min(col_int));
1✔
3401
}
1✔
3402

3403

3404
TEST(Query_Float4)
3405
{
1✔
3406
    Table t;
1✔
3407

3408
    auto col_float = t.add_column(type_Float, "1");
1✔
3409
    auto col_double = t.add_column(type_Double, "2");
1✔
3410
    t.add_column(type_Int, "3");
1✔
3411

3412
    t.create_object().set_all(std::numeric_limits<float>::max(), std::numeric_limits<double>::max(), 11111);
1✔
3413
    t.create_object().set_all(std::numeric_limits<float>::infinity(), std::numeric_limits<double>::infinity(), 11111);
1✔
3414
    t.create_object().set_all(12345.0f, 12345.0, 11111);
1✔
3415

3416
    Query q1 = t.where();
1✔
3417
    auto a1 = q1.max(col_float)->get_float();
1✔
3418
    auto a2 = q1.max(col_double)->get_double();
1✔
3419
    CHECK_EQUAL(std::numeric_limits<float>::infinity(), a1);
1✔
3420
    CHECK_EQUAL(std::numeric_limits<double>::infinity(), a2);
1✔
3421

3422

3423
    Query q2 = t.where();
1✔
3424
    auto a3 = q1.min(col_float);
1✔
3425
    auto a4 = q1.min(col_double);
1✔
3426
    CHECK_EQUAL(12345.0, a3);
1✔
3427
    CHECK_EQUAL(12345.0, a4);
1✔
3428
}
1✔
3429

3430

3431
TEST(Query_Float)
3432
{
1✔
3433
    Table t;
1✔
3434
    auto col_float = t.add_column(type_Float, "1");
1✔
3435
    auto col_double = t.add_column(type_Double, "2");
1✔
3436

3437
    ObjKey k0 = t.create_object().set_all(1.10f, 2.20).get_key();
1✔
3438
    ObjKey k1 = t.create_object().set_all(1.13f, 2.21).get_key();
1✔
3439
    t.create_object().set_all(1.13f, 2.22);
1✔
3440
    t.create_object().set_all(1.10f, 2.20).get_key();
1✔
3441
    ObjKey k4 = t.create_object().set_all(1.20f, 3.20).get_key();
1✔
3442

3443
    // Test find_all()
3444
    TableView v = t.where().equal(col_float, 1.13f).find_all();
1✔
3445
    CHECK_EQUAL(2, v.size());
1✔
3446
    CHECK_EQUAL(1.13f, v[0].get<float>(col_float));
1✔
3447
    CHECK_EQUAL(1.13f, v[1].get<float>(col_float));
1✔
3448

3449
    TableView v2 = t.where().equal(col_double, 3.2).find_all();
1✔
3450
    CHECK_EQUAL(1, v2.size());
1✔
3451
    CHECK_EQUAL(3.2, v2[0].get<double>(col_double));
1✔
3452

3453
    // Test operators (and count)
3454
    CHECK_EQUAL(2, t.where().equal(col_float, 1.13f).count());
1✔
3455
    CHECK_EQUAL(3, t.where().not_equal(col_float, 1.13f).count());
1✔
3456
    CHECK_EQUAL(3, t.where().greater(col_float, 1.1f).count());
1✔
3457
    CHECK_EQUAL(3, t.where().greater_equal(col_float, 1.13f).count());
1✔
3458
    CHECK_EQUAL(4, t.where().less_equal(col_float, 1.13f).count());
1✔
3459
    CHECK_EQUAL(2, t.where().less(col_float, 1.13f).count());
1✔
3460
    CHECK_EQUAL(3, t.where().between(col_float, 1.13f, 1.2f).count());
1✔
3461

3462
    CHECK_EQUAL(2, t.where().equal(col_double, 2.20).count());
1✔
3463
    CHECK_EQUAL(3, t.where().not_equal(col_double, 2.20).count());
1✔
3464
    CHECK_EQUAL(2, t.where().greater(col_double, 2.21).count());
1✔
3465
    CHECK_EQUAL(3, t.where().greater_equal(col_double, 2.21).count());
1✔
3466
    CHECK_EQUAL(4, t.where().less_equal(col_double, 2.22).count());
1✔
3467
    CHECK_EQUAL(3, t.where().less(col_double, 2.22).count());
1✔
3468
    CHECK_EQUAL(4, t.where().between(col_double, 2.20, 2.22).count());
1✔
3469

3470
    double epsilon = std::numeric_limits<double>::epsilon();
1✔
3471

3472
    // ------ Test sum()
3473
    // ... NO conditions
3474
    double sum1_d = 2.20 + 2.21 + 2.22 + 2.20 + 3.20;
1✔
3475
    CHECK_APPROXIMATELY_EQUAL(sum1_d, t.where().sum(col_double)->get_double(), 10 * epsilon);
1✔
3476

3477
    // Note: sum of float is calculated by having a double aggregate to where each float is added
3478
    // (thereby getting casted to double).
3479
    double sum1_f = double(1.10f) + double(1.13f) + double(1.13f) + double(1.10f) + double(1.20f);
1✔
3480
    double res = t.where().sum(col_float)->get_double();
1✔
3481
    CHECK_APPROXIMATELY_EQUAL(sum1_f, res, 10 * epsilon);
1✔
3482

3483
    // ... with conditions
3484
    double sum2_f = double(1.13f) + double(1.20f);
1✔
3485
    double sum2_d = 2.21 + 3.20;
1✔
3486
    Query q2 = t.where().between(col_float, 1.13f, 1.20f).not_equal(col_double, 2.22);
1✔
3487
    CHECK_APPROXIMATELY_EQUAL(sum2_f, q2.sum(col_float)->get_double(), 10 * epsilon);
1✔
3488
    CHECK_APPROXIMATELY_EQUAL(sum2_d, q2.sum(col_double)->get_double(), 10 * epsilon);
1✔
3489

3490
    // ------ Test average()
3491

3492
    // ... NO conditions
3493
    CHECK_APPROXIMATELY_EQUAL(sum1_f / 5, t.where().avg(col_float)->get_double(), 10 * epsilon);
1✔
3494
    CHECK_APPROXIMATELY_EQUAL(sum1_d / 5, t.where().avg(col_double)->get_double(), 10 * epsilon);
1✔
3495
    // ... with conditions
3496
    CHECK_APPROXIMATELY_EQUAL(sum2_f / 2, q2.avg(col_float)->get_double(), 10 * epsilon);
1✔
3497
    CHECK_APPROXIMATELY_EQUAL(sum2_d / 2, q2.avg(col_double)->get_double(), 10 * epsilon);
1✔
3498

3499
    // -------- Test minimum(), maximum()
3500

3501
    ObjKey ndx;
1✔
3502

3503
    // ... NO conditions
3504
    CHECK_EQUAL(1.20f, t.where().max(col_float));
1✔
3505
    t.where().max(col_float, &ndx);
1✔
3506
    CHECK_EQUAL(k4, ndx);
1✔
3507

3508
    CHECK_EQUAL(1.10f, t.where().min(col_float));
1✔
3509
    t.where().min(col_float, &ndx);
1✔
3510
    CHECK_EQUAL(k0, ndx);
1✔
3511

3512
    CHECK_EQUAL(3.20, t.where().max(col_double));
1✔
3513
    CHECK_EQUAL(3.20, t.where().max(col_double, &ndx));
1✔
3514

3515
    CHECK_EQUAL(2.20, t.where().min(col_double));
1✔
3516
    t.where().min(col_double, &ndx);
1✔
3517

3518
    // ... with conditions
3519
    CHECK_EQUAL(1.20f, q2.max(col_float));
1✔
3520
    q2.max(col_float, &ndx);
1✔
3521
    CHECK_EQUAL(k4, ndx);
1✔
3522

3523
    CHECK_EQUAL(1.13f, q2.min(col_float));
1✔
3524
    q2.min(col_float, &ndx);
1✔
3525
    CHECK_EQUAL(k1, ndx);
1✔
3526

3527
    CHECK_EQUAL(3.20, q2.max(col_double));
1✔
3528
    q2.max(col_double, &ndx);
1✔
3529
    CHECK_EQUAL(k4, ndx);
1✔
3530

3531
    CHECK_EQUAL(2.21, q2.min(col_double));
1✔
3532
    q2.min(col_double, &ndx);
1✔
3533
    CHECK_EQUAL(k1, ndx);
1✔
3534
}
1✔
3535

3536

3537
TEST(Query_DoubleCoordinates)
3538
{
1✔
3539
    Group group;
1✔
3540
    TableRef table = group.add_table("test");
1✔
3541

3542
    auto col0 = table->add_column(type_Double, "name");
1✔
3543
    auto col1 = table->add_column(type_Double, "age");
1✔
3544

3545
    size_t expected = 0;
1✔
3546

3547
    for (size_t t = 0; t < 100000; t++) {
100,001✔
3548
        Obj obj = table->create_object().set_all(double((t * 12345) % 1000), double((t * 12345) % 1000));
100,000✔
3549

3550
        if (obj.get<double>(col0) >= 100. && obj.get<double>(col0) <= 110. && obj.get<double>(col1) >= 100. &&
100,000✔
3551
            obj.get<double>(col1) <= 110.) {
100,000✔
3552
            expected++;
1,500✔
3553
        }
1,500✔
3554
    }
100,000✔
3555

3556
    // This unit test can be used as benchmark. Just enable this for loop
3557
    //    for (size_t t = 0; t < 1000; t++) {
3558
    Query q = table->column<double>(col0) >= 100. && table->column<double>(col0) <= 110. &&
1✔
3559
              table->column<double>(col1) >= 100. && table->column<double>(col1) <= 110.;
1✔
3560

3561
    size_t c = q.count();
1✔
3562
    CHECK_EQUAL(c, expected);
1✔
3563
}
1✔
3564

3565

3566
TEST_TYPES(Query_StrIndexed, std::true_type, std::false_type)
3567
{
2✔
3568
    Table ttt;
2✔
3569
    auto col_int = ttt.add_column(type_Int, "1");
2✔
3570
    auto col_str = ttt.add_column(type_String, "2");
2✔
3571

3572
    for (size_t t = 0; t < 10; t++) {
22✔
3573
        ttt.create_object().set_all(1, "a");
20✔
3574
        ttt.create_object().set_all(4, "b");
20✔
3575
        ttt.create_object().set_all(7, "c");
20✔
3576
        ttt.create_object().set_all(10, "a");
20✔
3577
        ttt.create_object().set_all(1, "b");
20✔
3578
        ttt.create_object().set_all(4, "c");
20✔
3579
    }
20✔
3580

3581
    if (TEST_TYPE::value == true) {
2✔
3582
        ttt.enumerate_string_column(col_str);
1✔
3583
    }
1✔
3584

3585
    ttt.add_search_index(col_str);
2✔
3586

3587
    auto s = *ttt.where().equal(col_str, "a").sum(col_int);
2✔
3588
    CHECK_EQUAL(10 * 11, s);
2✔
3589

3590
    s = *ttt.where().equal(col_str, "a").equal(col_int, 10).sum(col_int);
2✔
3591
    CHECK_EQUAL(100, s);
2✔
3592

3593
    s = *ttt.where().equal(col_int, 10).equal(col_str, "a").sum(col_int);
2✔
3594
    CHECK_EQUAL(100, s);
2✔
3595

3596
    TableView tv = ttt.where().equal(col_str, "a").find_all();
2✔
3597
    CHECK_EQUAL(10 * 2, tv.size());
2✔
3598
}
2✔
3599

3600
TEST(Query_FindAllContains2_2)
3601
{
1✔
3602
    Table ttt;
1✔
3603
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3604
    auto col_str = ttt.add_column(type_String, "2");
1✔
3605

3606
    ttt.create_object().set_all(0, "foo");
1✔
3607
    ttt.create_object().set_all(1, "foobar");
1✔
3608
    ttt.create_object().set_all(2, "hellofoobar");
1✔
3609
    ttt.create_object().set_all(3, "foO");
1✔
3610
    ttt.create_object().set_all(4, "foObar");
1✔
3611
    ttt.create_object().set_all(5, "hellofoObar");
1✔
3612
    ttt.create_object().set_all(6, "hellofo");
1✔
3613
    ttt.create_object().set_all(7, "fobar");
1✔
3614
    ttt.create_object().set_all(8, "oobar");
1✔
3615

3616
    // FIXME: UTF-8 case handling is only implemented on msw for now
3617
    Query q1 = ttt.where().contains(col_str, StringData("foO"), false);
1✔
3618
    TableView tv1 = q1.find_all();
1✔
3619
    CHECK_EQUAL(6, tv1.size());
1✔
3620
    CHECK_EQUAL(0, tv1.get_object(0).get<Int>(col_int));
1✔
3621
    CHECK_EQUAL(1, tv1.get_object(1).get<Int>(col_int));
1✔
3622
    CHECK_EQUAL(2, tv1.get_object(2).get<Int>(col_int));
1✔
3623
    CHECK_EQUAL(3, tv1.get_object(3).get<Int>(col_int));
1✔
3624
    CHECK_EQUAL(4, tv1.get_object(4).get<Int>(col_int));
1✔
3625
    CHECK_EQUAL(5, tv1.get_object(5).get<Int>(col_int));
1✔
3626
    Query q2 = ttt.where().contains(col_str, StringData("foO"), true);
1✔
3627
    TableView tv2 = q2.find_all();
1✔
3628
    CHECK_EQUAL(3, tv2.size());
1✔
3629
    CHECK_EQUAL(3, tv2.get_object(0).get<Int>(col_int));
1✔
3630
    CHECK_EQUAL(4, tv2.get_object(1).get<Int>(col_int));
1✔
3631
    CHECK_EQUAL(5, tv2.get_object(2).get<Int>(col_int));
1✔
3632
}
1✔
3633

3634
TEST(Query_SumNewAggregates)
3635
{
1✔
3636
    // test the new ACTION_FIND_PATTERN() method in array
3637
    Table t;
1✔
3638
    auto col_int = t.add_column(type_Int, "1");
1✔
3639
    for (size_t i = 0; i < 1000; i++) {
1,001✔
3640
        t.create_object().set(col_int, 1);
1,000✔
3641
        t.create_object().set(col_int, 2);
1,000✔
3642
        t.create_object().set(col_int, 4);
1,000✔
3643
        t.create_object().set(col_int, 6);
1,000✔
3644
    }
1,000✔
3645
    size_t c = t.where().equal(col_int, 2).count();
1✔
3646
    CHECK_EQUAL(1000, c);
1✔
3647

3648
    c = t.where().greater(col_int, 2).count();
1✔
3649
    CHECK_EQUAL(2000, c);
1✔
3650
}
1✔
3651

3652

3653
TEST(Query_SumMinMaxAvgForeignCol)
3654
{
1✔
3655
    Table t;
1✔
3656
    auto col_int0 = t.add_column(type_Int, "1");
1✔
3657
    auto col_int1 = t.add_column(type_Int, "2");
1✔
3658

3659
    t.create_object().set_all(1, 10);
1✔
3660
    t.create_object().set_all(2, 20);
1✔
3661
    t.create_object().set_all(2, 30);
1✔
3662
    t.create_object().set_all(4, 40);
1✔
3663

3664
    CHECK_EQUAL(50, t.where().equal(col_int0, 2).sum(col_int1));
1✔
3665
}
1✔
3666

3667
TEST(Query_AggregateSingleCond)
3668
{
1✔
3669
    Table t;
1✔
3670
    auto col_int = t.add_column(type_Int, "1");
1✔
3671

3672
    t.create_object().set(col_int, 1);
1✔
3673
    t.create_object().set(col_int, 2);
1✔
3674
    t.create_object().set(col_int, 2);
1✔
3675
    t.create_object().set(col_int, 3);
1✔
3676
    t.create_object().set(col_int, 3);
1✔
3677
    t.create_object().set(col_int, 4);
1✔
3678

3679
    auto s = t.where().equal(col_int, 2).sum(col_int);
1✔
3680
    CHECK_EQUAL(4, s);
1✔
3681

3682
    s = t.where().greater(col_int, 2).sum(col_int);
1✔
3683
    CHECK_EQUAL(10, s);
1✔
3684

3685
    s = t.where().less(col_int, 3).sum(col_int);
1✔
3686
    CHECK_EQUAL(5, s);
1✔
3687

3688
    s = t.where().not_equal(col_int, 3).sum(col_int);
1✔
3689
    CHECK_EQUAL(9, s);
1✔
3690
}
1✔
3691

3692
TEST(Query_FindAllRangeOr)
3693
{
1✔
3694
    Table ttt;
1✔
3695
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3696
    auto col_str = ttt.add_column(type_String, "2");
1✔
3697

3698
    ttt.create_object().set_all(1, "b");
1✔
3699
    ttt.create_object().set_all(2, "a"); //// match
1✔
3700
    ttt.create_object().set_all(3, "b"); //
1✔
3701
    ttt.create_object().set_all(1, "a"); //// match
1✔
3702
    ttt.create_object().set_all(2, "b"); //// match
1✔
3703
    ttt.create_object().set_all(3, "a");
1✔
3704
    ttt.create_object().set_all(1, "b");
1✔
3705
    ttt.create_object().set_all(2, "a"); //// match
1✔
3706
    ttt.create_object().set_all(3, "b"); //
1✔
3707

3708
    Query q1 = ttt.where().group().greater(col_int, 1).Or().equal(col_str, "a").end_group().less(col_int, 3);
1✔
3709
    TableView tv1 = q1.find_all();
1✔
3710
    CHECK_EQUAL(4, tv1.size());
1✔
3711
}
1✔
3712

3713

3714
TEST(Query_SimpleStr)
3715
{
1✔
3716
    Table ttt;
1✔
3717
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3718
    auto col_str = ttt.add_column(type_String, "2");
1✔
3719

3720
    ttt.create_object().set_all(1, "X");
1✔
3721
    ttt.create_object().set_all(2, "a");
1✔
3722
    ttt.create_object().set_all(3, "X");
1✔
3723
    ttt.create_object().set_all(4, "a");
1✔
3724
    ttt.create_object().set_all(5, "X");
1✔
3725
    ttt.create_object().set_all(6, "X");
1✔
3726

3727
    Query q = ttt.where().equal(col_str, "X");
1✔
3728
    size_t c = q.count();
1✔
3729
    CHECK_EQUAL(4, c);
1✔
3730

3731
    size_t r = q.remove();
1✔
3732
    CHECK_EQUAL(4, r);
1✔
3733
    CHECK_EQUAL(2, ttt.size());
1✔
3734
    CHECK_EQUAL(2, ttt.get_object(0).get<Int>(col_int));
1✔
3735
    CHECK_EQUAL(4, ttt.get_object(1).get<Int>(col_int));
1✔
3736

3737
    // test remove of all
3738
    Query q2 = ttt.where().greater(col_int, 0);
1✔
3739
    r = q2.remove();
1✔
3740
    CHECK_EQUAL(2, r);
1✔
3741
    CHECK_EQUAL(0, ttt.size());
1✔
3742
}
1✔
3743

3744

3745
TEST(Query_Simple)
3746
{
1✔
3747
    Table ttt;
1✔
3748
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3749
    auto col_str = ttt.add_column(type_String, "2");
1✔
3750

3751
    ObjKey k0 = ttt.create_object().set_all(1, "a").get_key();
1✔
3752
    ObjKey k1 = ttt.create_object().set_all(2, "a").get_key();
1✔
3753
    ObjKey k2 = ttt.create_object(ObjKey(0xc001ede1b0)).set_all(3, "X").get_key();
1✔
3754

3755
    Query q0 = ttt.where();
1✔
3756

3757
    TableView tv0 = q0.find_all();
1✔
3758
    CHECK_EQUAL(3, tv0.size());
1✔
3759
    CHECK_EQUAL(k0, tv0.get_key(0));
1✔
3760

3761
    Query q1 = ttt.where().equal(col_int, 2);
1✔
3762

3763
    TableView tv1 = q1.find_all();
1✔
3764
    CHECK_EQUAL(1, tv1.size());
1✔
3765
    CHECK_EQUAL(k1, tv1.get_key(0));
1✔
3766

3767
    Query q2 = ttt.where().Not().equal(col_str, "a");
1✔
3768

3769
    TableView tv2 = q2.find_all();
1✔
3770
    CHECK_EQUAL(1, tv2.size());
1✔
3771
    CHECK_EQUAL(k2, tv2.get_key(0));
1✔
3772

3773
    ttt.add_search_index(col_str);
1✔
3774

3775
    q2 = ttt.where().equal(col_str, "X");
1✔
3776
    tv2 = q2.find_all();
1✔
3777
    CHECK_EQUAL(1, tv2.size());
1✔
3778
    CHECK_EQUAL(k2, tv2.get_key(0));
1✔
3779

3780
    q2 = ttt.where().equal(col_str, "X").equal(col_int, 3);
1✔
3781
    tv2 = q2.find_all();
1✔
3782
    CHECK_EQUAL(1, tv2.size());
1✔
3783
    CHECK_EQUAL(k2, tv2.get_key(0));
1✔
3784
}
1✔
3785

3786

3787
TEST(Query_Sort1)
3788
{
1✔
3789
    Table ttt;
1✔
3790
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3791
    ttt.add_column(type_String, "2");
1✔
3792

3793
    ttt.create_object().set_all(1, "a"); // 0
1✔
3794
    ttt.create_object().set_all(2, "a"); // 1
1✔
3795
    ttt.create_object().set_all(3, "X"); // 2
1✔
3796
    ttt.create_object().set_all(1, "a"); // 3
1✔
3797
    ttt.create_object().set_all(2, "a"); // 4
1✔
3798
    ttt.create_object().set_all(3, "X"); // 5
1✔
3799
    ttt.create_object().set_all(9, "a"); // 6
1✔
3800
    ttt.create_object().set_all(8, "a"); // 7
1✔
3801
    ttt.create_object().set_all(7, "X"); // 8
1✔
3802

3803
    // tv.get_key()  = 0, 2, 3, 5, 6, 7, 8
3804
    // Vals         = 1, 3, 1, 3, 9, 8, 7
3805
    // result       = 3, 0, 5, 2, 8, 7, 6
3806

3807
    Query q = ttt.where().not_equal(col_int, 2);
1✔
3808
    TableView tv = q.find_all();
1✔
3809
    tv.sort(col_int);
1✔
3810

3811
    CHECK_EQUAL(tv.size(), 7);
1✔
3812
    CHECK_EQUAL(tv[0].get<Int>(col_int), 1);
1✔
3813
    CHECK_EQUAL(tv[1].get<Int>(col_int), 1);
1✔
3814
    CHECK_EQUAL(tv[2].get<Int>(col_int), 3);
1✔
3815
    CHECK_EQUAL(tv[3].get<Int>(col_int), 3);
1✔
3816
    CHECK_EQUAL(tv[4].get<Int>(col_int), 7);
1✔
3817
    CHECK_EQUAL(tv[5].get<Int>(col_int), 8);
1✔
3818
    CHECK_EQUAL(tv[6].get<Int>(col_int), 9);
1✔
3819

3820
    CHECK_EQUAL(tv[0].get_key(), ObjKey(0));
1✔
3821
    CHECK_EQUAL(tv[1].get_key(), ObjKey(3));
1✔
3822
    CHECK_EQUAL(tv[2].get_key(), ObjKey(2));
1✔
3823
    CHECK_EQUAL(tv[3].get_key(), ObjKey(5));
1✔
3824
    CHECK_EQUAL(tv[4].get_key(), ObjKey(8));
1✔
3825
    CHECK_EQUAL(tv[5].get_key(), ObjKey(7));
1✔
3826
    CHECK_EQUAL(tv[6].get_key(), ObjKey(6));
1✔
3827

3828
    tv = q.find_all();
1✔
3829
    tv.sort(col_int, false);
1✔
3830

3831
    CHECK_EQUAL(tv.size(), 7);
1✔
3832
    CHECK_EQUAL(tv[0].get_key(), ObjKey(6));
1✔
3833
    CHECK_EQUAL(tv[1].get_key(), ObjKey(7));
1✔
3834
    CHECK_EQUAL(tv[2].get_key(), ObjKey(8));
1✔
3835
    CHECK_EQUAL(tv[3].get_key(), ObjKey(2));
1✔
3836
    CHECK_EQUAL(tv[4].get_key(), ObjKey(5));
1✔
3837
    CHECK_EQUAL(tv[5].get_key(), ObjKey(0));
1✔
3838
    CHECK_EQUAL(tv[6].get_key(), ObjKey(3));
1✔
3839
}
1✔
3840

3841
TEST(Query_SortAscending)
3842
{
1✔
3843
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
3844

3845
    Table ttt;
1✔
3846
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3847
    ttt.add_column(type_String, "2");
1✔
3848

3849
    for (size_t t = 0; t < 1000; t++)
1,001✔
3850
        ttt.create_object().set_all(random.draw_int_mod(1100), "a"); // 0
1,000✔
3851

3852
    Query q = ttt.where();
1✔
3853
    TableView tv = q.find_all();
1✔
3854
    tv.sort(col_int);
1✔
3855

3856
    CHECK(tv.size() == 1000);
1✔
3857
    for (size_t t = 1; t < tv.size(); t++) {
1,000✔
3858
        CHECK(tv[t].get<Int>(col_int) >= tv[t - 1].get<Int>(col_int));
999✔
3859
    }
999✔
3860
}
1✔
3861

3862
TEST(Query_SortDescending)
3863
{
1✔
3864
    Random random(random_int<unsigned long>()); // Seed from slow global generator
1✔
3865

3866
    Table ttt;
1✔
3867
    auto col_int = ttt.add_column(type_Int, "1");
1✔
3868
    ttt.add_column(type_String, "2");
1✔
3869

3870
    for (size_t t = 0; t < 1000; t++)
1,001✔
3871
        ttt.create_object().set_all(random.draw_int_mod(1100), "a"); // 0
1,000✔
3872

3873
    Query q = ttt.where();
1✔
3874
    TableView tv = q.find_all();
1✔
3875
    tv.sort(col_int, false);
1✔
3876

3877
    CHECK(tv.size() == 1000);
1✔
3878
    for (size_t t = 1; t < tv.size(); t++) {
1,000✔
3879
        CHECK(tv[t].get<Int>(col_int) <= tv[t - 1].get<Int>(col_int));
999✔
3880
    }
999✔
3881
}
1✔
3882

3883

3884
TEST(Query_SortDates)
3885
{
1✔
3886
    Table table;
1✔
3887
    auto col_date = table.add_column(type_Timestamp, "first");
1✔
3888

3889
    table.create_object().set(col_date, Timestamp(1000, 0));
1✔
3890
    table.create_object().set(col_date, Timestamp(3000, 0));
1✔
3891
    table.create_object().set(col_date, Timestamp(2000, 0));
1✔
3892

3893
    TableView tv = table.where().find_all();
1✔
3894
    CHECK(tv.size() == 3);
1✔
3895

3896
    tv.sort(col_date);
1✔
3897
    CHECK_EQUAL(tv[0].get<Timestamp>(col_date), Timestamp(1000, 0));
1✔
3898
    CHECK_EQUAL(tv[1].get<Timestamp>(col_date), Timestamp(2000, 0));
1✔
3899
    CHECK_EQUAL(tv[2].get<Timestamp>(col_date), Timestamp(3000, 0));
1✔
3900
}
1✔
3901

3902
TEST(Query_DateRange)
3903
{
1✔
3904
    Table table;
1✔
3905
    auto col_date = table.add_column(type_Timestamp, "date", true);
1✔
3906

3907
    for (int64_t sec = 100; sec < 110; sec++) {
11✔
3908
        for (int nano = 0; nano < 5; nano++) {
60✔
3909
            table.create_object().set(col_date, Timestamp(sec, nano));
50✔
3910
        }
50✔
3911
        table.create_object();
10✔
3912
    }
10✔
3913

3914
    CHECK_EQUAL(table.where().between(col_date, Timestamp(100, 1), Timestamp(100, 1)).count(), 1);
1✔
3915
    CHECK_EQUAL(table.where().between(col_date, Timestamp(100, 1), Timestamp(100, 4)).count(), 4);
1✔
3916
    CHECK_EQUAL(table.where().between(col_date, Timestamp(100, 4), Timestamp(100, 7)).count(), 1);
1✔
3917
    CHECK_EQUAL(table.where().between(col_date, Timestamp(100, 4), Timestamp(101, 0)).count(), 2);
1✔
3918
    auto q = table.where().between(col_date, Timestamp(102, 0), Timestamp(103, 10));
1✔
3919
    CHECK_EQUAL(q.count(), 10);
1✔
3920
    auto d = q.get_description();
1✔
3921
    q = table.query(d);
1✔
3922
    CHECK_EQUAL(q.count(), 10);
1✔
3923
}
1✔
3924

3925
TEST(Query_SortBools)
3926
{
1✔
3927
    Table table;
1✔
3928
    auto col = table.add_column(type_Bool, "first");
1✔
3929

3930
    table.create_object().set(col, true);
1✔
3931
    table.create_object().set(col, false);
1✔
3932
    table.create_object().set(col, true);
1✔
3933

3934
    TableView tv = table.where().find_all();
1✔
3935
    CHECK(tv.size() == 3);
1✔
3936

3937
    tv.sort(col);
1✔
3938
    CHECK_EQUAL(tv[0].get<Bool>(col), false);
1✔
3939
    CHECK_EQUAL(tv[1].get<Bool>(col), true);
1✔
3940
    CHECK_EQUAL(tv[2].get<Bool>(col), true);
1✔
3941
}
1✔
3942

3943
TEST(Query_SortLinks)
3944
{
1✔
3945
    const size_t num_rows = 10;
1✔
3946
    Group g;
1✔
3947
    TableRef t1 = g.add_table("t1");
1✔
3948
    TableRef t2 = g.add_table("t2");
1✔
3949

3950
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
3951
    auto t1_str_col = t1->add_column(type_String, "t1_string");
1✔
3952
    auto t1_link_t2_col = t1->add_column(*t2, "t1_link_to_t2");
1✔
3953
    t2->add_column(type_Int, "t2_int");
1✔
3954
    t2->add_column(type_String, "t2_string");
1✔
3955
    t2->add_column(*t1, "t2_link_to_t1");
1✔
3956

3957
    std::vector<ObjKey> t1_keys;
1✔
3958
    std::vector<ObjKey> t2_keys;
1✔
3959
    t1->create_objects(num_rows, t1_keys);
1✔
3960
    t2->create_objects(num_rows, t2_keys);
1✔
3961
    std::vector<std::string> ordered_strings;
1✔
3962

3963
    for (size_t i = 0; i < num_rows; ++i) {
11✔
3964
        ordered_strings.push_back(std::string("a string") + util::to_string(i));
10✔
3965
        t1->get_object(t1_keys[i]).set_all(int64_t(i), StringData(ordered_strings[i]), t2_keys[num_rows - i - 1]);
10✔
3966
        t2->get_object(t1_keys[i]).set_all(int64_t(i), StringData(ordered_strings[i]), t1_keys[i]);
10✔
3967
    }
10✔
3968

3969
    TableView tv = t1->where().find_all();
1✔
3970

3971
    // Check natural order
3972
    CHECK_EQUAL(tv.size(), num_rows);
1✔
3973
    for (size_t i = 0; i < tv.size(); ++i) {
11✔
3974
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), i);
10✔
3975
        CHECK_EQUAL(tv[i].get<String>(t1_str_col), ordered_strings[i]);
10✔
3976
    }
10✔
3977

3978
    // Check sorted order by ints
3979
    tv.sort(t1_int_col);
1✔
3980
    CHECK_EQUAL(tv.size(), num_rows);
1✔
3981
    for (size_t i = 0; i < tv.size(); ++i) {
11✔
3982
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), i);
10✔
3983
        CHECK_EQUAL(tv[i].get<String>(t1_str_col), ordered_strings[i]);
10✔
3984
    }
10✔
3985

3986
    // Check that you can sort on a regular link column
3987
    tv = t1->where().find_all();
1✔
3988
    tv.sort(t1_link_t2_col);
1✔
3989
    CHECK_EQUAL(tv.size(), num_rows);
1✔
3990
    for (size_t i = 0; i < tv.size(); ++i) {
11✔
3991
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), num_rows - i - 1);
10✔
3992
        CHECK_EQUAL(tv[i].get<String>(t1_str_col), ordered_strings[num_rows - i - 1]);
10✔
3993
    }
10✔
3994
}
1✔
3995

3996
TEST(Query_SortLinkChains)
3997
{
1✔
3998
    Group g;
1✔
3999
    TableRef t1 = g.add_table("t1");
1✔
4000
    TableRef t2 = g.add_table("t2");
1✔
4001
    TableRef t3 = g.add_table("t3");
1✔
4002

4003
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4004
    auto t1_link_col = t1->add_column(*t2, "t1_link_t2");
1✔
4005

4006
    auto t2_int_col = t2->add_column(type_Int, "t2_int");
1✔
4007
    auto t2_link_col = t2->add_column(*t3, "t2_link_t3");
1✔
4008

4009
    auto t3_int_col = t3->add_column(type_Int, "t3_int", true);
1✔
4010
    auto t3_str_col = t3->add_column(type_String, "t3_str");
1✔
4011

4012
    ObjKeyVector t1_keys({0, 1, 2, 3, 4, 5, 6});
1✔
4013
    ObjKeyVector t2_keys({10, 11, 12, 13, 14, 15});
1✔
4014
    ObjKeyVector t3_keys({20, 21, 22, 23});
1✔
4015
    t1->create_objects(t1_keys);
1✔
4016
    t2->create_objects(t2_keys);
1✔
4017
    t3->create_objects(t3_keys);
1✔
4018

4019
    t1->get_object(t1_keys[0]).set(t1_int_col, 99);
1✔
4020
    for (size_t i = 0; i < t2->size(); i++) {
7✔
4021
        t1->get_object(t1_keys[i + 1]).set(t1_int_col, int64_t(i));
6✔
4022
        t2->get_object(t2_keys[i]).set(t2_int_col, int64_t(t1->size() - i));
6✔
4023
    }
6✔
4024

4025
    t1->get_object(t1_keys[0]).set(t1_link_col, t2_keys[1]);
1✔
4026
    t1->get_object(t1_keys[1]).set(t1_link_col, t2_keys[0]);
1✔
4027
    t1->get_object(t1_keys[2]).set(t1_link_col, t2_keys[2]);
1✔
4028
    t1->get_object(t1_keys[3]).set(t1_link_col, t2_keys[3]);
1✔
4029
    t1->get_object(t1_keys[4]).set(t1_link_col, t2_keys[5]);
1✔
4030
    t1->get_object(t1_keys[5]).set(t1_link_col, t2_keys[4]);
1✔
4031
    t1->get_object(t1_keys[6]).set(t1_link_col, t2_keys[1]);
1✔
4032

4033
    t2->get_object(t2_keys[0]).set(t2_link_col, t3_keys[3]);
1✔
4034
    t2->get_object(t2_keys[1]).set(t2_link_col, t3_keys[2]);
1✔
4035
    t2->get_object(t2_keys[2]).set(t2_link_col, t3_keys[0]);
1✔
4036
    t2->get_object(t2_keys[3]).set(t2_link_col, t3_keys[1]);
1✔
4037

4038
    t3->get_object(t3_keys[1]).set(t3_int_col, 4);
1✔
4039
    t3->get_object(t3_keys[2]).set(t3_int_col, 7);
1✔
4040
    t3->get_object(t3_keys[3]).set(t3_int_col, 3);
1✔
4041
    t3->get_object(t3_keys[0]).set(t3_str_col, "b");
1✔
4042
    t3->get_object(t3_keys[1]).set(t3_str_col, "a");
1✔
4043
    t3->get_object(t3_keys[2]).set(t3_str_col, "c");
1✔
4044
    t3->get_object(t3_keys[3]).set(t3_str_col, "k");
1✔
4045

4046
    //  T1                       T2                     T3
4047
    //  t1_int   t1_link_t2  |   t2_int  t2_link_t3 |   t3_int  t3_str
4048
    //  ==============================================================
4049
    //  99       11          |   5       23         |   null    "b"
4050
    //  0        10          |   4       22         |   4       "a"
4051
    //  1        12          |   3       20         |   7       "c"
4052
    //  2        13          |   2       21         |   3       "k"
4053
    //  3        15          |   1       null       |
4054
    //  4        14          |   0       null       |
4055
    //  5        11          |                      |
4056

4057
    TableView tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4058

4059
    // Test original functionality through chain class
4060
    std::vector<size_t> results1 = {0, 1, 2, 3, 4, 5};
1✔
4061
    tv.sort(SortDescriptor({{t1_int_col}}, {true}));
1✔
4062
    CHECK_EQUAL(tv.size(), results1.size());
1✔
4063
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4064
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[i]);
6✔
4065
    }
6✔
4066
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4067
    tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4068
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4069
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[results1.size() - 1 - i]);
6✔
4070
    }
6✔
4071

4072
    // Test basic one link chain
4073
    std::vector<size_t> results2 = {3, 4, 2, 1, 5, 0};
1✔
4074
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4075
    tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}, {true}));
1✔
4076
    CHECK_EQUAL(tv.size(), results2.size());
1✔
4077
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4078
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results2[i]);
6✔
4079
    }
6✔
4080
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4081
    tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}, {false}));
1✔
4082
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4083
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results2[results2.size() - 1 - i]);
6✔
4084
    }
6✔
4085

4086
    // Test link chain through two links with nulls
4087
    std::vector<size_t> results3 = {1, 0, 2, 5};
1✔
4088
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4089
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}}, {true}));
1✔
4090
    // No guarantees about nullified links except they are at the end.
4091
    CHECK(tv.size() >= results3.size());
1✔
4092
    util::Optional<int64_t> last;
1✔
4093
    for (size_t i = 0; i < results3.size(); ++i) {
5✔
4094
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results3[i]);
4✔
4095
        util::Optional<int64_t> current = tv[i]
4✔
4096
                                              .get_linked_object(t1_link_col)
4✔
4097
                                              .get_linked_object(t2_link_col)
4✔
4098
                                              .get<util::Optional<int64_t>>(t3_int_col);
4✔
4099
        if (last) {
4✔
4100
            CHECK(current);
2✔
4101
            CHECK(*current >= *last);
2✔
4102
        }
2✔
4103
        if (current) {
4✔
4104
            last = current;
3✔
4105
        }
3✔
4106
    }
4✔
4107
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4108
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}}, {false}));
1✔
4109
    // No guarantees about nullified links except they are at the beginning.
4110
    size_t num_nulls = tv.size() - results3.size();
1✔
4111
    for (size_t i = num_nulls; i < results3.size(); ++i) {
3✔
4112
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results3[results2.size() - 1 - i]);
2✔
4113
    }
2✔
4114

4115
    // Test link chain with nulls and a single local column
4116
    std::vector<size_t> results4 = {1, 0, 2, 5, 3, 4};
1✔
4117
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4118
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}, {t1_int_col}}));
1✔
4119
    CHECK_EQUAL(tv.size(), results4.size());
1✔
4120
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4121
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results4[i]);
6✔
4122
    }
6✔
4123
    std::vector<size_t> results4_rev = {1, 0, 2, 5, 4, 3};
1✔
4124
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4125
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}, {t1_int_col}}, {true, false}));
1✔
4126
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4127
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results4_rev[i]);
6✔
4128
    }
6✔
4129
    std::vector<size_t> results4_rev2 = {3, 4, 5, 2, 0, 1};
1✔
4130
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4131
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}, {t1_int_col}}, {false, true}));
1✔
4132
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4133
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results4_rev2[i]);
6✔
4134
    }
6✔
4135
    std::vector<size_t> results4_rev3 = {4, 3, 5, 2, 0, 1};
1✔
4136
    tv = t1->where().less(t1_int_col, 6).find_all();
1✔
4137
    tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}, {t1_int_col}}, {false, false}));
1✔
4138
    for (size_t i = 0; i < tv.size(); ++i) {
7✔
4139
        CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results4_rev3[i]);
6✔
4140
    }
6✔
4141
}
1✔
4142

4143
TEST(Query_LinkChainSortErrors)
4144
{
1✔
4145
    Group g;
1✔
4146
    TableRef t1 = g.add_table("t1");
1✔
4147
    TableRef t2 = g.add_table("t2");
1✔
4148

4149
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4150
    auto t1_linklist_col = t1->add_column_list(*t2, "t1_linklist");
1✔
4151
    auto t2_string_col = t2->add_column(type_String, "t2_string");
1✔
4152
    t2->add_column(*t1, "t2_link_t1"); // add a backlink to t1
1✔
4153

4154
    t1->create_object();
1✔
4155

4156
    // Disallow invalid column ids, linklists, other non-link column types.
4157
    ColKey backlink_ndx(ColKey::Idx{2}, col_type_Link, ColumnAttrMask{}, 0);
1✔
4158
    CHECK_LOGIC_ERROR(t1->get_sorted_view(SortDescriptor({{t1_linklist_col, t2_string_col}})),
1✔
4159
                      ErrorCodes::InvalidSortDescriptor);
1✔
4160
    CHECK_LOGIC_ERROR(t1->get_sorted_view(SortDescriptor({{backlink_ndx, t2_string_col}})),
1✔
4161
                      ErrorCodes::InvalidSortDescriptor);
1✔
4162
    CHECK_LOGIC_ERROR(t1->get_sorted_view(SortDescriptor({{t1_int_col, t2_string_col}})),
1✔
4163
                      ErrorCodes::InvalidSortDescriptor);
1✔
4164
    CHECK_LOGIC_ERROR(t1->get_sorted_view(SortDescriptor({{t1_linklist_col}})), ErrorCodes::InvalidSortDescriptor);
1✔
4165
}
1✔
4166

4167

4168
TEST(Query_EmptyDescriptors)
4169
{
1✔
4170
    Group g;
1✔
4171
    TableRef t1 = g.add_table("t1");
1✔
4172

4173
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4174

4175
    t1->create_object().set(t1_int_col, 4);
1✔
4176
    t1->create_object().set(t1_int_col, 3);
1✔
4177
    t1->create_object().set(t1_int_col, 2);
1✔
4178
    t1->create_object().set(t1_int_col, 3);
1✔
4179

4180
    std::vector<size_t> results = {4, 3, 2, 3}; // original order
1✔
4181

4182
    { // Sorting with an empty sort descriptor is a no-op
1✔
4183
        TableView tv = t1->where().find_all();
1✔
4184
        tv.sort(SortDescriptor());
1✔
4185
        for (size_t i = 0; i < results.size(); ++i) {
5✔
4186
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
4187
        }
4✔
4188
    }
1✔
4189
    { // Distinct with an empty descriptor is a no-op
1✔
4190
        TableView tv = t1->where().find_all();
1✔
4191
        tv.distinct(DistinctDescriptor());
1✔
4192
        for (size_t i = 0; i < results.size(); ++i) {
5✔
4193
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
4194
        }
4✔
4195
    }
1✔
4196
    { // Empty sort, empty distinct is still a no-op
1✔
4197
        TableView tv = t1->where().find_all();
1✔
4198
        tv.sort(SortDescriptor());
1✔
4199
        tv.distinct(DistinctDescriptor());
1✔
4200
        for (size_t i = 0; i < results.size(); ++i) {
5✔
4201
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
4202
        }
4✔
4203
    }
1✔
4204
    { // Arbitrary compounded empty sort and distinct is still a no-op
1✔
4205
        TableView tv = t1->where().find_all();
1✔
4206
        tv.sort(SortDescriptor());
1✔
4207
        tv.sort(SortDescriptor());
1✔
4208
        tv.distinct(DistinctDescriptor());
1✔
4209
        tv.sort(SortDescriptor());
1✔
4210
        tv.distinct(DistinctDescriptor());
1✔
4211
        tv.distinct(DistinctDescriptor());
1✔
4212
        tv.distinct(DistinctDescriptor());
1✔
4213
        for (size_t i = 0; i < results.size(); ++i) {
5✔
4214
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
4215
        }
4✔
4216
    }
1✔
4217
    { // Empty distinct compounded on a valid distinct is a no-op
1✔
4218
        TableView tv = t1->where().find_all();
1✔
4219
        tv.distinct(DistinctDescriptor());
1✔
4220
        tv.distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4221
        tv.distinct(DistinctDescriptor());
1✔
4222
        results = {4, 3, 2};
1✔
4223
        for (size_t i = 0; i < results.size(); ++i) {
4✔
4224
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
3✔
4225
        }
3✔
4226
    }
1✔
4227
    { // Empty sort compounded on a valid sort is a no-op
1✔
4228
        TableView tv = t1->where().find_all();
1✔
4229
        tv.sort(SortDescriptor());
1✔
4230
        tv.sort(SortDescriptor({{t1_int_col}}));
1✔
4231
        tv.sort(SortDescriptor());
1✔
4232
        results = {2, 3, 3, 4};
1✔
4233
        for (size_t i = 0; i < results.size(); ++i) {
5✔
4234
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
4235
        }
4✔
4236
    }
1✔
4237
}
1✔
4238

4239
TEST(Query_AllowEmptyDescriptors)
4240
{
1✔
4241
    Group g;
1✔
4242
    TableRef t1 = g.add_table("t1");
1✔
4243
    t1->add_column(type_Int, "t1_int");
1✔
4244
    t1->add_column(type_String, "t1_str");
1✔
4245
    t1->create_object();
1✔
4246

4247
    DescriptorOrdering ordering;
1✔
4248

4249
    CHECK(!ordering.will_apply_sort());
1✔
4250
    CHECK(!ordering.will_apply_distinct());
1✔
4251
    CHECK(!ordering.will_apply_limit());
1✔
4252
    CHECK(!ordering.will_limit_to_zero());
1✔
4253
    CHECK_EQUAL(ordering.size(), 0);
1✔
4254

4255
    ordering.append_sort(SortDescriptor());
1✔
4256
    ordering.append_distinct(DistinctDescriptor());
1✔
4257
    CHECK(!ordering.will_apply_sort());
1✔
4258
    CHECK(!ordering.will_apply_distinct());
1✔
4259
    CHECK(!ordering.will_apply_limit());
1✔
4260
    CHECK(!ordering.will_limit_to_zero());
1✔
4261
    CHECK_EQUAL(ordering.size(), 0);
1✔
4262
}
1✔
4263

4264
TEST(Query_DescriptorsWillApply)
4265
{
1✔
4266
    Group g;
1✔
4267
    TableRef t1 = g.add_table("t1");
1✔
4268
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4269
    auto t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4270

4271
    t1->create_object();
1✔
4272

4273
    DescriptorOrdering ordering;
1✔
4274

4275
    CHECK(!ordering.will_apply_sort());
1✔
4276
    CHECK(!ordering.will_apply_distinct());
1✔
4277
    CHECK(!ordering.will_apply_limit());
1✔
4278
    CHECK(!ordering.will_limit_to_zero());
1✔
4279
    CHECK_EQUAL(ordering.size(), 0);
1✔
4280

4281
    ordering.append_sort(SortDescriptor());
1✔
4282
    CHECK(!ordering.will_apply_sort());
1✔
4283
    CHECK(!ordering.will_apply_distinct());
1✔
4284
    CHECK(!ordering.will_apply_limit());
1✔
4285
    CHECK(!ordering.will_limit_to_zero());
1✔
4286
    CHECK_EQUAL(ordering.size(), 0);
1✔
4287

4288
    ordering.append_distinct(DistinctDescriptor());
1✔
4289
    CHECK(!ordering.will_apply_sort());
1✔
4290
    CHECK(!ordering.will_apply_distinct());
1✔
4291
    CHECK(!ordering.will_apply_limit());
1✔
4292
    CHECK(!ordering.will_limit_to_zero());
1✔
4293
    CHECK_EQUAL(ordering.size(), 0);
1✔
4294

4295
    ordering.append_limit(LimitDescriptor());
1✔
4296
    CHECK(!ordering.will_apply_sort());
1✔
4297
    CHECK(!ordering.will_apply_distinct());
1✔
4298
    CHECK(!ordering.will_apply_limit());
1✔
4299
    CHECK(!ordering.will_limit_to_zero());
1✔
4300
    CHECK_EQUAL(ordering.size(), 0);
1✔
4301

4302
    ordering.append_sort(SortDescriptor({{t1_int_col}}));
1✔
4303
    CHECK(ordering.will_apply_sort());
1✔
4304
    CHECK(!ordering.will_apply_distinct());
1✔
4305
    CHECK(!ordering.will_apply_limit());
1✔
4306
    CHECK(!ordering.will_limit_to_zero());
1✔
4307

4308
    ordering.append_distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4309
    CHECK(ordering.will_apply_sort());
1✔
4310
    CHECK(ordering.will_apply_distinct());
1✔
4311
    CHECK(!ordering.will_apply_limit());
1✔
4312
    CHECK(!ordering.will_limit_to_zero());
1✔
4313

4314
    ordering.append_distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4315
    CHECK(ordering.will_apply_sort());
1✔
4316
    CHECK(ordering.will_apply_distinct());
1✔
4317
    CHECK(!ordering.will_apply_limit());
1✔
4318
    CHECK(!ordering.will_limit_to_zero());
1✔
4319

4320
    ordering.append_sort(SortDescriptor({{t1_str_col}}));
1✔
4321
    CHECK(ordering.will_apply_sort());
1✔
4322
    CHECK(ordering.will_apply_distinct());
1✔
4323
    CHECK(!ordering.will_apply_limit());
1✔
4324
    CHECK(!ordering.will_limit_to_zero());
1✔
4325

4326
    ordering.append_limit(LimitDescriptor(1));
1✔
4327
    CHECK(ordering.will_apply_sort());
1✔
4328
    CHECK(ordering.will_apply_distinct());
1✔
4329
    CHECK(ordering.will_apply_limit());
1✔
4330
    CHECK(!ordering.will_limit_to_zero());
1✔
4331

4332
    CHECK_EQUAL(ordering.size(), 5);
1✔
4333
    CHECK(ordering.get_type(0) == DescriptorType::Sort);
1✔
4334
    CHECK(ordering.get_type(1) == DescriptorType::Distinct);
1✔
4335
    CHECK(ordering.get_type(2) == DescriptorType::Distinct);
1✔
4336
    CHECK(ordering.get_type(3) == DescriptorType::Sort);
1✔
4337
    CHECK(ordering.get_type(4) == DescriptorType::Limit);
1✔
4338

4339
    DescriptorOrdering ordering_copy = ordering;
1✔
4340
    CHECK(ordering.will_apply_sort());
1✔
4341
    CHECK(ordering.will_apply_distinct());
1✔
4342
    CHECK(ordering.will_apply_limit());
1✔
4343
    CHECK(!ordering.will_limit_to_zero());
1✔
4344
    CHECK(ordering_copy.will_apply_sort());
1✔
4345
    CHECK(ordering_copy.will_apply_distinct());
1✔
4346
    CHECK(ordering_copy.will_apply_limit());
1✔
4347
    CHECK(!ordering_copy.will_limit_to_zero());
1✔
4348

4349
    ordering_copy.append_limit({10});
1✔
4350
    ordering_copy.append_limit({0});
1✔
4351
    CHECK(ordering_copy.will_limit_to_zero());
1✔
4352
}
1✔
4353

4354
TEST(Query_FindWithDescriptorOrdering)
4355
{
1✔
4356
    Group g;
1✔
4357
    TableRef t1 = g.add_table("t1");
1✔
4358
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4359
    auto t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4360

4361
    auto k0 = t1->create_object().set_all(1, "A").get_key();
1✔
4362
    auto k1 = t1->create_object().set_all(1, "A").get_key();
1✔
4363
    auto k2 = t1->create_object().set_all(1, "B").get_key();
1✔
4364
    auto k3 = t1->create_object().set_all(2, "B").get_key();
1✔
4365
    auto k4 = t1->create_object().set_all(2, "A").get_key();
1✔
4366
    auto k5 = t1->create_object().set_all(2, "A").get_key();
1✔
4367

4368
    //     T1
4369
    //   | t1_int   t1_str  |
4370
    //   ====================
4371
    // 0 | 1        "A"     |
4372
    // 1 | 1        "A"     |
4373
    // 2 | 1        "B"     |
4374
    // 3 | 2        "B"     |
4375
    // 4 | 2        "A"     |
4376
    // 5 | 2        "A"     |
4377

4378
    using ResultList = std::vector<std::pair<int64_t, ObjKey>>; // value, key
1✔
4379
    {
1✔
4380
        // applying only limit
4381
        DescriptorOrdering ordering;
1✔
4382
        TableView tv = t1->where().find_all(ordering);
1✔
4383
        CHECK_EQUAL(tv.size(), 6);
1✔
4384
        CHECK_EQUAL(t1->where().count(ordering), 6);
1✔
4385
        ordering.append_limit({2});
1✔
4386
        ResultList expected = {{1, k0}, {1, k1}};
1✔
4387
        tv = t1->where().find_all(ordering);
1✔
4388
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4389
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4390
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4391
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4392
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4393
        }
2✔
4394
        ordering.append_limit({1}); // two limits should apply the minimum limit
1✔
4395
        expected = {{1, k0}};
1✔
4396
        tv = t1->where().find_all(ordering);
1✔
4397
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4398
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4399
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4400
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
1✔
4401
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
1✔
4402
        }
1✔
4403
    }
1✔
4404
    {
1✔
4405
        // applying sort and sort with prepend
4406
        DescriptorOrdering ordering;
1✔
4407
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {true}), SortDescriptor::MergeMode::prepend);
1✔
4408
        ordering.append_sort(SortDescriptor({{t1_int_col}}, {true}), SortDescriptor::MergeMode::prepend);
1✔
4409
        TableView tv = t1->where().find_all(ordering);
1✔
4410
        ResultList expected = {{1, k0}, {1, k1}, {1, k2}, {2, k4}, {2, k5}, {2, k3}};
1✔
4411
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4412
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4413
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
4414
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
6✔
4415
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
6✔
4416
        }
6✔
4417
    }
1✔
4418
    {
1✔
4419
        // applying sort and sort with append
4420
        DescriptorOrdering ordering;
1✔
4421
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {true}), SortDescriptor::MergeMode::append);
1✔
4422
        ordering.append_sort(SortDescriptor({{t1_int_col}}, {true}), SortDescriptor::MergeMode::append);
1✔
4423
        TableView tv = t1->where().find_all(ordering);
1✔
4424
        ResultList expected = {{1, k0}, {1, k1}, {2, k4}, {2, k5}, {1, k2}, {2, k3}};
1✔
4425
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4426
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4427
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
4428
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
6✔
4429
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
6✔
4430
        }
6✔
4431
    }
1✔
4432
    {
1✔
4433
        // applying string sort, then a limit, and then a descending integer sort, then replace the last sort with
4434
        // an ascending integer sort - the end result should reflect the limit and the first and last sort descriptors
4435
        DescriptorOrdering ordering;
1✔
4436
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4437
        ordering.append_limit(LimitDescriptor(4));
1✔
4438
        ordering.append_sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4439
        ordering.append_sort(SortDescriptor({{t1_int_col}}, {true}), SortDescriptor::MergeMode::replace);
1✔
4440
        TableView tv = t1->where().find_all(ordering);
1✔
4441
        ResultList expected = {{1, k2}, {1, k0}, {1, k1}, {2, k3}};
1✔
4442
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4443
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4444
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
4445
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
4✔
4446
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
4✔
4447
        }
4✔
4448
    }
1✔
4449
    {
1✔
4450
        // applying sort and limit
4451
        DescriptorOrdering ordering;
1✔
4452
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4453
        ordering.append_limit({2});
1✔
4454
        TableView tv = t1->where().find_all(ordering);
1✔
4455
        ResultList expected = {{1, k2}, {2, k3}};
1✔
4456
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4457
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4458
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4459
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4460
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4461
        }
2✔
4462
    }
1✔
4463
    { // sort limit distinct
1✔
4464
        DescriptorOrdering ordering;
1✔
4465
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4466
        ordering.append_limit({3});
1✔
4467
        ordering.append_distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4468
        TableView tv = t1->where().find_all(ordering);
1✔
4469
        ResultList expected = {{1, k2}, {2, k3}};
1✔
4470
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4471
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4472
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4473
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4474
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4475
        }
2✔
4476
    }
1✔
4477
    { // sort distinct limit
1✔
4478
        DescriptorOrdering ordering;
1✔
4479
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4480
        ordering.append_distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4481
        ordering.append_limit({1});
1✔
4482
        TableView tv = t1->where().find_all(ordering);
1✔
4483
        ResultList expected = {{1, k2}};
1✔
4484
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4485
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4486
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4487
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
1✔
4488
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
1✔
4489
        }
1✔
4490
    }
1✔
4491
    { // limit sort distinct
1✔
4492
        DescriptorOrdering ordering;
1✔
4493
        ordering.append_limit({2});
1✔
4494
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4495
        ordering.append_distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4496
        TableView tv = t1->where().find_all(ordering);
1✔
4497
        ResultList expected = {{1, k0}};
1✔
4498
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4499
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4500
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4501
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
1✔
4502
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
1✔
4503
        }
1✔
4504
    }
1✔
4505
    { // sort limit sort limit
1✔
4506
        DescriptorOrdering ordering;
1✔
4507
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {true}));
1✔
4508
        ordering.append_limit({5});
1✔
4509
        ordering.append_sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4510
        ordering.append_limit({3});
1✔
4511
        TableView tv = t1->where().find_all(ordering);
1✔
4512
        ResultList expected = {{2, k4}, {2, k5}, {1, k0}};
1✔
4513
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4514
        CHECK_EQUAL(t1->where().count(ordering), expected.size());
1✔
4515
        for (size_t i = 0; i < tv.size(); ++i) {
4✔
4516
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
3✔
4517
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
3✔
4518
        }
3✔
4519
    }
1✔
4520
}
1✔
4521

4522

4523
TEST(Query_FindWithDescriptorOrderingOverTableviewSync)
4524
{
1✔
4525
    Group g;
1✔
4526
    TableRef t1 = g.add_table("t1");
1✔
4527
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4528
    auto t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4529

4530
    auto init_table = [&]() {
2✔
4531
        t1->clear();
2✔
4532
        t1->create_object().set_all(0, "A");
2✔
4533
        t1->create_object().set_all(1, "A");
2✔
4534
        t1->create_object().set_all(2, "B");
2✔
4535
        t1->create_object().set_all(3, "B");
2✔
4536
        t1->create_object().set_all(4, "A");
2✔
4537
        t1->create_object().set_all(5, "A");
2✔
4538
    };
2✔
4539

4540
    //     T1
4541
    //   | t1_int   t1_str  |
4542
    //   ====================
4543
    // 0 | 0        "A"     |
4544
    // 1 | 1        "A"     |
4545
    // 2 | 2        "B"     |
4546
    // 3 | 3        "B"     |
4547
    // 4 | 4        "A"     |
4548
    // 5 | 5        "A"     |
4549

4550
    using ResultList = std::vector<std::pair<size_t, std::string>>; // t1_int, t1_str
1✔
4551
    {
1✔
4552
        // applying only limit
4553
        init_table();
1✔
4554
        DescriptorOrdering ordering;
1✔
4555
        Query base = t1->where().greater(t1_int_col, 2);
1✔
4556
        TableView tv = base.find_all(ordering);
1✔
4557
        CHECK_EQUAL(tv.size(), 3);
1✔
4558
        CHECK_EQUAL(base.count(ordering), 3);
1✔
4559
        ordering.append_limit({2});
1✔
4560
        ResultList expected = {{3, "B"}, {4, "A"}};
1✔
4561
        tv = base.find_all(ordering);
1✔
4562
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4563
        CHECK_EQUAL(base.count(ordering), expected.size());
1✔
4564
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4565
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4566
            CHECK_EQUAL(tv[i].get<String>(t1_str_col), expected[i].second);
2✔
4567
        }
2✔
4568
        t1->create_object().set_all(6, "C");
1✔
4569
        t1->get_object(4).remove();
1✔
4570
        expected = {{3, "B"}, {5, "A"}};
1✔
4571
        CHECK(!tv.is_in_sync());
1✔
4572
        tv.sync_if_needed();
1✔
4573
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4574
        CHECK_EQUAL(base.count(ordering), expected.size());
1✔
4575
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4576
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4577
            CHECK_EQUAL(tv[i].get<String>(t1_str_col), expected[i].second);
2✔
4578
        }
2✔
4579
    }
1✔
4580
    { // applying sort and limit
1✔
4581
        init_table();
1✔
4582
        DescriptorOrdering ordering;
1✔
4583
        Query base = t1->where().greater(t1_int_col, 2);
1✔
4584
        TableView tv = base.find_all(ordering);
1✔
4585
        CHECK_EQUAL(tv.size(), 3);
1✔
4586
        CHECK_EQUAL(base.count(ordering), 3);
1✔
4587
        ordering.append_sort(SortDescriptor({{t1_str_col}}, {true}));
1✔
4588
        ordering.append_limit({2});
1✔
4589
        ResultList expected = {{4, "A"}, {5, "A"}};
1✔
4590
        tv = base.find_all(ordering);
1✔
4591
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4592
        CHECK_EQUAL(base.count(ordering), expected.size());
1✔
4593
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4594
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4595
            CHECK_EQUAL(tv[i].get<String>(t1_str_col), expected[i].second);
2✔
4596
        }
2✔
4597
        t1->create_object().set_all(6, "C");
1✔
4598
        t1->get_object(4).remove();
1✔
4599
        expected = {{5, "A"}, {3, "B"}};
1✔
4600
        CHECK(!tv.is_in_sync());
1✔
4601
        tv.sync_if_needed();
1✔
4602
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4603
        CHECK_EQUAL(base.count(ordering), expected.size());
1✔
4604
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4605
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4606
            CHECK_EQUAL(tv[i].get<String>(t1_str_col), expected[i].second);
2✔
4607
        }
2✔
4608
    }
1✔
4609
}
1✔
4610

4611
TEST(Query_DistinctAndSort)
4612
{
1✔
4613
    Group g;
1✔
4614
    TableRef t1 = g.add_table("t1");
1✔
4615
    TableRef t2 = g.add_table("t2");
1✔
4616
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4617
    auto t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4618
    auto t1_link_col = t1->add_column(*t2, "t1_link_t2");
1✔
4619
    auto t2_int_col = t2->add_column(type_Int, "t2_int");
1✔
4620

4621
    ObjKeyVector t1_keys({0, 1, 2, 3, 4, 5});
1✔
4622
    ObjKeyVector t2_keys({10, 11, 12, 13, 14, 15});
1✔
4623
    t1->create_objects(t1_keys);
1✔
4624
    t2->create_objects(t2_keys);
1✔
4625

4626
    t1->get_object(t1_keys[0]).set_all(1, "A", t2_keys[1]);
1✔
4627
    t1->get_object(t1_keys[1]).set_all(1, "A", t2_keys[0]);
1✔
4628
    t1->get_object(t1_keys[2]).set_all(1, "B", t2_keys[2]);
1✔
4629
    t1->get_object(t1_keys[3]).set_all(2, "B", t2_keys[3]);
1✔
4630
    t1->get_object(t1_keys[4]).set_all(2, "A", t2_keys[5]);
1✔
4631
    t1->get_object(t1_keys[5]).set_all(2, "A", t2_keys[4]);
1✔
4632

4633
    t2->get_object(t2_keys[0]).set(t2_int_col, 0);
1✔
4634
    t2->get_object(t2_keys[1]).set(t2_int_col, 0);
1✔
4635
    t2->get_object(t2_keys[2]).set(t2_int_col, 1);
1✔
4636
    t2->get_object(t2_keys[3]).set(t2_int_col, 1);
1✔
4637
    t2->get_object(t2_keys[4]).set(t2_int_col, 2);
1✔
4638
    t2->get_object(t2_keys[5]).set(t2_int_col, 2);
1✔
4639

4640
    //     T1                              T2
4641
    //   | t1_int   t1_str   t1_link_t2  | t2_int  |
4642
    //   ===========================================
4643
    // 0 | 1        "A"      1           | 0       |
4644
    // 1 | 1        "A"      0           | 0       |
4645
    // 2 | 1        "B"      2           | 1       |
4646
    // 3 | 2        "B"      3           | 1       |
4647
    // 4 | 2        "A"      5           | 2       |
4648
    // 5 | 2        "A"      4           | 2       |
4649

4650
    using ResultList = std::vector<std::pair<size_t, ObjKey>>; // value, key
1✔
4651
    {                                                          // distinct with no sort keeps original order
1✔
4652
        TableView tv = t1->where().find_all();
1✔
4653
        ResultList expected = {{1, t1_keys[0]}, {2, t1_keys[3]}};
1✔
4654
        tv.distinct(t1_int_col);
1✔
4655
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4656
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4657
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4658
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4659
        }
2✔
4660
    }
1✔
4661
    { // distinct on a sorted view retains sorted order
1✔
4662
        TableView tv = t1->where().find_all();
1✔
4663
        ResultList expected = {{1, t1_keys[0]}, {2, t1_keys[4]}};
1✔
4664
        tv.sort(SortDescriptor({{t1_str_col}, {t1_int_col}}));
1✔
4665
        tv.distinct(t1_int_col);
1✔
4666
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4667
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4668
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4669
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4670
        }
2✔
4671
    }
1✔
4672
    { // distinct on a view sorted descending retains sorted order
1✔
4673
        TableView tv = t1->where().find_all();
1✔
4674
        ResultList expected = {{2, t1_keys[3]}, {1, t1_keys[2]}};
1✔
4675
        tv.sort(SortDescriptor({{t1_str_col}, {t1_int_col}}, {false /* descending */, false /* descending */}));
1✔
4676
        tv.distinct(t1_int_col);
1✔
4677
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4678
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4679
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4680
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4681
        }
2✔
4682
    }
1✔
4683
    { // distinct on a sorted view (different from table order) retains sorted order
1✔
4684
        TableView tv = t1->where().find_all();
1✔
4685
        ResultList expected = {{2, t1_keys[3]}, {1, t1_keys[0]}};
1✔
4686
        tv.sort(t1_int_col, false /* descending */);
1✔
4687
        tv.distinct(t1_int_col);
1✔
4688
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4689
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4690
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4691
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4692
        }
2✔
4693
    }
1✔
4694
    { // distinct across links on an unsorted view retains original order
1✔
4695
        TableView tv = t1->where().find_all();
1✔
4696
        ResultList expected = {{1, t1_keys[0]}, {1, t1_keys[2]}, {2, t1_keys[4]}};
1✔
4697
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
4698
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4699
        for (size_t i = 0; i < tv.size(); ++i) {
4✔
4700
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
3✔
4701
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
3✔
4702
        }
3✔
4703
    }
1✔
4704
    { // distinct on a view sorted across links retains sorted order
1✔
4705
        TableView tv = t1->where().find_all();
1✔
4706
        ResultList expected = {{1, t1_keys[0]}, {2, t1_keys[3]}};
1✔
4707
        tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}));
1✔
4708
        tv.distinct(t1_int_col);
1✔
4709
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4710
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4711
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
2✔
4712
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
2✔
4713
        }
2✔
4714
    }
1✔
4715
    { // distinct across links and sort across links
1✔
4716
        TableView tv = t1->where().find_all();
1✔
4717
        ResultList expected = {{1, t1_keys[0]}, {1, t1_keys[2]}, {2, t1_keys[4]}};
1✔
4718
        tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}));
1✔
4719
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
4720
        CHECK_EQUAL(tv.size(), expected.size());
1✔
4721
        for (size_t i = 0; i < tv.size(); ++i) {
4✔
4722
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), expected[i].first);
3✔
4723
            CHECK_EQUAL(tv.get_key(i), expected[i].second);
3✔
4724
        }
3✔
4725
    }
1✔
4726
}
1✔
4727

4728
TEST(Query_SortDistinctOrderThroughHandover)
4729
{
1✔
4730
    SHARED_GROUP_TEST_PATH(path);
1✔
4731
    std::unique_ptr<Replication> hist_w(make_in_realm_history());
1✔
4732
    DBRef sg_w = DB::create(*hist_w, path, DBOptions(crypt_key()));
1✔
4733
    auto g = sg_w->start_write();
1✔
4734

4735
    TableRef t1 = g->add_table("t1");
1✔
4736
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4737
    auto t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4738

4739
    ObjKey k0 = t1->create_object().set_all(100, "A").get_key();
1✔
4740
    ObjKey k1 = t1->create_object().set_all(200, "A").get_key();
1✔
4741
    ObjKey k2 = t1->create_object().set_all(300, "A").get_key();
1✔
4742
    t1->create_object().set_all(300, "A");
1✔
4743
    ObjKey k4 = t1->create_object().set_all(400, "A").get_key();
1✔
4744

4745
    g->commit_and_continue_as_read();
1✔
4746
    using ResultList = std::vector<std::pair<std::string, ObjKey>>;
1✔
4747

4748
    auto check_across_handover = [&](ResultList results, std::unique_ptr<TableView> tv) {
6✔
4749
        tv->sync_if_needed();
6✔
4750
        CHECK(tv->is_in_sync());
6✔
4751
        CHECK_EQUAL(tv->size(), results.size());
6✔
4752
        for (size_t i = 0; i < tv->size(); ++i) {
16✔
4753
            CHECK_EQUAL(tv->get_object(i).get<String>(t1_str_col), results[i].first);
10✔
4754
            CHECK_EQUAL(tv->get_key(i), results[i].second);
10✔
4755
        }
10✔
4756
    };
6✔
4757

4758
    //     T1
4759
    //   | t1_int     t1_str   |
4760
    //   =======================
4761
    // 0 | 100        "A"      |
4762
    // 1 | 200        "A"      |
4763
    // 2 | 300        "A"      |
4764
    // 3 | 300        "A"      |
4765
    // 4 | 400        "A"      |
4766

4767
    { // sort descending then distinct
1✔
4768
        TableView tv = t1->where().find_all();
1✔
4769
        ResultList results = {{"A", k4}};
1✔
4770
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4771
        tv.distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4772

4773
        CHECK_EQUAL(tv.size(), results.size());
1✔
4774
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4775
            CHECK_EQUAL(tv.get_object(i).get<String>(t1_str_col), results[i].first);
1✔
4776
            CHECK_EQUAL(tv.get_key(i), results[i].second);
1✔
4777
        }
1✔
4778
        auto tr = g->duplicate();
1✔
4779
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4780
        check_across_handover(results, std::move(tv2));
1✔
4781
    }
1✔
4782
    { // sort descending then distinct then limit
1✔
4783
        TableView tv = t1->where().find_all();
1✔
4784
        ResultList results = {};
1✔
4785
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4786
        tv.distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4787
        tv.limit(LimitDescriptor(0));
1✔
4788
        CHECK_EQUAL(tv.size(), results.size());
1✔
4789
        auto tr = g->duplicate();
1✔
4790
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4791
        check_across_handover(results, std::move(tv2));
1✔
4792
    }
1✔
4793
    { // sort descending then distinct then limit and include
1✔
4794
        TableView tv = t1->where().find_all();
1✔
4795
        ResultList results = {};
1✔
4796
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4797
        tv.distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4798
        tv.limit(LimitDescriptor(0));
1✔
4799
        CHECK_EQUAL(tv.size(), results.size());
1✔
4800
        auto tr = g->duplicate();
1✔
4801
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4802
        check_across_handover(results, std::move(tv2));
1✔
4803
    }
1✔
4804
    { // distinct then sort descending
1✔
4805
        TableView tv = t1->where().find_all();
1✔
4806
        std::vector<std::pair<std::string, ObjKey>> results = {{"A", k0}};
1✔
4807
        tv.distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4808
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4809
        CHECK_EQUAL(tv.size(), results.size());
1✔
4810
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4811
            CHECK_EQUAL(tv.get_object(i).get<String>(t1_str_col), results[i].first);
1✔
4812
            CHECK_EQUAL(tv.get_key(i), results[i].second);
1✔
4813
        }
1✔
4814
        auto tr = g->duplicate();
1✔
4815
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4816
        check_across_handover(results, std::move(tv2));
1✔
4817
    }
1✔
4818
    { // sort descending then multicolumn distinct
1✔
4819
        TableView tv = t1->where().find_all();
1✔
4820
        std::vector<std::pair<std::string, ObjKey>> results = {{"A", k4}, {"A", k2}, {"A", k1}, {"A", k0}};
1✔
4821
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4822
        tv.distinct(DistinctDescriptor({{t1_str_col}, {t1_int_col}}));
1✔
4823
        CHECK_EQUAL(tv.size(), results.size());
1✔
4824
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
4825
            CHECK_EQUAL(tv.get_object(i).get<String>(t1_str_col), results[i].first);
4✔
4826
            CHECK_EQUAL(tv.get_key(i), results[i].second);
4✔
4827
        }
4✔
4828
        auto tr = g->duplicate();
1✔
4829
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4830
        check_across_handover(results, std::move(tv2));
1✔
4831
    }
1✔
4832
    { // multicolumn distinct then sort descending
1✔
4833
        TableView tv = t1->where().find_all();
1✔
4834
        std::vector<std::pair<std::string, ObjKey>> results = {{"A", k4}, {"A", k2}, {"A", k1}, {"A", k0}};
1✔
4835
        tv.distinct(DistinctDescriptor({{t1_str_col}, {t1_int_col}}));
1✔
4836
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4837
        CHECK_EQUAL(tv.size(), results.size());
1✔
4838
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
4839
            CHECK_EQUAL(tv.get_object(i).get<String>(t1_str_col), results[i].first);
4✔
4840
            CHECK_EQUAL(tv.get_key(i), results[i].second);
4✔
4841
        }
4✔
4842
        auto tr = g->duplicate();
1✔
4843
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4844
        check_across_handover(results, std::move(tv2));
1✔
4845
    }
1✔
4846
}
1✔
4847

4848
TEST(Query_CompoundDescriptors)
4849
{
1✔
4850
    SHARED_GROUP_TEST_PATH(path);
1✔
4851
    std::unique_ptr<Replication> hist_w(make_in_realm_history());
1✔
4852
    DBRef sg_w = DB::create(*hist_w, path, DBOptions(crypt_key()));
1✔
4853
    auto g = sg_w->start_write();
1✔
4854

4855
    TableRef t1 = g->add_table("t1");
1✔
4856
    ColKey t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4857
    ColKey t1_str_col = t1->add_column(type_String, "t1_str");
1✔
4858

4859
    ObjKey k0 = t1->create_object().set_all(1, "A").get_key();
1✔
4860
    ObjKey k1 = t1->create_object().set_all(1, "A").get_key();
1✔
4861
    ObjKey k2 = t1->create_object().set_all(1, "B").get_key();
1✔
4862
    ObjKey k3 = t1->create_object().set_all(2, "B").get_key();
1✔
4863
    ObjKey k4 = t1->create_object().set_all(2, "A").get_key();
1✔
4864
    ObjKey k5 = t1->create_object().set_all(2, "A").get_key();
1✔
4865

4866
    g->commit_and_continue_as_read();
1✔
4867
    using ResultList = std::vector<std::pair<size_t, ObjKey>>;
1✔
4868

4869
    auto check_across_handover = [&](ResultList results, std::unique_ptr<TableView> tv) {
6✔
4870
        tv->sync_if_needed();
6✔
4871
        CHECK(tv->is_in_sync());
6✔
4872
        CHECK_EQUAL(tv->size(), results.size());
6✔
4873
        for (size_t i = 0; i < tv->size(); ++i) {
27✔
4874
            CHECK_EQUAL(tv->get_object(i).get<Int>(t1_int_col), results[i].first);
21✔
4875
            CHECK_EQUAL(tv->get_key(i), results[i].second);
21✔
4876
        }
21✔
4877
    };
6✔
4878

4879
    //     T1
4880
    //   | t1_int   t1_str  |
4881
    //   ====================
4882
    // 0 | 1        "A"     |
4883
    // 1 | 1        "A"     |
4884
    // 2 | 1        "B"     |
4885
    // 3 | 2        "B"     |
4886
    // 4 | 2        "A"     |
4887
    // 5 | 2        "A"     |
4888

4889
    { // sorting twice should the same as a single sort with both criteria
1✔
4890
        // but reversed: sort(a).sort(b) == sort(b, a)
4891
        ResultList results = {{2, k3}, {1, k2}, {2, k4}, {2, k5}, {1, k0}, {1, k1}};
1✔
4892
        TableView tv = t1->where().find_all();
1✔
4893
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
4894
        tv.sort(SortDescriptor({{t1_str_col}}, {false}));
1✔
4895
        CHECK_EQUAL(tv.size(), results.size());
1✔
4896
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
4897
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
6✔
4898
            CHECK_EQUAL(tv.get_key(i), results[i].second);
6✔
4899
        }
6✔
4900
        auto tr = g->duplicate();
1✔
4901
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4902
        check_across_handover(results, std::move(tv2));
1✔
4903

4904
        tv = t1->where().find_all();
1✔
4905
        tv.sort(SortDescriptor({{t1_str_col}, {t1_int_col}}, {false, false}));
1✔
4906
        CHECK_EQUAL(tv.size(), results.size());
1✔
4907
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
4908
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
6✔
4909
            CHECK_EQUAL(tv.get_key(i), results[i].second);
6✔
4910
        }
6✔
4911
        auto hp = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4912
        check_across_handover(results, std::move(hp));
1✔
4913
    }
1✔
4914

4915
    { // two distincts are not the same as a single distinct with both criteria
1✔
4916
        ResultList results = {{1, k0}, {2, k3}};
1✔
4917
        TableView tv = t1->where().find_all();
1✔
4918
        tv.distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4919
        tv.distinct(DistinctDescriptor({{t1_str_col}}));
1✔
4920
        CHECK_EQUAL(tv.size(), results.size());
1✔
4921
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4922
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
2✔
4923
            CHECK_EQUAL(tv.get_key(i), results[i].second);
2✔
4924
        }
2✔
4925
        auto tr = g->duplicate();
1✔
4926
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4927
        check_across_handover(results, std::move(tv2));
1✔
4928

4929
        results = {{1, k0}, {1, k2}, {2, k3}, {2, k4}};
1✔
4930
        tv = t1->where().find_all();
1✔
4931
        tv.distinct(DistinctDescriptor({{t1_int_col}, {t1_str_col}}));
1✔
4932
        CHECK_EQUAL(tv.size(), results.size());
1✔
4933
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
4934
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
4✔
4935
            CHECK_EQUAL(tv.get_key(i), results[i].second);
4✔
4936
        }
4✔
4937
        auto hp = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4938
        check_across_handover(results, std::move(hp));
1✔
4939
    }
1✔
4940

4941
    { // check results of sort-distinct-sort-distinct
1✔
4942
        TableView tv = t1->where().find_all();
1✔
4943
        tv.sort(SortDescriptor({{t1_str_col}, {t1_int_col}}, {true, true}));
1✔
4944
        tv.distinct(DistinctDescriptor({{t1_int_col}}));
1✔
4945
        ResultList results = {{1, k0}, {2, k4}};
1✔
4946
        CHECK_EQUAL(tv.size(), results.size());
1✔
4947
        for (size_t i = 0; i < tv.size(); ++i) {
3✔
4948
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
2✔
4949
            CHECK_EQUAL(tv.get_key(i), results[i].second);
2✔
4950
        }
2✔
4951
        auto tr = g->duplicate();
1✔
4952
        auto tv2 = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4953
        check_across_handover(results, std::move(tv2));
1✔
4954

4955
        tv.sort(SortDescriptor({{t1_int_col}}, {false})); // = {{2, 4}, {1, 0}}
1✔
4956
        tv.distinct(DistinctDescriptor({{t1_str_col}}));  // = {{2, 4}}
1✔
4957
        results = {{2, k4}};
1✔
4958
        CHECK_EQUAL(tv.size(), results.size());
1✔
4959
        for (size_t i = 0; i < tv.size(); ++i) {
2✔
4960
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i].first);
1✔
4961
            CHECK_EQUAL(tv.get_key(i), results[i].second);
1✔
4962
        }
1✔
4963
        auto hp = tr->import_copy_of(tv, PayloadPolicy::Stay);
1✔
4964
        check_across_handover(results, std::move(hp));
1✔
4965
    }
1✔
4966
}
1✔
4967

4968
TEST(Query_DistinctThroughLinks)
4969
{
1✔
4970
    Group g;
1✔
4971
    TableRef t1 = g.add_table("t1");
1✔
4972
    TableRef t2 = g.add_table("t2");
1✔
4973
    TableRef t3 = g.add_table("t3");
1✔
4974

4975
    auto t1_int_col = t1->add_column(type_Int, "t1_int");
1✔
4976
    auto t1_link_col = t1->add_column(*t2, "t1_link_t2");
1✔
4977

4978
    auto t2_int_col = t2->add_column(type_Int, "t2_int");
1✔
4979
    auto t2_link_col = t2->add_column(*t3, "t2_link_t3");
1✔
4980

4981
    auto t3_int_col = t3->add_column(type_Int, "t3_int", true);
1✔
4982
    auto t3_str_col = t3->add_column(type_String, "t3_str");
1✔
4983

4984
    ObjKeyVector t1_keys({0, 1, 2, 3, 4, 5, 6});
1✔
4985
    ObjKeyVector t2_keys({10, 11, 12, 13, 14, 15});
1✔
4986
    ObjKeyVector t3_keys({20, 21, 22, 23});
1✔
4987
    t1->create_objects(t1_keys);
1✔
4988
    t2->create_objects(t2_keys);
1✔
4989
    t3->create_objects(t3_keys);
1✔
4990

4991
    t1->get_object(t1_keys[0]).set(t1_int_col, 99);
1✔
4992
    for (size_t i = 0; i < t2->size(); i++) {
7✔
4993
        t1->get_object(t1_keys[i + 1]).set(t1_int_col, int64_t(i));
6✔
4994
        t2->get_object(t2_keys[i]).set(t2_int_col, int64_t(t2->size() - i - 1));
6✔
4995
    }
6✔
4996
    t2->get_object(t2_keys[0]).set(t2_int_col, 0);
1✔
4997
    t2->get_object(t2_keys[1]).set(t2_int_col, 0);
1✔
4998

4999
    t1->get_object(t1_keys[0]).set(t1_link_col, t2_keys[1]);
1✔
5000
    t1->get_object(t1_keys[1]).set(t1_link_col, t2_keys[0]);
1✔
5001
    t1->get_object(t1_keys[2]).set(t1_link_col, t2_keys[2]);
1✔
5002
    t1->get_object(t1_keys[3]).set(t1_link_col, t2_keys[3]);
1✔
5003
    t1->get_object(t1_keys[4]).set(t1_link_col, t2_keys[5]);
1✔
5004
    t1->get_object(t1_keys[5]).set(t1_link_col, t2_keys[4]);
1✔
5005
    t1->get_object(t1_keys[6]).set(t1_link_col, t2_keys[1]);
1✔
5006

5007
    t2->get_object(t2_keys[0]).set(t2_link_col, t3_keys[3]);
1✔
5008
    t2->get_object(t2_keys[1]).set(t2_link_col, t3_keys[2]);
1✔
5009
    t2->get_object(t2_keys[2]).set(t2_link_col, t3_keys[0]);
1✔
5010
    t2->get_object(t2_keys[3]).set(t2_link_col, t3_keys[1]);
1✔
5011

5012
    t3->get_object(t3_keys[1]).set(t3_int_col, 4);
1✔
5013
    t3->get_object(t3_keys[2]).set(t3_int_col, 7);
1✔
5014
    t3->get_object(t3_keys[3]).set(t3_int_col, 3);
1✔
5015
    t3->get_object(t3_keys[0]).set(t3_str_col, "b");
1✔
5016
    t3->get_object(t3_keys[1]).set(t3_str_col, "a");
1✔
5017
    t3->get_object(t3_keys[2]).set(t3_str_col, "c");
1✔
5018
    t3->get_object(t3_keys[3]).set(t3_str_col, "k");
1✔
5019

5020
    //  T1                       T2                     T3
5021
    //  t1_int   t1_link_t2  |   t2_int  t2_link_t3 |   t3_int  t3_str
5022
    //  ==============================================================
5023
    //  99       1           |   0       3          |   null    "b"
5024
    //  0        0           |   0       2          |   4       "a"
5025
    //  1        2           |   3       0          |   7       "c"
5026
    //  2        3           |   2       1          |   3       "k"
5027
    //  3        5           |   1       null       |
5028
    //  4        4           |   0       null       |
5029
    //  5        1           |                      |
5030

5031
    {
1✔
5032
        TableView tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5033

5034
        // Test original funcionality through chain class
5035
        std::vector<size_t> results1 = {0, 1, 2, 3, 4, 5};
1✔
5036
        tv.distinct(DistinctDescriptor({{t1_int_col}}));
1✔
5037
        CHECK_EQUAL(tv.size(), results1.size());
1✔
5038
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
5039
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[i]);
6✔
5040
        }
6✔
5041
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5042
        tv.distinct(DistinctDescriptor({{t1_int_col}}));
1✔
5043
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
5044
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[i]); // results haven't been sorted
6✔
5045
        }
6✔
5046
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5047
        tv.sort(SortDescriptor({{t1_int_col}}, {true}));
1✔
5048
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
5049
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[i]); // still same order here by conincidence
6✔
5050
        }
6✔
5051
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5052
        tv.sort(SortDescriptor({{t1_int_col}}, {false}));
1✔
5053
        for (size_t i = 0; i < tv.size(); ++i) {
7✔
5054
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results1[results1.size() - 1 - i]); // now its reversed
6✔
5055
        }
6✔
5056
    }
1✔
5057

5058
    {
1✔
5059
        TableView tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5060

5061
        // Test basic one link chain
5062
        std::vector<size_t> results2 = {0, 1, 2, 4};
1✔
5063
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
5064
        CHECK_EQUAL(tv.size(), results2.size());
1✔
5065
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
5066
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results2[i]);
4✔
5067
        }
4✔
5068

5069
        std::vector<size_t> results2_sorted_link = {0, 4, 2, 1};
1✔
5070
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5071
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
5072
        tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}, {true}));
1✔
5073
        CHECK_EQUAL(tv.size(), results2_sorted_link.size());
1✔
5074
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
5075
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results2_sorted_link[i]);
4✔
5076
        }
4✔
5077
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5078
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
5079
        tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}, {false}));
1✔
5080
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
5081
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results2_sorted_link[results2_sorted_link.size() - 1 - i]);
4✔
5082
        }
4✔
5083
    }
1✔
5084

5085
    {
1✔
5086
        TableView tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5087

5088
        // Test link chain through two links with nulls
5089
        std::vector<size_t> results3 = {0, 1, 2, 5};
1✔
5090
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5091
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_link_col, t3_int_col}}));
1✔
5092
        // Nullified links are excluded from distinct.
5093
        CHECK_EQUAL(tv.size(), results3.size());
1✔
5094
        for (size_t i = 0; i < results3.size(); ++i) {
5✔
5095
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results3[i]);
4✔
5096
        }
4✔
5097

5098
        results3 = {1, 0, 2, 5}; // sorted order on t3_col_int { null, 3, 4, 7 }
1✔
5099
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5100
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_link_col, t3_int_col}}));
1✔
5101
        tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}}));
1✔
5102
        CHECK_EQUAL(tv.size(), results3.size());
1✔
5103
        for (size_t i = 0; i < results3.size(); ++i) {
5✔
5104
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results3[i]);
4✔
5105
        }
4✔
5106
        tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5107
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_link_col, t3_int_col}}));
1✔
5108
        tv.sort(SortDescriptor({{t1_link_col, t2_link_col, t3_int_col}}, {false}));
1✔
5109
        CHECK_EQUAL(tv.size(), results3.size());
1✔
5110
        for (size_t i = 0; i < results3.size(); ++i) {
5✔
5111
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results3[results3.size() - 1 - i]);
4✔
5112
        }
4✔
5113
    }
1✔
5114

5115
    {
1✔
5116
        TableView tv = t1->where().less(t1_int_col, 6).find_all();
1✔
5117

5118
        // Test distinct after sort
5119
        tv.sort(SortDescriptor({{t1_link_col, t2_int_col}}, {true}));
1✔
5120
        //  t1_int   link.t2_int
5121
        //  ====================
5122
        //  0        0
5123
        //  3        0
5124
        //  5        0
5125
        //  4        1
5126
        //  2        2
5127
        //  1        3
5128

5129
        tv.distinct(DistinctDescriptor({{t1_link_col, t2_int_col}}));
1✔
5130
        //  t1_int   link.t2_int
5131
        //  ====================
5132
        //  0        0
5133
        //  4        1
5134
        //  2        2
5135
        //  1        3
5136

5137
        std::vector<size_t> results = {0, 4, 2, 1};
1✔
5138
        CHECK_EQUAL(tv.size(), results.size());
1✔
5139
        for (size_t i = 0; i < tv.size(); ++i) {
5✔
5140
            CHECK_EQUAL(tv[i].get<Int>(t1_int_col), results[i]);
4✔
5141
        }
4✔
5142
    }
1✔
5143
}
1✔
5144

5145
TEST(Query_Sort_And_Requery_Typed1)
5146
{
1✔
5147
    Table ttt;
1✔
5148
    auto col_int = ttt.add_column(type_Int, "1");
1✔
5149
    auto col_str = ttt.add_column(type_String, "2");
1✔
5150

5151
    ttt.create_object().set_all(1, "a"); // 0 *
1✔
5152
    ttt.create_object().set_all(2, "a"); // 1
1✔
5153
    ttt.create_object().set_all(3, "X"); // 2
1✔
5154
    ttt.create_object().set_all(1, "a"); // 3 *
1✔
5155
    ttt.create_object().set_all(2, "a"); // 4
1✔
5156
    ttt.create_object().set_all(3, "X"); // 5
1✔
5157
    ttt.create_object().set_all(9, "a"); // 6 *
1✔
5158
    ttt.create_object().set_all(8, "a"); // 7 *
1✔
5159
    ttt.create_object().set_all(7, "X"); // 8
1✔
5160

5161
    // tv.get_key()  = 0, 2, 3, 5, 6, 7, 8
5162
    // Vals         = 1, 3, 1, 3, 9, 8, 7
5163
    // result       = 3, 0, 5, 2, 8, 7, 6
5164

5165
    Query q = ttt.where().not_equal(col_int, 2);
1✔
5166
    TableView tv = q.find_all();
1✔
5167

5168
    ObjKey match = ttt.where(&tv).equal(col_int, 7).find();
1✔
5169
    CHECK_EQUAL(match, ObjKey(8));
1✔
5170

5171
    tv.sort(col_int);
1✔
5172

5173
    CHECK(tv.size() == 7);
1✔
5174
    CHECK(tv[0].get<Int>(col_int) == 1);
1✔
5175
    CHECK(tv[1].get<Int>(col_int) == 1);
1✔
5176
    CHECK(tv[2].get<Int>(col_int) == 3);
1✔
5177
    CHECK(tv[3].get<Int>(col_int) == 3);
1✔
5178
    CHECK(tv[4].get<Int>(col_int) == 7);
1✔
5179
    CHECK(tv[5].get<Int>(col_int) == 8);
1✔
5180
    CHECK(tv[6].get<Int>(col_int) == 9);
1✔
5181

5182
    Query q2 = ttt.where(&tv).not_equal(col_str, "X");
1✔
5183
    TableView tv2 = q2.find_all();
1✔
5184

5185
    CHECK_EQUAL(4, tv2.size());
1✔
5186
    CHECK_EQUAL(1, tv2[0].get<Int>(col_int));
1✔
5187
    CHECK_EQUAL(1, tv2[1].get<Int>(col_int));
1✔
5188
    CHECK_EQUAL(8, tv2[2].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5189
    CHECK_EQUAL(9, tv2[3].get<Int>(col_int));
1✔
5190

5191
    match = ttt.where(&tv).not_equal(col_str, "X").find();
1✔
5192
    CHECK_EQUAL(match, ObjKey(0));
1✔
5193

5194
    match = ttt.where(&tv).not_equal(col_str, "a").find();
1✔
5195
    CHECK_EQUAL(match, ObjKey(2));
1✔
5196
}
1✔
5197

5198

5199
TEST(Query_Sort_And_Requery_FindFirst)
5200
{
1✔
5201
    Table ttt;
1✔
5202
    auto col_int0 = ttt.add_column(type_Int, "1");
1✔
5203
    auto col_int1 = ttt.add_column(type_Int, "2");
1✔
5204

5205
    ttt.create_object().set_all(1, 60);
1✔
5206
    ttt.create_object().set_all(2, 50); // **
1✔
5207
    ttt.create_object().set_all(3, 40); // *
1✔
5208
    ttt.create_object().set_all(1, 30);
1✔
5209
    ttt.create_object().set_all(2, 20); // **
1✔
5210
    ttt.create_object().set_all(3, 10); // **
1✔
5211

5212
    Query q = ttt.where().greater(col_int0, 1);
1✔
5213
    TableView tv = q.find_all();
1✔
5214
    CHECK_EQUAL(tv.size(), 4);
1✔
5215
    tv.sort(col_int1);
1✔
5216

5217
    // 3, 2, 3, 2
5218
    ObjKey k = ttt.where(&tv).equal(col_int0, 3).find();
1✔
5219
    auto s = ttt.where(&tv).not_equal(col_int1, 40).sum(col_int0);
1✔
5220
    CHECK_EQUAL(k, ObjKey(5));
1✔
5221
    CHECK_EQUAL(s, 7);
1✔
5222
}
1✔
5223

5224

5225
TEST(Query_Sort_And_Requery)
5226
{
1✔
5227
    // New where(tableview) method
5228
    Table table;
1✔
5229
    auto col_int = table.add_column(type_Int, "first1");
1✔
5230
    auto col_str = table.add_column(type_String, "second1");
1✔
5231

5232
    table.create_object().set_all(1, "a");
1✔
5233
    table.create_object().set_all(2, "a");
1✔
5234
    table.create_object().set_all(3, "X");
1✔
5235
    table.create_object().set_all(1, "a");
1✔
5236
    table.create_object().set_all(2, "a");
1✔
5237
    table.create_object().set_all(3, "X");
1✔
5238
    table.create_object().set_all(9, "a");
1✔
5239
    table.create_object().set_all(8, "a");
1✔
5240
    table.create_object().set_all(7, "X");
1✔
5241

5242
    Query q = table.where().not_equal(col_int, 2);
1✔
5243
    TableView tv = q.find_all();
1✔
5244
    tv.sort(col_int);
1✔
5245

5246
    CHECK(tv.size() == 7);
1✔
5247

5248
    CHECK(tv[0].get<Int>(col_int) == 1);
1✔
5249
    CHECK(tv[1].get<Int>(col_int) == 1);
1✔
5250
    CHECK(tv[2].get<Int>(col_int) == 3);
1✔
5251
    CHECK(tv[3].get<Int>(col_int) == 3);
1✔
5252
    CHECK(tv[4].get<Int>(col_int) == 7);
1✔
5253
    CHECK(tv[5].get<Int>(col_int) == 8);
1✔
5254
    CHECK(tv[6].get<Int>(col_int) == 9);
1✔
5255

5256
    Query q2 = table.where(&tv).not_equal(col_str, "X");
1✔
5257
    TableView tv2 = q2.find_all();
1✔
5258

5259
    CHECK_EQUAL(4, tv2.size());
1✔
5260
    CHECK_EQUAL(1, tv2[0].get<Int>(col_int));
1✔
5261
    CHECK_EQUAL(1, tv2[1].get<Int>(col_int));
1✔
5262
    CHECK_EQUAL(8, tv2[2].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5263
    CHECK_EQUAL(9, tv2[3].get<Int>(col_int));
1✔
5264

5265
    Query q3 = table.where(&tv2).not_equal(col_str, "X");
1✔
5266
    TableView tv3 = q3.find_all();
1✔
5267

5268
    CHECK_EQUAL(4, tv3.size());
1✔
5269
    CHECK_EQUAL(1, tv3[0].get<Int>(col_int));
1✔
5270
    CHECK_EQUAL(1, tv3[1].get<Int>(col_int));
1✔
5271
    CHECK_EQUAL(8, tv3[2].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5272
    CHECK_EQUAL(9, tv3[3].get<Int>(col_int));
1✔
5273

5274
    // Test that remove() maintains order
5275
    tv3.get_object(0).remove();
1✔
5276
    tv3.sync_if_needed();
1✔
5277
    // q5 and q3 should behave the same.
5278
    Query q5 = table.where(&tv2).not_equal(col_str, "X");
1✔
5279
    TableView tv5 = q5.find_all();
1✔
5280
    tv5.sync_if_needed(); // you may think tv5 is in sync, BUT it was generated from tv2 which wasn't
1✔
5281
    // Note the side effect - as tv5 depends on ... on tv2 etc, all views are synchronized.
5282
    CHECK_EQUAL(3, tv5.size());
1✔
5283
    CHECK_EQUAL(1, tv5[0].get<Int>(col_int));
1✔
5284
    CHECK_EQUAL(8, tv5[1].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5285
    CHECK_EQUAL(9, tv5[2].get<Int>(col_int));
1✔
5286

5287
    CHECK_EQUAL(6, tv.size());
1✔
5288
    CHECK_EQUAL(3, tv3.size());
1✔
5289
    CHECK_EQUAL(1, tv3[0].get<Int>(col_int));
1✔
5290
    CHECK_EQUAL(8, tv3[1].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5291
    CHECK_EQUAL(9, tv3[2].get<Int>(col_int));
1✔
5292

5293
    Query q4 = table.where(&tv3).not_equal(col_str, "X");
1✔
5294
    TableView tv4 = q4.find_all();
1✔
5295

5296
    CHECK_EQUAL(3, tv4.size());
1✔
5297
    CHECK_EQUAL(1, tv4[0].get<Int>(col_int));
1✔
5298
    CHECK_EQUAL(8, tv4[1].get<Int>(col_int)); // 8, 9 (sort order) instead of 9, 8 (table order)
1✔
5299
    CHECK_EQUAL(9, tv4[2].get<Int>(col_int));
1✔
5300
}
1✔
5301

5302

5303
TEST(Query_Sort_And_Requery_Untyped_Monkey2)
5304
{
1✔
5305
    // New where(tableview) method
5306
    for (int iter = 0; iter < 1; iter++) {
2✔
5307
        size_t b;
1✔
5308
        Table table;
1✔
5309
        auto col_int0 = table.add_column(type_Int, "first1");
1✔
5310
        auto col_int1 = table.add_column(type_Int, "second1");
1✔
5311

5312
        // Add random data to table
5313
        for (size_t t = 0; t < 2 * REALM_MAX_BPNODE_SIZE; t++) {
2,001✔
5314
            int64_t val1 = rand() % 5;
2,000✔
5315
            int64_t val2 = rand() % 5;
2,000✔
5316
            table.create_object().set_all(val1, val2);
2,000✔
5317
        }
2,000✔
5318

5319
        // Query and sort
5320
        Query q = table.where().equal(col_int1, 2);
1✔
5321
        TableView tv = q.find_all();
1✔
5322
        tv.sort(col_int0);
1✔
5323

5324
        // Requery and keep original sort order
5325
        Query q2 = table.where(&tv).not_equal(col_int0, 3);
1✔
5326
        TableView tv2 = q2.find_all();
1✔
5327

5328
        b = 0;
1✔
5329
        // Test if sort order is the same as original
5330
        for (size_t t = 0; t < tv2.size(); t++) {
329✔
5331
            ObjKey a = tv2.get_key(t);
328✔
5332
            REALM_ASSERT_EX(b < tv.size(), b, tv.size());
328✔
5333
            while (a != tv.get_key(b)) {
746✔
5334
                b++;
418✔
5335
            }
418✔
5336
        }
328✔
5337

5338
        // We cannot test remove() if query resulted in 0 items
5339
        if (tv2.size() == 0)
1✔
5340
            continue;
×
5341

5342
        size_t remove = rand() % tv2.size();
1✔
5343
        static_cast<void>(remove);
1✔
5344

5345
        Query q3 = table.where(&tv2).not_equal(col_int0, 2);
1✔
5346
        TableView tv3 = q3.find_all();
1✔
5347

5348
        b = 0;
1✔
5349
        // Test if sort order is the same as original
5350
        for (size_t t = 0; t < tv3.size(); t++) {
245✔
5351
            ObjKey a = tv3.get_key(t);
244✔
5352
            REALM_ASSERT_EX(b < tv2.size(), b, tv2.size());
244✔
5353
            while (a != tv2.get_key(b)) {
571✔
5354
                b++;
327✔
5355
                CHECK(b < tv2.size());
327✔
5356
            }
327✔
5357
        }
244✔
5358

5359
        // Now test combinations of sorted and non-sorted tableviews
5360
        Query q4 = table.where().not_equal(col_int0, 1);
1✔
5361
        TableView tv4 = q4.find_all();
1✔
5362

5363
        Query q5 = table.where(&tv4).not_equal(col_int0, 2);
1✔
5364
        TableView tv5 = q5.find_all();
1✔
5365

5366
        for (size_t t = 1; t < tv5.size(); t++) {
1,222✔
5367
            CHECK(tv5.get_key(t - 1) < tv5.get_key(t));
1,221✔
5368
        }
1,221✔
5369

5370
        // Test that tv5 is ordered the same way as tv4 (tv5 is subset of tv4)
5371
        size_t foreignindex = 0;
1✔
5372
        for (size_t t = 0; t < tv5.size(); t++) {
1,223✔
5373
            size_t foreignindex2 = 0;
1,222✔
5374
            while (tv4.get_key(foreignindex2) != tv5.get_key(t))
995,504✔
5375
                foreignindex2++;
994,282✔
5376

5377
            CHECK(foreignindex2 >= foreignindex);
1,222✔
5378
            foreignindex = foreignindex2;
1,222✔
5379
        }
1,222✔
5380

5381
        // New test where both tableviews are sorted according to a column, and both sets are equal
5382
        Query q6 = table.where().not_equal(col_int0, 2);
1✔
5383
        TableView tv6 = q6.find_all();
1✔
5384

5385
        Query q7 = table.where(&tv6).not_equal(col_int0, 2);
1✔
5386
        TableView tv7 = q7.find_all();
1✔
5387

5388
        // Test that tv7 is ordered the same way as tv6
5389
        foreignindex = 0;
1✔
5390
        for (size_t t = 0; t < tv5.size(); t++) {
1,223✔
5391
            size_t foreignindex2 = 0;
1,222✔
5392
            while (tv4.get_key(foreignindex2) != tv5.get_key(t))
995,504✔
5393
                foreignindex2++;
994,282✔
5394

5395
            CHECK(foreignindex2 >= foreignindex);
1,222✔
5396
            foreignindex = foreignindex2;
1,222✔
5397
        }
1,222✔
5398

5399
        tv7.sort(col_int1);
1✔
5400
        tv6.sort(col_int1);
1✔
5401

5402
        // Test that tv7 is ordered the same way as tv6
5403
        foreignindex = 0;
1✔
5404
        for (size_t t = 0; t < tv5.size(); t++) {
1,223✔
5405
            size_t foreignindex2 = 0;
1,222✔
5406
            while (tv4.get_key(foreignindex2) != tv5.get_key(t))
995,504✔
5407
                foreignindex2++;
994,282✔
5408

5409
            CHECK(foreignindex2 >= foreignindex);
1,222✔
5410
            foreignindex = foreignindex2;
1,222✔
5411
        }
1,222✔
5412
    }
1✔
5413
}
1✔
5414

5415
TEST(Query_Performance)
5416
{
1✔
5417
    Group g;
1✔
5418
    auto foo = g.add_table("Foo");
1✔
5419
    auto bar = g.add_table("Bar");
1✔
5420

5421
    auto col_double = foo->add_column(type_Double, "doubles");
1✔
5422
    auto col_int = foo->add_column(type_Int, "ints");
1✔
5423
    auto col_link = bar->add_column(*foo, "links");
1✔
5424
    auto col_linklist = bar->add_column_list(*foo, "linklists");
1✔
5425

5426
    for (int i = 0; i < 10000; i++) {
10,001✔
5427
        auto obj = foo->create_object();
10,000✔
5428
        obj.set(col_double, double(i % 19));
10,000✔
5429
        obj.set(col_int, 30 - (i % 19));
10,000✔
5430
    }
10,000✔
5431
    auto it = foo->begin();
1✔
5432
    for (int i = 0; i < 1000; i++) {
1,001✔
5433
        auto obj = bar->create_object();
1,000✔
5434
        obj.set(col_link, it->get_key());
1,000✔
5435
        auto ll = obj.get_linklist(col_linklist);
1,000✔
5436
        for (size_t j = 0; j < 10; j++) {
11,000✔
5437
            ll.add(it->get_key());
10,000✔
5438
            ++it;
10,000✔
5439
        }
10,000✔
5440
    }
1,000✔
5441

5442
    auto t1 = steady_clock::now();
1✔
5443

5444
    size_t cnt = (foo->column<double>(col_double) > 10).count();
1✔
5445
    CHECK_EQUAL(cnt, 4208);
1✔
5446

5447
    auto t2 = steady_clock::now();
1✔
5448
    CHECK(t2 > t1);
1✔
5449

5450
    cnt = (bar->link(col_link).column<double>(col_double) > 10).count();
1✔
5451
    CHECK_EQUAL(cnt, 421);
1✔
5452

5453
    auto t3 = steady_clock::now();
1✔
5454
    CHECK(t3 > t2);
1✔
5455
    CALLGRIND_START_INSTRUMENTATION;
1✔
5456

5457
    cnt = (bar->link(col_linklist).column<double>(col_double) > 15).count();
1✔
5458
    CHECK_EQUAL(cnt, 630);
1✔
5459

5460
    CALLGRIND_STOP_INSTRUMENTATION;
1✔
5461
    auto t4 = steady_clock::now();
1✔
5462
    CHECK(t4 > t3);
1✔
5463

5464
    cnt = (foo->column<double>(col_double) > foo->column<Int>(col_int)).count();
1✔
5465
    CHECK_EQUAL(cnt, 1578);
1✔
5466

5467
    auto t5 = steady_clock::now();
1✔
5468
    CHECK(t5 > t4);
1✔
5469

5470
    /*
5471
    std::cout << "Row against constant: " << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
5472
    std::cout << "Linked row against constant: " << duration_cast<microseconds>(t3 - t2).count() << " us"
5473
              << std::endl;
5474
    std::cout << "List row against constant: " << duration_cast<microseconds>(t4 - t3).count() << " us" << std::endl;
5475
    std::cout << "Row against row: " << duration_cast<microseconds>(t5 - t4).count() << " us" << std::endl;
5476
    */
5477
}
1✔
5478

5479
TEST(Query_AllocatorBug_DestOlderThanSource)
5480
{
1✔
5481
    // At some point this test failed when cluster node size was 4.
5482
    Group g;
1✔
5483
    auto foo = g.add_table("Foo");
1✔
5484
    auto bar = g.add_table("Bar");
1✔
5485

5486
    auto col_double = foo->add_column(type_Double, "doubles");
1✔
5487
    auto col_int = foo->add_column(type_Int, "ints");
1✔
5488
    auto col_link = bar->add_column(*foo, "links");
1✔
5489
    auto col_linklist = bar->add_column_list(*foo, "linklists");
1✔
5490

5491
    for (int i = 0; i < 10000; i++) {
10,001✔
5492
        auto obj = foo->create_object();
10,000✔
5493
        obj.set(col_double, double(i % 19));
10,000✔
5494
        obj.set(col_int, 30 - (i % 19));
10,000✔
5495
    }
10,000✔
5496

5497
    // At this point the WrappedAllocator in "foo" points to a translation table with 6 elements
5498

5499
    auto it = foo->begin();
1✔
5500
    for (int i = 0; i < 1000; i++) {
1,001✔
5501
        // During this a new slab is needed, so the translation table in "bar" contains 7 elements.
5502
        // Some clusters if "bar" will use this last element for translation
5503
        auto obj = bar->create_object();
1,000✔
5504
        obj.set(col_link, it->get_key());
1,000✔
5505
        auto ll = obj.get_linklist(col_linklist);
1,000✔
5506
        for (size_t j = 0; j < 10; j++) {
11,000✔
5507
            ll.add(it->get_key());
10,000✔
5508
            ++it;
10,000✔
5509
        }
10,000✔
5510
    }
1,000✔
5511

5512
    // When traversion clusters in "bar" wee should use the "bar" wrapped allocator and not the
5513
    // one in "foo" (that was the error)
5514
    auto cnt = (bar->link(col_link).column<double>(col_double) > 10).count();
1✔
5515
    CHECK_EQUAL(cnt, 421);
1✔
5516
}
1✔
5517

5518
TEST(Query_AllocatorBug_SourceOlderThanDest)
5519
{
1✔
5520
    Group g;
1✔
5521
    auto foo = g.add_table("Foo");
1✔
5522
    auto bar = g.add_table("Bar");
1✔
5523

5524
    auto col_double = foo->add_column_list(type_Double, "doubles");
1✔
5525
    auto col_link = bar->add_column(*foo, "links");
1✔
5526
    auto col_linklist = bar->add_column_list(*foo, "linklists");
1✔
5527

5528
    for (int i = 0; i < 10000; i++)
10,001✔
5529
        foo->create_object();
10,000✔
5530

5531
    // foo's WrappedAllocator now points to a translation table with 2 elements
5532

5533
    auto it = foo->begin();
1✔
5534
    for (int i = 0; i < 1000; i++) {
1,001✔
5535
        auto obj = bar->create_object();
1,000✔
5536
        obj.set(col_link, it->get_key());
1,000✔
5537
        auto ll = obj.get_linklist(col_linklist);
1,000✔
5538
        for (size_t j = 0; j < 10; j++) {
11,000✔
5539
            ll.add(it->get_key());
10,000✔
5540
            ++it;
10,000✔
5541
        }
10,000✔
5542
    }
1,000✔
5543

5544
    // bar's WrappedAllocator now points to a translation table with 3 elements
5545

5546
    int i = 0;
1✔
5547
    for (auto& obj : *foo) {
10,000✔
5548
        obj.get_list<double>(col_double).add(double(i % 19));
10,000✔
5549
        ++i;
10,000✔
5550
    }
10,000✔
5551

5552
    // foo's WrappedAllocator now points to a translation table with 6 elements
5553

5554
    // If this query uses bar's allocator to access foo it'll perform an out-of-bounds read
5555
    // for any of the values in slabs 3-5
5556
    auto cnt = (bar->link(col_link).column<Lst<double>>(col_double) > 10).count();
1✔
5557
    CHECK_EQUAL(cnt, 421);
1✔
5558
    cnt = (bar->link(col_link).column<Lst<double>>(col_double).size() == 1).count();
1✔
5559
    cnt = (bar->link(col_link).column<Lst<double>>(col_double).min() == 1).count();
1✔
5560
}
1✔
5561

5562
TEST(Query_LinkToDictionary)
5563
{
1✔
5564
    Group g;
1✔
5565
    auto target = g.add_table("target");
1✔
5566
    auto dict_col = target->add_column_dictionary(type_String, "string", true);
1✔
5567
    auto source = g.add_table("source");
1✔
5568
    auto link_col = source->add_column(*target, "link");
1✔
5569

5570
    for (int i = 0; i < 200; ++i) {
201✔
5571
        auto target_obj = target->create_object();
200✔
5572
        target_obj.get_dictionary(dict_col).insert("key", "value");
200✔
5573
        source->create_object().set_all(target_obj.get_key());
200✔
5574
    }
200✔
5575

5576
    // Will crash if this uses the wrong allocator
5577
    auto q = source->link(link_col).column<Dictionary>(dict_col) == StringData();
1✔
5578
    CHECK_EQUAL(q.count(), 0);
1✔
5579
}
1✔
5580

5581
TEST(Query_StringNodeEqualBaseBug)
5582
{
1✔
5583
    Group g;
1✔
5584
    TableRef table = g.add_table("table");
1✔
5585
    auto col_type = table->add_column(type_String, "type");
1✔
5586
    auto col_tags = table->add_column(type_String, "tags");
1✔
5587
    table->add_search_index(col_type);
1✔
5588

5589
    // Create 2 clusters
5590
    for (int i = 0; i < 500; i++) {
501✔
5591
        table->create_object().set(col_type, "project").set(col_tags, "tag001");
500✔
5592
    }
500✔
5593

5594
    Query q = table->where()
1✔
5595
                  .equal(col_type, StringData("test"), false)
1✔
5596
                  .Or()
1✔
5597
                  .contains(col_tags, StringData("tag005"), false);
1✔
5598
    auto tv = q.find_all();
1✔
5599
    CHECK_EQUAL(tv.size(), 0);
1✔
5600
    table->begin()->set(col_type, "task");
1✔
5601
    tv.sync_if_needed();
1✔
5602
    CHECK_EQUAL(tv.size(), 0);
1✔
5603
}
1✔
5604

5605
// Disabled because it is timing-dependent and frequently causes spurious failures on CI.
5606
TEST_IF(Query_OptimalNode, false)
5607
{
×
5608
    const char* types[9] = {"todo", "task", "issue", "report", "test", "item", "epic", "story", "flow"};
×
5609
    Group g;
×
5610
    TableRef table = g.add_table("table");
×
5611
    auto col_type = table->add_column(type_String, "type");
×
5612
    auto col_tags = table->add_column(type_String, "tags");
×
5613
    table->add_search_index(col_type);
×
5614

5615
    for (int i = 0; i < 10000; i++) {
×
5616
        auto obj = table->create_object();
×
5617
        std::string type = types[i % 9];
×
5618
        std::string val = type + util::to_string(i % 10);
×
5619
        obj.set(col_type, val);
×
5620
        std::string tags;
×
5621
        for (int j = 0; j < 7; j++) {
×
5622
            tags += " TAG" + util::to_string(i % 500);
×
5623
        }
×
5624
        obj.set(col_tags, tags);
×
5625
    }
×
5626

5627
    auto q1 = table->where().equal(col_type, StringData("todo0"), false);
×
5628
    q1.count(); // Warm up
×
5629
    auto t1 = steady_clock::now();
×
5630
    auto cnt = q1.count();
×
5631
    auto t2 = steady_clock::now();
×
5632
    CHECK_EQUAL(cnt, 112);
×
5633
    auto dur1 = duration_cast<microseconds>(t2 - t1).count();
×
5634
    // std::cout << "cnt: " << cnt << " dur1: " << dur1 << " us" << std::endl;
5635

5636
    auto q2 = table->where().contains(col_tags, StringData("tag0"), false);
×
5637
    q2.count(); // Warm up
×
5638
    t1 = steady_clock::now();
×
5639
    cnt = q2.count();
×
5640
    t2 = steady_clock::now();
×
5641
    CHECK_EQUAL(cnt, 20);
×
5642
    auto dur2 = duration_cast<microseconds>(t2 - t1).count();
×
5643
    // std::cout << "cnt: " << cnt << " dur2: " << dur2 << " us" << std::endl;
5644

5645
    Query q = q1.and_query(q2);
×
5646
    q.count(); // Warm up
×
5647
    t1 = steady_clock::now();
×
5648
    cnt = q.count();
×
5649
    t2 = steady_clock::now();
×
5650
    CHECK_EQUAL(cnt, 3);
×
5651
    auto dur3 = duration_cast<microseconds>(t2 - t1).count();
×
5652

5653
    // The duration of the combined query should be closer to the duration for
5654
    // the query using the index.
5655
    CHECK_GREATER(dur3, dur1);
×
5656
    CHECK_LESS(dur3, dur2 / 5);
×
5657
    // std::cout << "cnt: " << cnt << " dur3: " << dur3 << " us" << std::endl;
5658
}
×
5659

5660
NONCONCURRENT_TEST(Query_IntPerformance)
5661
{
1✔
5662
    Table table;
1✔
5663
    auto col_1 = table.add_column(type_Int, "a");
1✔
5664
    auto col_2 = table.add_column(type_Int, "b");
1✔
5665

5666
    for (int i = 0; i < 1000; i++) {
1,001✔
5667
        table.create_object().set(col_1, i).set(col_2, i == 500 ? 500 : 2);
1,000✔
5668
    }
1,000✔
5669

5670
    Query q1 = table.where().equal(col_2, 2);
1✔
5671
    Query q2 = table.where().not_equal(col_1, 500);
1✔
5672
    Query q3 = table.query("a == b");
1✔
5673

5674
    auto t1 = steady_clock::now();
1✔
5675

5676
    CALLGRIND_START_INSTRUMENTATION;
1✔
5677

5678
    size_t nb_reps = 1000;
1✔
5679
    for (size_t t = 0; t < nb_reps; t++) {
1,001✔
5680
        TableView tv = q1.find_all();
1,000✔
5681
        CHECK_EQUAL(tv.size(), 999);
1,000✔
5682
    }
1,000✔
5683

5684
    auto t2 = steady_clock::now();
1✔
5685

5686
    for (size_t t = 0; t < nb_reps; t++) {
1,001✔
5687
        TableView tv = q2.find_all();
1,000✔
5688
        CHECK_EQUAL(tv.size(), 999);
1,000✔
5689
    }
1,000✔
5690

5691
    auto t3 = steady_clock::now();
1✔
5692

5693
    for (size_t t = 0; t < nb_reps; t++) {
1,001✔
5694
        TableView tv = q3.find_all();
1,000✔
5695
        CHECK_EQUAL(tv.size(), 2);
1,000✔
5696
    }
1,000✔
5697

5698
    auto t4 = steady_clock::now();
1✔
5699

5700
    for (size_t t = 0; t < nb_reps; t++) {
1,001✔
5701
        auto sum = q2.sum(col_2);
1,000✔
5702
        CHECK_EQUAL(sum, 1998);
1,000✔
5703
    }
1,000✔
5704

5705
    CALLGRIND_STOP_INSTRUMENTATION;
1✔
5706

5707
    auto t5 = steady_clock::now();
1✔
5708

5709
    std::cout << nb_reps << " repetitions in Query_IntPerformance" << std::endl;
1✔
5710
    std::cout << "    time equal: " << duration_cast<nanoseconds>(t2 - t1).count() / nb_reps << " ns/rep"
1✔
5711
              << std::endl;
1✔
5712
    std::cout << "    time not_equal: " << duration_cast<nanoseconds>(t3 - t2).count() / nb_reps << " ns/rep"
1✔
5713
              << std::endl;
1✔
5714
    std::cout << "    time a == b: " << duration_cast<nanoseconds>(t4 - t3).count() / nb_reps << " ns/rep"
1✔
5715
              << std::endl;
1✔
5716
    std::cout << "    time sum: " << duration_cast<nanoseconds>(t5 - t4).count() / nb_reps << " ns/rep" << std::endl;
1✔
5717
}
1✔
5718

5719
TEST(Query_NotWithEmptyGroup)
5720
{
1✔
5721
    Group g;
1✔
5722
    TableRef table = g.add_table("table");
1✔
5723
    auto col = table->add_column(type_String, "type");
1✔
5724
    table->create_object().set(col, "hello");
1✔
5725
    auto q = table->where().equal(col, "hello").Or().Not().group().end_group();
1✔
5726
    CHECK_EQUAL(q.count(), 1);
1✔
5727
    q = table->where().Not().group().end_group().Or().equal(col, "hello");
1✔
5728
    CHECK_EQUAL(q.count(), 1);
1✔
5729
}
1✔
5730

5731
TEST(Query_AsymmetricObjects)
5732
{
1✔
5733
    Group g;
1✔
5734
    TableRef table = g.add_table("table", Table::Type::TopLevelAsymmetric);
1✔
5735
    auto col = table->add_column(type_String, "type");
1✔
5736
    table->create_object().set(col, "hello");
1✔
5737
    CHECK_LOGIC_ERROR(table->where().equal(col, "hello").Or().Not().group().end_group(),
1✔
5738
                      ErrorCodes::IllegalOperation);
1✔
5739
}
1✔
5740

5741
TEST(Query_NestedLinkCount)
5742
{
1✔
5743
    Group g;
1✔
5744
    auto table = g.add_table_with_primary_key("class_TestClass", type_Int, "id");
1✔
5745
    table->add_column_list(*table, "children");
1✔
5746
    table->add_column_dictionary(*table, "dictionary");
1✔
5747
    table->add_column_list(*table, "list");
1✔
5748
    table->add_column(*table, "Object");
1✔
5749

5750
    Obj o1 = table->create_object_with_primary_key(1);
1✔
5751
    Obj o2 = table->create_object_with_primary_key(2);
1✔
5752
    Obj o3 = table->create_object_with_primary_key(3);
1✔
5753

5754
    o2.get_linklist("children").add(o1.get_key());
1✔
5755
    auto dict = o2.get_dictionary("dictionary");
1✔
5756
    dict.insert("key", o3);
1✔
5757
    o3.get_linklist("children").add(o2.get_key());
1✔
5758

5759
    o1.set("Object", o1.get_key());
1✔
5760
    o2.set("Object", o2.get_key());
1✔
5761
    o3.set("Object", o2.get_key());
1✔
5762

5763
    auto q = table->query("children.list.@size == 0");
1✔
5764
    CHECK_EQUAL(q.count(), 2);
1✔
5765

5766
    q = table->query("@links.TestClass.children.dictionary.@size == 0");
1✔
5767
    CHECK_EQUAL(q.count(), 1); // Only o2
1✔
5768

5769
    q = table->query("@links.TestClass.children.list.@size == 0");
1✔
5770
    CHECK_EQUAL(q.count(), 2);
1✔
5771

5772
    o3.get_dictionary("dictionary").insert("key", o1);
1✔
5773
    auto list = o3.get_linklist("list");
1✔
5774
    list.add(o1.get_key());
1✔
5775
    list.add(o1.get_key());
1✔
5776
    list.add(o1.get_key());
1✔
5777
    list.add(o1.get_key());
1✔
5778
    list.add(o1.get_key());
1✔
5779

5780
    q = table->query("@links.TestClass.children.list.@size == 5");
1✔
5781
    CHECK_EQUAL(q.count(), 1);
1✔
5782

5783
    dict.insert("key1", o3);
1✔
5784
    dict.insert("key2", o3);
1✔
5785
    dict.insert("key3", o3);
1✔
5786
    dict.insert("key4", o3);
1✔
5787
    q = table->query("@links.TestClass.children.dictionary.@size == 5");
1✔
5788
    CHECK_EQUAL(q.count(), 1); // Only o2
1✔
5789

5790
    q = table->query("@links.TestClass.Object.@size > 0");
1✔
5791
    CHECK_EQUAL(q.count(), 2);
1✔
5792
    q = table->query("@links.TestClass.Object.Object.@size > 0");
1✔
5793
    CHECK_EQUAL(q.count(), 2);
1✔
5794
    q = table->query("Object.@links.TestClass.Object.@size > 0");
1✔
5795
    CHECK_EQUAL(q.count(), 3);
1✔
5796
}
1✔
5797

5798
#endif // TEST_QUERY
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc