• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <gtest/gtest.h>
17 
18 #include <string>
19 
20 #include "common.h"
21 #include "grd_api_manager.h"
22 #include "rdb_errno.h"
23 #include "rdb_helper.h"
24 #include "rdb_open_callback.h"
25 using namespace testing::ext;
26 using namespace OHOS::NativeRdb;
27 namespace Test {
28 class RdbReadOnlyTest : public testing::Test {
29 public:
30     static void SetUpTestCase(void);
31     static void TearDownTestCase(void);
32     void SetUp();
33     void TearDown();
34 
35     static const std::string READONLY_DATABASE_NAME;
36     static const std::string READONLY_DATABASE_NAME_18; // for testcase 18
37     static const std::string READONLY_DATABASE_BAK_NAME;
38     static const std::string DATABASE_NAME;
39     static std::shared_ptr<RdbStore> readOnlyStore;
40 };
41 
42 const std::string RdbReadOnlyTest::DATABASE_NAME = RDB_TEST_PATH + "database.db";
43 const std::string RdbReadOnlyTest::READONLY_DATABASE_NAME = RDB_TEST_PATH + "readOnly.db";
44 const std::string RdbReadOnlyTest::READONLY_DATABASE_NAME_18 = RDB_TEST_PATH + "readOnly1.db";
45 const std::string RdbReadOnlyTest::READONLY_DATABASE_BAK_NAME = RDB_TEST_PATH + "readOnlyBak.db";
46 std::shared_ptr<RdbStore> RdbReadOnlyTest::readOnlyStore = nullptr;
47 
48 class ReadOnlyTestOpenCallback : public RdbOpenCallback {
49 public:
50     int OnCreate(RdbStore &store) override;
51     int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
52     static const std::string CREATE_TABLE_TEST;
53 };
54 
55 const std::string ReadOnlyTestOpenCallback::CREATE_TABLE_TEST =
56     "CREATE TABLE IF NOT EXISTS test "
57     "(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, salary REAL, blobType BLOB)";
58 
OnCreate(RdbStore & store)59 int ReadOnlyTestOpenCallback::OnCreate(RdbStore &store)
60 {
61     return store.ExecuteSql(CREATE_TABLE_TEST);
62 }
63 
OnUpgrade(RdbStore & store,int oldVersion,int newVersion)64 int ReadOnlyTestOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
65 {
66     return E_OK;
67 }
68 
SetUpTestCase(void)69 void RdbReadOnlyTest::SetUpTestCase(void)
70 {
71     int errCode = E_ERROR;
72     RdbHelper::DeleteRdbStore(READONLY_DATABASE_NAME);
73     RdbStoreConfig config(READONLY_DATABASE_NAME);
74     config.SetBundleName("com.example.readOnly.rdb");
75     ReadOnlyTestOpenCallback helper;
76     // user_version is 1
77     auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
78     EXPECT_NE(nullptr, store);
79     EXPECT_EQ(E_OK, errCode);
80 
81     int64_t id;
82     ValuesBucket values;
83     values.PutString("name", "zhangSan");
84     int ret = store->Insert(id, "test", values);
85     EXPECT_EQ(E_OK, ret);
86     // id is 1
87     EXPECT_EQ(1, id);
88 
89     RdbHelper::ClearCache();
90 
91     RdbStoreConfig config1(READONLY_DATABASE_NAME);
92     config1.SetBundleName("com.example.readOnly.rdb");
93     config1.SetReadOnly(true);
94     ReadOnlyTestOpenCallback helper1;
95     // user_version is 1
96     readOnlyStore = RdbHelper::GetRdbStore(config1, 1, helper1, errCode);
97     EXPECT_NE(nullptr, readOnlyStore);
98     EXPECT_EQ(E_OK, errCode);
99 }
100 
TearDownTestCase(void)101 void RdbReadOnlyTest::TearDownTestCase(void)
102 {
103     readOnlyStore = nullptr;
104     EXPECT_EQ(E_OK, RdbHelper::DeleteRdbStore(RdbReadOnlyTest::DATABASE_NAME));
105     EXPECT_EQ(E_OK, RdbHelper::DeleteRdbStore(RdbReadOnlyTest::READONLY_DATABASE_NAME));
106     EXPECT_EQ(E_OK, RdbHelper::DeleteRdbStore(RdbReadOnlyTest::READONLY_DATABASE_BAK_NAME));
107 }
108 
SetUp()109 void RdbReadOnlyTest::SetUp()
110 {
111 }
112 
TearDown()113 void RdbReadOnlyTest::TearDown()
114 {
115 }
116 
117 /**
118  * @tc.name: RdbStore_ReadOnly_0001, open read-only database if the database is not exist
119  * @tc.desc: 1. set isReadOnly as true
120  *           2. open read-only database
121  * @tc.type: FUNC
122  */
123 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0001, TestSize.Level1)
124 {
125     int errCode = E_ERROR;
126     RdbStoreConfig config(RdbReadOnlyTest::DATABASE_NAME);
127     config.SetReadOnly(true);
128     ReadOnlyTestOpenCallback helper;
129     // create read-only database, user_version is 1
130     auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
131     EXPECT_EQ(nullptr, store);
132     EXPECT_EQ(E_SQLITE_CANTOPEN, errCode);
133 }
134 
135 /**
136  * @tc.name: RdbStore_ReadOnly_0002, insert data
137  * @tc.desc: insert data into read-only database
138  * @tc.type: FUNC
139  */
140 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0002, TestSize.Level1)
141 {
142     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
143 
144     int64_t id;
145     ValuesBucket values;
146     values.PutString("name", "liSi");
147     int ret = store->Insert(id, "test", values);
148     EXPECT_EQ(E_NOT_SUPPORT, ret);
149 }
150 
151 /**
152  * @tc.name: RdbStore_ReadOnly_0003, update data
153  * @tc.desc: update data in read-only database
154  * @tc.type: FUNC
155  */
156 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0003, TestSize.Level1)
157 {
158     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
159 
160     int changedRows;
161     ValuesBucket values;
162     // salary is 300.5
163     values.PutDouble("salary", 300.5);
164     auto ret = store->Update(changedRows, "test", values);
165     EXPECT_EQ(E_NOT_SUPPORT, ret);
166 }
167 
168 /**
169  * @tc.name: RdbStore_ReadOnly_0004, delete data
170  * @tc.desc: delete data from read-only database
171  * @tc.type: FUNC
172  */
173 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0004, TestSize.Level1)
174 {
175     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
176 
177     int deletedRows;
178     auto ret = store->Delete(deletedRows, "test", "id = 1");
179     EXPECT_EQ(E_NOT_SUPPORT, ret);
180 }
181 
182 /**
183  * @tc.name: RdbStore_ReadOnly_0005
184  * @tc.desc: execute transaction
185  * @tc.type: FUNC
186  */
187 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0005, TestSize.Level1)
188 {
189     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
190 
191     auto ret = store->BeginTransaction();
192     EXPECT_EQ(E_NOT_SUPPORT, ret);
193 
194     ret = store->Commit();
195     EXPECT_EQ(E_NOT_SUPPORT, ret);
196 
197     ret = store->RollBack();
198     EXPECT_EQ(E_NOT_SUPPORT, ret);
199 }
200 
201 /**
202  * @tc.name: RdbStore_ReadOnly_0006
203  * @tc.desc: batch insert data
204  * @tc.type: FUNC
205  */
206 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0006, TestSize.Level1)
207 {
208     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
209 
210     int64_t number = 0;
211     std::vector<ValuesBucket> valuesBuckets;
212     ValuesBucket values;
213     values.PutString("name", "zhangSan");
214     valuesBuckets.push_back(std::move(values));
215     int error = store->BatchInsert(number, "test", valuesBuckets);
216     EXPECT_EQ(E_NOT_SUPPORT, error);
217 }
218 
219 /**
220  * @tc.name: RdbStore_ReadOnly_0007
221  * @tc.desc: get user_version by querySql
222  * @tc.type: FUNC
223  */
224 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0007, TestSize.Level1)
225 {
226     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
227 
228     auto resultSet = store->QuerySql("PRAGMA user_version");
229 
230     EXPECT_NE(nullptr, resultSet);
231     EXPECT_EQ(E_OK, resultSet->GoToFirstRow());
232 
233     int value = 0;
234     // column index is 0
235     EXPECT_EQ(E_OK, resultSet->GetInt(0, value));
236     EXPECT_EQ(1, value);
237 
238     EXPECT_EQ(E_OK, resultSet->Close());
239 }
240 
241 /**
242  * @tc.name: RdbStore_ReadOnly_0008
243  * @tc.desc: get user_version by execute
244  * @tc.type: FUNC
245  */
246 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0008, TestSize.Level1)
247 {
248     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
249 
250     auto [ret, object] = store->Execute("PRAGMA user_version");
251     EXPECT_EQ(E_NOT_SUPPORT, ret);
252 
253     std::tie(ret, object) = store->Execute("PRAGMA user_version=2");
254     EXPECT_EQ(E_NOT_SUPPORT, ret);
255 }
256 
257 /**
258  * @tc.name: RdbStore_ReadOnly_0009
259  * @tc.desc: query data
260  * @tc.type: FUNC
261  */
262 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0009, TestSize.Level1)
263 {
264     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
265 
266     auto resultSet = store->QuerySql("SELECT * FROM test");
267 
268     int count = 0;
269     EXPECT_EQ(E_OK, resultSet->GetRowCount(count));
270     // count is 1
271     EXPECT_EQ(1, count);
272 
273     EXPECT_EQ(E_OK, resultSet->Close());
274 }
275 
276 /**
277  * @tc.name: RdbStore_ReadOnly_0010
278  * @tc.desc: get user_version by executeSql
279  * @tc.type: FUNC
280  */
281 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0010, TestSize.Level1)
282 {
283     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
284 
285     auto ret = store->ExecuteSql("PRAGMA user_version");
286     EXPECT_EQ(E_NOT_SUPPORT, ret);
287 
288     ret = store->ExecuteSql("SELECT * FROM test");
289     EXPECT_EQ(E_NOT_SUPPORT, ret);
290 }
291 
292 /**
293  * @tc.name: RdbStore_ReadOnly_0011
294  * @tc.desc: replace data
295  * @tc.type: FUNC
296  */
297 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0011, TestSize.Level1)
298 {
299     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
300 
301     int64_t id;
302     ValuesBucket values;
303     values.PutString("name", "zhangSan");
304     int ret = store->Replace(id, "test", values);
305     EXPECT_EQ(E_NOT_SUPPORT, ret);
306 }
307 
308 /**
309  * @tc.name: RdbStore_ReadOnly_0012
310  * @tc.desc: test ExecuteAndGetLong
311  * @tc.type: FUNC
312  */
313 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0012, TestSize.Level1)
314 {
315     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
316 
317     int64_t count;
318     int ret = store->ExecuteAndGetLong(count, "SELECT COUNT(*) FROM test");
319     EXPECT_EQ(E_OK, ret);
320 
321     ret = store->ExecuteAndGetLong(count, "PRAGMA user_version");
322     EXPECT_EQ(E_DATABASE_BUSY, ret);
323 }
324 
325 /**
326  * @tc.name: RdbStore_ReadOnly_0013
327  * @tc.desc: test ExecuteAndGetString
328  * @tc.type: FUNC
329  */
330 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0013, TestSize.Level1)
331 {
332     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
333 
334     std::string count;
335     int ret = store->ExecuteAndGetString(count, "SELECT COUNT(*) FROM test");
336     EXPECT_EQ(E_OK, ret);
337 
338     ret = store->ExecuteAndGetString(count, "PRAGMA user_version");
339     EXPECT_EQ(E_DATABASE_BUSY, ret);
340 }
341 
342 /**
343  * @tc.name: RdbStore_ReadOnly_0014
344  * @tc.desc: test ExecuteForLastInsertedRowId
345  * @tc.type: FUNC
346  */
347 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0014, TestSize.Level1)
348 {
349     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
350 
351     int64_t outValue;
352     int ret = store->ExecuteForLastInsertedRowId(outValue, "", {});
353     EXPECT_EQ(E_NOT_SUPPORT, ret);
354 }
355 
356 /**
357  * @tc.name: RdbStore_ReadOnly_0015
358  * @tc.desc: test ExecuteForChangedRowCount
359  * @tc.type: FUNC
360  */
361 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0015, TestSize.Level1)
362 {
363     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
364 
365     int64_t outValue;
366     int ret = store->ExecuteForChangedRowCount(outValue, "", {});
367     EXPECT_EQ(E_NOT_SUPPORT, ret);
368 }
369 
370 /**
371  * @tc.name: RdbStore_ReadOnly_0016
372  * @tc.desc: get user_version by GetVersion
373  * @tc.type: FUNC
374  */
375 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0016, TestSize.Level1)
376 {
377     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
378 
379     int version = -1;
380     auto ret = store->GetVersion(version);
381     EXPECT_EQ(E_OK, ret);
382     // version is 1
383     EXPECT_EQ(1, version);
384 }
385 
386 /**
387  * @tc.name: RdbStore_ReadOnly_0017
388  * @tc.desc: set user_version by SetVersion
389  * @tc.type: FUNC
390  */
391 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0017, TestSize.Level1)
392 {
393     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
394 
395     int version = 2;
396     auto ret = store->SetVersion(version);
397     EXPECT_EQ(E_NOT_SUPPORT, ret);
398 }
399 
400 /**
401  * @tc.name: RdbStore_ReadOnly_0018
402  * @tc.desc: test vector db
403  * @tc.type: FUNC
404  */
405 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0018, TestSize.Level1)
406 {
407     if (!OHOS::NativeRdb::IsUsingArkData()) {
408         return;
409     }
410     int errCode = E_ERROR;
411     RdbStoreConfig config(RdbReadOnlyTest::READONLY_DATABASE_NAME_18);
412     config.SetBundleName("com.example.readOnly.rdb");
413     config.SetReadOnly(true);
414     config.SetIsVector(true);
415     ReadOnlyTestOpenCallback helper;
416     // user_version is 1
417     auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
418     ASSERT_NE(nullptr, store);
419 
420     auto [ret, id] = store->BeginTrans();
421     EXPECT_EQ(E_NOT_SUPPORT, ret);
422 
423     // id is 1
424     ret = store->Commit(1);
425     EXPECT_EQ(E_NOT_SUPPORT, ret);
426 
427     // id is 1
428     ret = store->RollBack(1);
429     EXPECT_EQ(E_NOT_SUPPORT, ret);
430 
431     ValueObject obj;
432     // id is 1
433     std::tie(ret, obj) = store->Execute("PRAGMA user_version", {}, 1);
434     EXPECT_EQ(E_NOT_SUPPORT, ret);
435 }
436 
437 /**
438  * @tc.name: RdbStore_ReadOnly_0019
439  * @tc.desc: test encrypt db
440  * @tc.type: FUNC
441  */
442 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0019, TestSize.Level1)
443 {
444     int errCode = E_ERROR;
445     RdbStoreConfig config(RdbReadOnlyTest::DATABASE_NAME);
446     config.SetBundleName("com.example.encrypt.rdb");
447     config.SetEncryptStatus(true);
448     ReadOnlyTestOpenCallback helper;
449     // user_version is 1
450     auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
451     EXPECT_NE(nullptr, store);
452 
453     RdbHelper::ClearCache();
454 
455     RdbStoreConfig config1(RdbReadOnlyTest::DATABASE_NAME);
456     config1.SetBundleName("com.example.encrypt.rdb");
457     config1.SetReadOnly(true);
458     config1.SetEncryptStatus(true);
459     // user_version is 1
460     store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
461     EXPECT_NE(nullptr, store);
462 
463     EXPECT_EQ(E_OK, RdbHelper::DeleteRdbStore(RdbReadOnlyTest::DATABASE_NAME));
464 }
465 
466 /**
467  * @tc.name: RdbStore_ReadOnly_0020
468  * @tc.desc: test attach and detach
469  * @tc.type: FUNC
470  */
471 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0020, TestSize.Level1)
472 {
473     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
474 
475     RdbStoreConfig config(RdbReadOnlyTest::READONLY_DATABASE_NAME);
476     auto [ret, size] = store->Attach(config, RdbReadOnlyTest::DATABASE_NAME);
477     EXPECT_EQ(E_NOT_SUPPORT, ret);
478 
479     std::tie(ret, size) = store->Detach(RdbReadOnlyTest::DATABASE_NAME);
480     EXPECT_EQ(E_NOT_SUPPORT, ret);
481 }
482 
483 /**
484  * @tc.name: RdbStore_ReadOnly_0021
485  * @tc.desc: test SetDistributedTables
486  * @tc.type: FUNC
487  */
488 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0021, TestSize.Level1)
489 {
490     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
491 
492     AbsRdbPredicates predicates("test");
493     OHOS::DistributedRdb::DistributedConfig config;
494     // type is 0
495     auto ret = store->SetDistributedTables({}, 0, config);
496     EXPECT_EQ(E_NOT_SUPPORT, ret);
497 }
498 
499 /**
500  * @tc.name: RdbStore_ReadOnly_0022
501  * @tc.desc: test CleanDirtyData
502  * @tc.type: FUNC
503  */
504 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0022, TestSize.Level1)
505 {
506     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
507 
508     uint64_t cursor = 1;
509     auto ret = store->CleanDirtyData("test", cursor);
510     EXPECT_EQ(E_NOT_SUPPORT, ret);
511 }
512 
513 /**
514  * @tc.name: RdbStore_ReadOnly_0023
515  * @tc.desc: test BatchInsertWithConflictResolution
516  * @tc.type: FUNC
517  */
518 HWTEST_F(RdbReadOnlyTest, RdbStore_ReadOnly_0023, TestSize.Level1)
519 {
520     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
521 
522     ValuesBuckets rows;
523     for (int i = 0; i < 5; i++) {
524         ValuesBucket row;
525         row.Put("name", "Jim");
526         rows.Put(row);
527     }
528     auto ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_NONE);
529     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
530     ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_ROLLBACK);
531     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
532     ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_ABORT);
533     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
534     ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_FAIL);
535     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
536     ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_IGNORE);
537     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
538     ret = store->BatchInsertWithConflictResolution("test", rows, ConflictResolution::ON_CONFLICT_REPLACE);
539     EXPECT_EQ(E_NOT_SUPPORT, ret.first);
540 }
541 
542 /**
543  * @tc.name: RdbStore_CreateTransaction_001
544  * @tc.desc: test Create Transaction
545  * @tc.type: FUNC
546  */
547 HWTEST_F(RdbReadOnlyTest, RdbStore_CreateTransaction_001, TestSize.Level1)
548 {
549     std::shared_ptr<RdbStore> &store = RdbReadOnlyTest::readOnlyStore;
550     auto [errCode, trans] = store->CreateTransaction(Transaction::DEFERRED);
551     EXPECT_EQ(E_NOT_SUPPORT, errCode);
552     EXPECT_EQ(trans, nullptr);
553 
554     std::tie(errCode, trans) = store->CreateTransaction(Transaction::IMMEDIATE);
555     EXPECT_EQ(E_NOT_SUPPORT, errCode);
556     EXPECT_EQ(trans, nullptr);
557 
558     std::tie(errCode, trans) = store->CreateTransaction(Transaction::EXCLUSIVE);
559     EXPECT_EQ(E_NOT_SUPPORT, errCode);
560     EXPECT_EQ(trans, nullptr);
561 }
562 }