Passed
Pull Request — 4 (#10173)
by Guy
08:47
created

DataQueryTest::testCustomFieldWithAliasSort()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 10
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 12
rs 9.9332
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\ORM\DataQuery;
6
use SilverStripe\ORM\DataObject;
7
use SilverStripe\ORM\DB;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\ORM\Tests\DataQueryTest\ObjectE;
10
use SilverStripe\Security\Member;
11
12
/**
13
 * @skipUpgrade
14
 */
15
class DataQueryTest extends SapphireTest
16
{
17
18
    protected static $fixture_file = 'DataQueryTest.yml';
19
20
    protected static $extra_dataobjects = [
21
        DataQueryTest\ObjectA::class,
22
        DataQueryTest\ObjectB::class,
23
        DataQueryTest\ObjectC::class,
24
        DataQueryTest\ObjectD::class,
25
        DataQueryTest\ObjectE::class,
26
        DataQueryTest\ObjectF::class,
27
        DataQueryTest\ObjectG::class,
28
        DataQueryTest\ObjectH::class,
29
        DataQueryTest\ObjectI::class,
30
        SQLSelectTest\TestObject::class,
31
        SQLSelectTest\TestBase::class,
32
        SQLSelectTest\TestChild::class,
33
    ];
34
35
    public function testSortByJoinedFieldRetainsSourceInformation()
36
    {
37
        $bar = new DataQueryTest\ObjectC();
38
        $bar->Title = "Bar";
39
        $bar->write();
40
41
        $foo = new DataQueryTest\ObjectB();
42
        $foo->Title = "Foo";
43
        $foo->TestC = $bar->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property TestC does not exist on SilverStripe\ORM\Tests\DataQueryTest\ObjectB. Since you implemented __set, consider adding a @property annotation.
Loading history...
44
        $foo->write();
45
46
        $query = new DataQuery(DataQueryTest\ObjectB::class);
47
        $result = $query->leftJoin(
48
            'DataQueryTest_C',
49
            "\"DataQueryTest_B\".\"TestCID\" = \"DataQueryTest_B\".\"ID\""
50
        )->sort('"DataQueryTest_B"."Title"', 'ASC');
51
52
        $result = $result->execute()->record();
53
        $this->assertEquals('Foo', $result['Title']);
54
    }
55
56
    /**
57
     * Test the leftJoin() and innerJoin method of the DataQuery object
58
     */
59
    public function testJoins()
60
    {
61
        $dq = new DataQuery(Member::class);
62
        $dq->innerJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
63
        $this->assertSQLContains(
64
            "INNER JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
65
            $dq->sql($parameters)
66
        );
67
68
        $dq = new DataQuery(Member::class);
69
        $dq->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
70
        $this->assertSQLContains(
71
            "LEFT JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
72
            $dq->sql($parameters)
73
        );
74
    }
75
76
    public function testApplyRelation()
77
    {
78
        // Test applyRelation with two has_ones pointing to the same class
79
        $dq = new DataQuery(DataQueryTest\ObjectB::class);
80
        $dq->applyRelation('TestC');
81
        $this->assertTrue($dq->query()->isJoinedTo('testc_DataQueryTest_C'));
82
        $this->assertStringContainsString('"testc_DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCID"', $dq->sql());
83
84
        $dq = new DataQuery(DataQueryTest\ObjectB::class);
85
        $dq->applyRelation('TestCTwo');
86
        $this->assertTrue($dq->query()->isJoinedTo('testctwo_DataQueryTest_C'));
87
        $this->assertStringContainsString('"testctwo_DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCTwoID"', $dq->sql());
88
    }
89
90
    public function testApplyRelationDeepInheritance()
91
    {
92
        //test has_one relation
93
        $newDQ = new DataQuery(DataQueryTest\ObjectE::class);
94
        //apply a relation to a relation from an ancestor class
95
        $newDQ->applyRelation('TestA');
96
        $this->assertTrue($newDQ->query()->isJoinedTo('DataQueryTest_C'));
97
        $this->assertStringContainsString('"testa_DataQueryTest_A"."ID" = "DataQueryTest_C"."TestAID"', $newDQ->sql($params));
98
99
        //test many_many relation
100
101
        //test many_many with separate inheritance
102
        $newDQ = new DataQuery(DataQueryTest\ObjectC::class);
103
        $baseDBTable = DataObject::getSchema()->baseDataTable(DataQueryTest\ObjectC::class);
104
        $newDQ->applyRelation('ManyTestAs');
105
        //check we are "joined" to the DataObject's table (there is no distinction between FROM or JOIN clauses)
106
        $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable));
107
        //check we are explicitly selecting "FROM" the DO's table
108
        $this->assertStringContainsString("FROM \"$baseDBTable\"", $newDQ->sql());
109
110
        //test many_many with shared inheritance
111
        $newDQ = new DataQuery(DataQueryTest\ObjectE::class);
112
        $baseDBTable = DataObject::getSchema()->baseDataTable(DataQueryTest\ObjectE::class);
113
        //check we are "joined" to the DataObject's table (there is no distinction between FROM or JOIN clauses)
114
        $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable));
115
        //check we are explicitly selecting "FROM" the DO's table
116
        $this->assertStringContainsString("FROM \"$baseDBTable\"", $newDQ->sql(), 'The FROM clause is missing from the query');
117
        $newDQ->applyRelation('ManyTestGs');
118
        //confirm we are still joined to the base table
119
        $this->assertTrue($newDQ->query()->isJoinedTo($baseDBTable));
120
        //double check it is the "FROM" clause
121
        $this->assertStringContainsString("FROM \"$baseDBTable\"", $newDQ->sql(), 'The FROM clause has been removed from the query');
122
        //another (potentially less crude check) for checking "FROM" clause
123
        $fromTables = $newDQ->query()->getFrom();
124
        $this->assertEquals('"' . $baseDBTable . '"', $fromTables[$baseDBTable]);
125
    }
126
127
    public function testRelationReturn()
128
    {
129
        $dq = new DataQuery(DataQueryTest\ObjectC::class);
130
        $this->assertEquals(
131
            DataQueryTest\ObjectA::class,
132
            $dq->applyRelation('TestA'),
133
            'DataQuery::applyRelation should return the name of the related object.'
134
        );
135
        $this->assertEquals(
136
            DataQueryTest\ObjectA::class,
137
            $dq->applyRelation('TestAs'),
138
            'DataQuery::applyRelation should return the name of the related object.'
139
        );
140
        $this->assertEquals(
141
            DataQueryTest\ObjectA::class,
142
            $dq->applyRelation('ManyTestAs'),
143
            'DataQuery::applyRelation should return the name of the related object.'
144
        );
145
146
        $this->assertEquals(
147
            DataQueryTest\ObjectB::class,
148
            $dq->applyRelation('TestB'),
149
            'DataQuery::applyRelation should return the name of the related object.'
150
        );
151
        $this->assertEquals(
152
            DataQueryTest\ObjectB::class,
153
            $dq->applyRelation('TestBs'),
154
            'DataQuery::applyRelation should return the name of the related object.'
155
        );
156
        $this->assertEquals(
157
            DataQueryTest\ObjectB::class,
158
            $dq->applyRelation('ManyTestBs'),
159
            'DataQuery::applyRelation should return the name of the related object.'
160
        );
161
        $newDQ = new DataQuery(DataQueryTest\ObjectE::class);
162
        $this->assertEquals(
163
            DataQueryTest\ObjectA::class,
164
            $newDQ->applyRelation('TestA'),
165
            'DataQuery::applyRelation should return the name of the related object.'
166
        );
167
    }
168
169
    public function testRelationOrderWithCustomJoin()
170
    {
171
        $dataQuery = new DataQuery(DataQueryTest\ObjectB::class);
172
        $dataQuery->innerJoin('DataQueryTest_D', '"DataQueryTest_D"."RelationID" = "DataQueryTest_B"."ID"');
173
        $dataQuery->execute();
174
        $this->assertTrue(true);
175
    }
176
177
    public function testDisjunctiveGroup()
178
    {
179
        $dq = new DataQuery(DataQueryTest\ObjectA::class);
180
181
        $dq->where('DataQueryTest_A.ID = 2');
182
        $subDq = $dq->disjunctiveGroup();
183
        $subDq->where('DataQueryTest_A.Name = \'John\'');
184
        $subDq->where('DataQueryTest_A.Name = \'Bob\'');
185
186
        $this->assertSQLContains(
187
            "WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') OR (DataQueryTest_A.Name = 'Bob'))",
188
            $dq->sql($parameters)
189
        );
190
    }
191
192
    public function testConjunctiveGroup()
193
    {
194
        $dq = new DataQuery(DataQueryTest\ObjectA::class);
195
196
        $dq->where('DataQueryTest_A.ID = 2');
197
        $subDq = $dq->conjunctiveGroup();
198
        $subDq->where('DataQueryTest_A.Name = \'John\'');
199
        $subDq->where('DataQueryTest_A.Name = \'Bob\'');
200
201
        $this->assertSQLContains(
202
            "WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') AND (DataQueryTest_A.Name = 'Bob'))",
203
            $dq->sql($parameters)
204
        );
205
    }
206
207
    /**
208
     * @todo Test paramaterised
209
     */
210
    public function testNestedGroups()
211
    {
212
        $dq = new DataQuery(DataQueryTest\ObjectA::class);
213
214
        $dq->where('DataQueryTest_A.ID = 2');
215
        $subDq = $dq->disjunctiveGroup();
216
        $subDq->where('DataQueryTest_A.Name = \'John\'');
217
        $subSubDq = $subDq->conjunctiveGroup();
218
        $subSubDq->where('DataQueryTest_A.Age = 18');
219
        $subSubDq->where('DataQueryTest_A.Age = 50');
220
        $subDq->where('DataQueryTest_A.Name = \'Bob\'');
221
222
        $this->assertSQLContains(
223
            "WHERE (DataQueryTest_A.ID = 2) AND ((DataQueryTest_A.Name = 'John') OR ((DataQueryTest_A.Age = 18) "
224
                . "AND (DataQueryTest_A.Age = 50)) OR (DataQueryTest_A.Name = 'Bob'))",
225
            $dq->sql($parameters)
226
        );
227
    }
228
229
    public function testEmptySubgroup()
230
    {
231
        $dq = new DataQuery(DataQueryTest\ObjectA::class);
232
        $dq->conjunctiveGroup();
233
234
        // Empty groups should have no where condition at all
235
        $this->assertSQLNotContains('WHERE', $dq->sql($parameters));
236
    }
237
238
    public function testSubgroupHandoff()
239
    {
240
        $dq = new DataQuery(DataQueryTest\ObjectA::class);
241
        $subDq = $dq->disjunctiveGroup();
242
243
        $orgDq = clone $dq;
244
245
        $subDq->sort('"DataQueryTest_A"."Name"');
246
        $orgDq->sort('"DataQueryTest_A"."Name"');
247
248
        $this->assertSQLEquals($dq->sql($parameters), $orgDq->sql($parameters));
249
250
        $subDq->limit(5, 7);
251
        $orgDq->limit(5, 7);
252
253
        $this->assertSQLEquals($dq->sql($parameters), $orgDq->sql($parameters));
254
    }
255
256
    public function testOrderByMultiple()
257
    {
258
        $dq = new DataQuery(SQLSelectTest\TestObject::class);
259
        $dq = $dq->sort('"Name" ASC, MID("Name", 8, 1) DESC');
260
        $this->assertStringContainsString(
261
            'ORDER BY "SQLSelectTest_DO"."Name" ASC, "_SortColumn0" DESC',
262
            $dq->sql($parameters)
263
        );
264
    }
265
266
    public function testDefaultSort()
267
    {
268
        $query = new DataQuery(DataQueryTest\ObjectE::class);
269
        $result = $query->column('Title');
270
        $this->assertEquals(['First', 'Second', 'Last'], $result);
271
    }
272
273
    public function testDistinct()
274
    {
275
        $query = new DataQuery(DataQueryTest\ObjectE::class);
276
        $this->assertStringContainsString('SELECT DISTINCT', $query->sql($params), 'Query is set as distinct by default');
277
278
        $query = $query->distinct(false);
279
        $this->assertStringNotContainsString('SELECT DISTINCT', $query->sql($params), 'Query does not contain distinct');
280
281
        $query = $query->distinct(true);
282
        $this->assertStringContainsString('SELECT DISTINCT', $query->sql($params), 'Query contains distinct');
283
    }
284
285
    public function testComparisonClauseInt()
286
    {
287
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)");
288
        $query = new DataQuery(DataQueryTest\ObjectF::class);
289
        $query->where(DB::get_conn()->comparisonClause('"SortOrder"', '2'));
290
        $this->assertGreaterThan(0, $query->count(), "Couldn't find SortOrder");
291
        static::resetDBSchema(true);
292
    }
293
294
    public function testComparisonClauseDateFull()
295
    {
296
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
297
        $query = new DataQuery(DataQueryTest\ObjectF::class);
298
        $query->where(DB::get_conn()->comparisonClause('"MyDate"', '1988-03-04%'));
299
        $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
300
        static::resetDBSchema(true);
301
    }
302
303
    public function testSurrogateFieldSort()
304
    {
305
        $query = new DataQuery(DataQueryTest\ObjectE::class);
306
        $query->sort(
307
            sprintf(
308
                '(case when "Title" = %s then 1 else 0 end)',
309
                DB::get_conn()->quoteString('Second')
310
            ),
311
            'DESC',
312
            true
313
        );
314
        $query->sort('SortOrder', 'ASC', false);
315
        $query->sort(
316
            sprintf(
317
                '(case when "Title" = %s then 0 else 1 end)',
318
                DB::get_conn()->quoteString('Fourth')
319
            ),
320
            'DESC',
321
            false
322
        );
323
        $this->assertEquals(
324
            $query->execute()->column('Title'),
325
            $query->column('Title')
326
        );
327
    }
328
329
    public function testCustomFieldWithAliasSort()
330
    {
331
        $query = new DataQuery(DataQueryTest\ObjectE::class);
332
        $query->selectField(sprintf(
333
            '(case when "Title" = %s then 1 else 0 end)',
334
            DB::get_conn()->quoteString('Second')
335
        ), 'CustomColumn');
336
        $query->sort('CustomColumn', 'DESC', true);
337
        $query->sort('SortOrder', 'ASC', false);
338
        $this->assertEquals(
339
            ['Second', 'First', 'Last'],
340
            $query->column('Title')
341
        );
342
    }
343
344
    public function testComparisonClauseDateStartsWith()
345
    {
346
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
347
        $query = new DataQuery(DataQueryTest\ObjectF::class);
348
        $query->where(DB::get_conn()->comparisonClause('"MyDate"', '1988%'));
349
        $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
350
        static::resetDBSchema(true);
351
    }
352
353
    public function testComparisonClauseDateStartsPartial()
354
    {
355
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
356
        $query = new DataQuery(DataQueryTest\ObjectF::class);
357
        $query->where(DB::get_conn()->comparisonClause('"MyDate"', '%03-04%'));
358
        $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
359
        static::resetDBSchema(true);
360
    }
361
362
    public function testComparisonClauseTextCaseInsensitive()
363
    {
364
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
365
        $query = new DataQuery(DataQueryTest\ObjectF::class);
366
        $query->where(DB::get_conn()->comparisonClause('"MyString"', 'helloworld'));
367
        $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString");
368
        static::resetDBSchema(true);
369
    }
370
371
    public function testComparisonClauseTextCaseSensitive()
372
    {
373
        DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
374
        $query = new DataQuery(DataQueryTest\ObjectF::class);
375
        $query->where(DB::get_conn()->comparisonClause('"MyString"', 'HelloWorld', false, false, true));
376
        $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString");
377
378
        $query2 = new DataQuery(DataQueryTest\ObjectF::class);
379
        $query2->where(DB::get_conn()->comparisonClause('"MyString"', 'helloworld', false, false, true));
380
        $this->assertEquals(0, $query2->count(), "Found mystring. Shouldn't be able too.");
381
        static::resetDBSchema(true);
382
    }
383
384
    /**
385
     * Tests that getFinalisedQuery can include all tables
386
     */
387
    public function testConditionsIncludeTables()
388
    {
389
        // Including filter on parent table only doesn't pull in second
390
        $query = new DataQuery(DataQueryTest\ObjectC::class);
391
        $query->sort('"SortOrder"');
392
        $query->where(
393
            [
394
            '"DataQueryTest_C"."Title" = ?' => ['First']
395
            ]
396
        );
397
        $result = $query->getFinalisedQuery(['Title']);
398
        $from = $result->getFrom();
399
        $this->assertContains('DataQueryTest_C', array_keys($from));
400
        $this->assertNotContains('DataQueryTest_E', array_keys($from));
401
402
        // Including filter on sub-table requires it
403
        $query = new DataQuery(DataQueryTest\ObjectC::class);
404
        $query->sort('"SortOrder"');
405
        $query->where(
406
            ['"DataQueryTest_C"."Title" = ? OR "DataQueryTest_E"."SortOrder" > ?' => ['First', 2]]
407
        );
408
        $result = $query->getFinalisedQuery(['Title']);
409
        $from = $result->getFrom();
410
411
        // Check that including "SortOrder" prompted inclusion of DataQueryTest_E table
412
        $this->assertContains('DataQueryTest_C', array_keys($from));
413
        $this->assertContains('DataQueryTest_E', array_keys($from));
414
        $arrayResult = iterator_to_array($result->execute());
415
        $first = array_shift($arrayResult);
416
        $this->assertNotNull($first);
417
        $this->assertEquals('First', $first['Title']);
418
        $second = array_shift($arrayResult);
419
        $this->assertNotNull($second);
420
        $this->assertEquals('Last', $second['Title']);
421
        $this->assertEmpty(array_shift($arrayResult));
422
    }
423
424
    public function testColumnReturnsAllValues()
425
    {
426
        $first = new DataQueryTest\ObjectA();
427
        $first->Name = 'Bar';
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataQueryTest\ObjectA. Since you implemented __set, consider adding a @property annotation.
Loading history...
428
        $first->write();
429
430
        $second = new DataQueryTest\ObjectA();
431
        $second->Name = 'Foo';
432
        $second->write();
433
434
        $third = new DataQueryTest\ObjectA();
435
        $third->Name = 'Bar';
436
        $third->write();
437
438
        $result = DataQueryTest\ObjectA::get()->column('Name');
439
        $this->assertEquals(['Bar', 'Foo', 'Bar'], $result);
440
    }
441
442
    public function testColumnUniqueReturnsAllValues()
443
    {
444
        $first = new DataQueryTest\ObjectA();
445
        $first->Name = 'Bar';
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataQueryTest\ObjectA. Since you implemented __set, consider adding a @property annotation.
Loading history...
446
        $first->write();
447
448
        $second = new DataQueryTest\ObjectA();
449
        $second->Name = 'Foo';
450
        $second->write();
451
452
        $third = new DataQueryTest\ObjectA();
453
        $third->Name = 'Bar';
454
        $third->write();
455
456
        $result = DataQueryTest\ObjectA::get()->columnUnique('Name');
457
        $this->assertCount(2, $result);
458
        $this->assertContains('Bar', $result);
459
        $this->assertContains('Foo', $result);
460
    }
461
462
    /**
463
     * Tests that sorting against multiple relationships is working
464
     */
465
    public function testMultipleRelationSort()
466
    {
467
        $query = new DataQuery(DataQueryTest\ObjectH::class);
468
        $query->applyRelation('ManyTestEs');
469
        $query->applyRelation('ManyTestIs');
470
        $query->sort([
0 ignored issues
show
Bug introduced by
array('"manytestes_DataQ...Order"', '"SortOrder"') of type array<integer,string> is incompatible with the type string expected by parameter $sort of SilverStripe\ORM\DataQuery::sort(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

470
        $query->sort(/** @scrutinizer ignore-type */ [
Loading history...
471
            '"manytestes_DataQueryTest_E"."SortOrder"',
472
            '"manytestis_DataQueryTest_I"."SortOrder"',
473
            '"SortOrder"',
474
        ]);
475
476
        $titles = $query->column('Name');
477
478
        $this->assertEquals('First', $titles[0]);
479
        $this->assertEquals('Second', $titles[1]);
480
        $this->assertEquals('Last', $titles[2]);
481
    }
482
483
    public function testExistsCreatesFunctionalQueries()
484
    {
485
        $this->assertTrue(
486
            ObjectE::get()->exists(),
487
            'Query for ObjectE exists because there\'s more than 1 record'
488
        );
489
        $this->assertFalse(
490
            ObjectE::get()->where(['"Title" = ?' => 'Foo'])->exists(),
491
            'Query for ObjectE with Title Foo does NOT exists because there\'s no matching record'
492
        );
493
        $this->assertTrue(
494
            ObjectE::get()->dataQuery()->groupby('"SortOrder"')->exists(),
495
            'Existence of query for ObjectE is not affected by group by'
496
        );
497
        $this->assertTrue(
498
            ObjectE::get()->limit(1)->exists(),
499
            'Existence of query for ObjectE is not affected by limit if records are returned'
500
        );
501
        $this->assertFalse(
502
            ObjectE::get()->limit(4, 9999)->exists(),
503
            'Existence of query for ObjectE is affected by limit if no records are returned'
504
        );
505
506
        $query = new DataQuery(ObjectE::class);
507
        $this->assertTrue(
508
            $query->exists(),
509
            'exist returns true if query return results'
510
        );
511
        $query = new DataQuery(ObjectE::class);
512
        $this->assertFalse(
513
            $query->where(['"Title" = ?' => 'Foo'])->exists(),
514
            'exist returns false if there\'s no results'
515
        );
516
        $query = new DataQuery(ObjectE::class);
517
        $this->assertTrue(
518
            $query->groupby('"SortOrder"')->exists(),
519
            'exist is unaffected by group by'
520
        );
521
        $query = new DataQuery(ObjectE::class);
522
        $this->assertTrue(
523
            $query->limit(1)->exists(),
524
            'exist is unaffected by limit as long as one recard is returned'
525
        );
526
        $this->assertFalse(
527
            $query->limit(1, 9999)->exists(),
528
            'exist is false when a limit returns no results'
529
        );
530
    }
531
}
532