xref: /expo/apps/test-suite/tests/SQLite.ts (revision 36864eca)
1import { Asset } from 'expo-asset';
2import * as FS from 'expo-file-system';
3import { Platform } from 'expo-modules-core';
4import * as SQLite from 'expo-sqlite';
5
6export const name = 'SQLite';
7
8// The version here needs to be the same as both the podspec and build.gradle for expo-sqlite
9const VERSION = '3.42.0';
10
11// TODO: Only tests successful cases, needs to test error cases like bad database name etc.
12export function test(t) {
13  t.describe('SQLite', () => {
14    t.it('should be able to drop + create a table, insert, query', async () => {
15      const db = SQLite.openDatabase('test.db');
16      await new Promise((resolve, reject) => {
17        db.transaction(
18          (tx) => {
19            const nop = () => {};
20            const onError = (tx, error) => {
21              reject(error);
22              return false;
23            };
24
25            tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError);
26            tx.executeSql(
27              'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);',
28              [],
29              nop,
30              onError
31            );
32            tx.executeSql(
33              'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)',
34              ['Tim Duncan', 1, 23.4],
35              nop,
36              onError
37            );
38            tx.executeSql(
39              'INSERT INTO Users (name, k, j) VALUES ("Manu Ginobili", 5, 72.8)',
40              [],
41              nop,
42              onError
43            );
44            tx.executeSql(
45              'INSERT INTO Users (name, k, j) VALUES ("Nikhilesh Sigatapu", 7, 42.14)',
46              [],
47              nop,
48              onError
49            );
50
51            tx.executeSql(
52              'SELECT * FROM Users',
53              [],
54              (tx, results) => {
55                t.expect(results.rows.length).toEqual(3);
56                t.expect(results.rows.item(0).j).toBeCloseTo(23.4);
57              },
58              onError
59            );
60          },
61          reject,
62          () => {
63            resolve(null);
64          }
65        );
66      });
67
68      if (Platform.OS !== 'web') {
69        const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
70        t.expect(exists).toBeTruthy();
71      }
72    });
73
74    t.it(`should use specified SQLite version: ${VERSION}`, () => {
75      const db = SQLite.openDatabase('test.db');
76
77      db.transaction((tx) => {
78        tx.executeSql('SELECT sqlite_version()', [], (_, results) => {
79          const queryVersion = results.rows._array[0]['sqlite_version()'];
80          t.expect(queryVersion).toEqual(VERSION);
81        });
82      });
83    });
84
85    t.it(`unixepoch() is supported`, () => {
86      const db = SQLite.openDatabase('test.db');
87
88      db.transaction((tx) => {
89        tx.executeSql('SELECT unixepoch()', [], (_, results) => {
90          const epoch = results.rows._array[0]['unixepoch()'];
91          t.expect(epoch).toBeTruthy();
92        });
93      });
94    });
95
96    if (Platform.OS !== 'web') {
97      t.it(
98        'should work with a downloaded .db file',
99        async () => {
100          await FS.downloadAsync(
101            Asset.fromModule(require('../assets/asset-db.db')).uri,
102            `${FS.documentDirectory}SQLite/downloaded.db`
103          );
104
105          const db = SQLite.openDatabase('downloaded.db');
106          await new Promise((resolve, reject) => {
107            db.transaction(
108              (tx) => {
109                const onError = (tx, error) => {
110                  reject(error);
111                  return false;
112                };
113                tx.executeSql(
114                  'SELECT * FROM Users',
115                  [],
116                  (tx, results) => {
117                    t.expect(results.rows.length).toEqual(3);
118                    t.expect(results.rows._array[0].j).toBeCloseTo(23.4);
119                  },
120                  onError
121                );
122              },
123              reject,
124              () => {
125                resolve(null);
126              }
127            );
128          });
129          db.closeAsync();
130        },
131        30000
132      );
133    }
134
135    t.it('should be able to recreate db from scratch by deleting file', async () => {
136      {
137        const db = SQLite.openDatabase('test.db');
138        await new Promise((resolve, reject) => {
139          db.transaction(
140            (tx) => {
141              const nop = () => {};
142              const onError = (tx, error) => {
143                reject(error);
144                return false;
145              };
146
147              tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError);
148              tx.executeSql(
149                'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);',
150                [],
151                nop,
152                onError
153              );
154              tx.executeSql(
155                'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)',
156                ['Tim Duncan', 1, 23.4],
157                nop,
158                onError
159              );
160
161              tx.executeSql(
162                'SELECT * FROM Users',
163                [],
164                (tx, results) => {
165                  t.expect(results.rows.length).toEqual(1);
166                },
167                onError
168              );
169            },
170            reject,
171            () => {
172              resolve(null);
173            }
174          );
175        });
176      }
177
178      if (Platform.OS !== 'web') {
179        const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
180        t.expect(exists).toBeTruthy();
181      }
182
183      if (Platform.OS !== 'web') {
184        await FS.deleteAsync(`${FS.documentDirectory}SQLite/test.db`);
185        const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
186        t.expect(exists).toBeFalsy();
187      }
188
189      {
190        const db = SQLite.openDatabase('test.db');
191        await new Promise((resolve, reject) => {
192          db.transaction(
193            (tx) => {
194              const nop = () => {};
195              const onError = (tx, error) => {
196                reject(error);
197                return false;
198              };
199
200              tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError);
201              tx.executeSql(
202                'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);',
203                [],
204                nop,
205                onError
206              );
207              tx.executeSql(
208                'SELECT * FROM Users',
209                [],
210                (tx, results) => {
211                  t.expect(results.rows.length).toEqual(0);
212                },
213                onError
214              );
215
216              tx.executeSql(
217                'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)',
218                ['Tim Duncan', 1, 23.4],
219                nop,
220                onError
221              );
222              tx.executeSql(
223                'SELECT * FROM Users',
224                [],
225                (tx, results) => {
226                  t.expect(results.rows.length).toEqual(1);
227                },
228                onError
229              );
230            },
231            reject,
232            () => {
233              resolve(null);
234            }
235          );
236        });
237      }
238    });
239
240    t.it('should maintain correct type of potentialy null bind parameters', async () => {
241      const db = SQLite.openDatabase('test.db');
242      await new Promise((resolve, reject) => {
243        db.transaction(
244          (tx) => {
245            const nop = () => {};
246            const onError = (tx, error) => {
247              reject(error);
248              return false;
249            };
250
251            tx.executeSql('DROP TABLE IF EXISTS Nulling;', [], nop, onError);
252            tx.executeSql(
253              'CREATE TABLE IF NOT EXISTS Nulling (id INTEGER PRIMARY KEY NOT NULL, x NUMERIC, y NUMERIC)',
254              [],
255              nop,
256              onError
257            );
258            tx.executeSql('INSERT INTO Nulling (x, y) VALUES (?, ?)', [null, null], nop, onError);
259            tx.executeSql('INSERT INTO Nulling (x, y) VALUES (null, null)', [], nop, onError);
260
261            tx.executeSql(
262              'SELECT * FROM Nulling',
263              [],
264              (tx, results) => {
265                t.expect(results.rows.item(0).x).toBeNull();
266                t.expect(results.rows.item(0).y).toBeNull();
267                t.expect(results.rows.item(1).x).toBeNull();
268                t.expect(results.rows.item(1).y).toBeNull();
269              },
270              onError
271            );
272          },
273          reject,
274          () => {
275            resolve(null);
276          }
277        );
278      });
279
280      if (Platform.OS !== 'web') {
281        const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
282        t.expect(exists).toBeTruthy();
283      }
284    });
285
286    // Do not try to test PRAGMA statements support in web
287    // as it is expected to not be working.
288    // See https://stackoverflow.com/a/10298712
289    if (Platform.OS !== 'web') {
290      t.it('should support PRAGMA statements', async () => {
291        const db = SQLite.openDatabase('test.db');
292        await new Promise((resolve, reject) => {
293          db.transaction(
294            (tx) => {
295              const nop = () => {};
296              const onError = (tx, error) => {
297                reject(error);
298                return false;
299              };
300
301              tx.executeSql('DROP TABLE IF EXISTS SomeTable;', [], nop, onError);
302              tx.executeSql(
303                'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
304                [],
305                nop,
306                onError
307              );
308              // a result-returning pragma
309              tx.executeSql(
310                'PRAGMA table_info(SomeTable);',
311                [],
312                (tx, results) => {
313                  t.expect(results.rows.length).toEqual(2);
314                  t.expect(results.rows.item(0).name).toEqual('id');
315                  t.expect(results.rows.item(1).name).toEqual('name');
316                },
317                onError
318              );
319              // a no-result pragma
320              tx.executeSql('PRAGMA case_sensitive_like = true;', [], nop, onError);
321              // a setter/getter pragma
322              tx.executeSql('PRAGMA user_version = 123;', [], nop, onError);
323              tx.executeSql(
324                'PRAGMA user_version;',
325                [],
326                (tx, results) => {
327                  t.expect(results.rows.length).toEqual(1);
328                  t.expect(results.rows.item(0).user_version).toEqual(123);
329                },
330                onError
331              );
332            },
333            reject,
334            () => {
335              resolve(null);
336            }
337          );
338        });
339      });
340    }
341
342    t.it('should support the `RETURNING` clause using raw queries', async () => {
343      const db = SQLite.openDatabase('test.db');
344      await new Promise((resolve, reject) => {
345        db.transaction(
346          (tx) => {
347            const nop = () => {};
348            const onError = (_, error) => {
349              reject(error);
350              return false;
351            };
352
353            tx.executeSql('DROP TABLE IF EXISTS customers;', [], nop, onError);
354            tx.executeSql(
355              'CREATE TABLE customers (id PRIMARY KEY NOT NULL, name VARCHAR(255),email VARCHAR(255));',
356              [],
357              nop,
358              onError
359            );
360          },
361          reject,
362          () => {
363            resolve(null);
364          }
365        );
366      });
367
368      db.execRawQuery(
369        [
370          {
371            // Unsupprted on Android using the `exec` function
372            sql: "INSERT INTO customers (id, name, email) VALUES (1, 'John Doe', '[email protected]') RETURNING name, email;",
373            args: [],
374          },
375        ],
376        false,
377        (tx, results) => {
378          // @ts-expect-error
379          t.expect(results.rows[0].email).toBe('[email protected]');
380          // @ts-expect-error
381          t.expect(results.rows[0].name).toBe('John Doe');
382        }
383      );
384
385      db.execRawQuery(
386        [
387          {
388            sql: "UPDATE customers SET name='Jane Doe', email='[email protected]' WHERE id=1 RETURNING name, email;",
389            args: [],
390          },
391        ],
392        false,
393        (tx, results) => {
394          // @ts-expect-error
395          t.expect(results.rows[0].email).toBe('[email protected]');
396          // @ts-expect-error
397          t.expect(results.rows[0].name).toBe('Jane Doe');
398        }
399      );
400
401      db.execRawQuery(
402        [
403          {
404            // Unsupprted on Android using the `exec` function
405            sql: 'DELETE from customers WHERE id=1 RETURNING name, email;',
406            args: [],
407          },
408        ],
409        false,
410        (tx, results) => {
411          // @ts-expect-error
412          t.expect(results.rows[0].email).toBe('[email protected]');
413          // @ts-expect-error
414          t.expect(results.rows[0].name).toBe('Jane Doe');
415        }
416      );
417    });
418
419    t.it('should return correct rowsAffected value', async () => {
420      const db = SQLite.openDatabase('test.db');
421      await new Promise((resolve, reject) => {
422        db.transaction(
423          (tx) => {
424            const nop = () => {};
425            const onError = (tx, error) => {
426              reject(error);
427              return false;
428            };
429
430            tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError);
431            tx.executeSql(
432              'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
433              [],
434              nop,
435              onError
436            );
437            tx.executeSql(
438              'INSERT INTO Users (name) VALUES (?), (?), (?)',
439              ['name1', 'name2', 'name3'],
440              nop,
441              onError
442            );
443          },
444          reject,
445          () => {
446            resolve(null);
447          }
448        );
449      });
450      await new Promise((resolve, reject) => {
451        db.transaction(
452          (tx) => {
453            const onError = (tx, error) => {
454              reject(error);
455              return false;
456            };
457            tx.executeSql(
458              'DELETE FROM Users WHERE name=?',
459              ['name1'],
460              (tx, results) => {
461                t.expect(results.rowsAffected).toEqual(1);
462              },
463              onError
464            );
465            tx.executeSql(
466              'DELETE FROM Users WHERE name=? OR name=?',
467              ['name2', 'name3'],
468              (tx, results) => {
469                t.expect(results.rowsAffected).toEqual(2);
470              },
471              onError
472            );
473            tx.executeSql(
474              // ensure deletion succeedeed
475              'SELECT * from Users',
476              [],
477              (tx, results) => {
478                t.expect(results.rows.length).toEqual(0);
479              },
480              onError
481            );
482          },
483          reject,
484          () => {
485            resolve(null);
486          }
487        );
488      });
489    });
490
491    if (Platform.OS !== 'web') {
492      // It is not expected to work on web, since we cannot execute PRAGMA to enable foreign keys support
493      t.it('should return correct rowsAffected value when deleting cascade', async () => {
494        const db = SQLite.openDatabase('test.db');
495        db.exec([{ sql: 'PRAGMA foreign_keys = ON;', args: [] }], false, () => {});
496        await new Promise((resolve, reject) => {
497          db.transaction(
498            (tx) => {
499              const nop = () => {};
500              const onError = (tx, error) => {
501                reject(error);
502                return false;
503              };
504
505              tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError);
506              tx.executeSql('DROP TABLE IF EXISTS Posts;', [], nop, onError);
507              tx.executeSql(
508                'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
509                [],
510                nop,
511                onError
512              );
513              tx.executeSql(
514                'CREATE TABLE IF NOT EXISTS Posts (post_id INTEGER PRIMARY KEY NOT NULL, content VARCHAR(64), userposted INTEGER, FOREIGN KEY(userposted) REFERENCES Users(user_id) ON DELETE CASCADE);',
515                [],
516                nop,
517                onError
518              );
519              tx.executeSql(
520                'INSERT INTO Users (name) VALUES (?), (?), (?)',
521                ['name1', 'name2', 'name3'],
522                nop,
523                onError
524              );
525
526              tx.executeSql(
527                'INSERT INTO Posts (content, userposted) VALUES (?, ?), (?, ?), (?, ?)',
528                ['post1', 1, 'post2', 1, 'post3', 2],
529                nop,
530                onError
531              );
532              tx.executeSql('PRAGMA foreign_keys=off;', [], nop, onError);
533            },
534            reject,
535            () => {
536              resolve(null);
537            }
538          );
539        });
540        await new Promise((resolve, reject) => {
541          db.transaction(
542            (tx) => {
543              const nop = () => {};
544              const onError = (tx, error) => {
545                reject(error);
546                return false;
547              };
548              tx.executeSql('PRAGMA foreign_keys=on;', [], nop, onError);
549              tx.executeSql(
550                'DELETE FROM Users WHERE name=?',
551                ['name1'],
552                (tx, results) => {
553                  t.expect(results.rowsAffected).toEqual(1);
554                },
555                onError
556              );
557              tx.executeSql(
558                'DELETE FROM Users WHERE name=? OR name=?',
559                ['name2', 'name3'],
560                (tx, results) => {
561                  t.expect(results.rowsAffected).toEqual(2);
562                },
563                onError
564              );
565
566              tx.executeSql(
567                // ensure deletion succeeded
568                'SELECT * from Users',
569                [],
570                (tx, results) => {
571                  t.expect(results.rows.length).toEqual(0);
572                },
573                onError
574              );
575
576              tx.executeSql(
577                'SELECT * from Posts',
578                [],
579                (tx, results) => {
580                  t.expect(results.rows.length).toEqual(0);
581                },
582                onError
583              );
584              tx.executeSql('PRAGMA foreign_keys=off;', [], nop, onError);
585            },
586            reject,
587            () => {
588              resolve(null);
589            }
590          );
591        });
592      });
593    }
594
595    if (Platform.OS !== 'web') {
596      t.it('should delete db on filesystem from the `deleteAsync()` call', async () => {
597        const db = SQLite.openDatabase('test.db');
598        let fileInfo = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
599        t.expect(fileInfo.exists).toBeTruthy();
600
601        await db.closeAsync();
602        await db.deleteAsync();
603        fileInfo = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
604        t.expect(fileInfo.exists).toBeFalsy();
605      });
606    }
607  });
608
609  if (Platform.OS !== 'web') {
610    t.describe('SQLiteAsync', () => {
611      const throws = async (run) => {
612        let error = null;
613        try {
614          await run();
615        } catch (e) {
616          error = e;
617        }
618        t.expect(error).toBeTruthy();
619      };
620
621      t.it('should support async transaction', async () => {
622        const db = SQLite.openDatabase('test.db');
623
624        // create table
625        await db.transactionAsync(async (tx) => {
626          await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []);
627          await tx.executeSqlAsync(
628            'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
629            []
630          );
631        });
632
633        // fetch data from network
634        async function fakeUserFetcher(userID) {
635          switch (userID) {
636            case 1: {
637              return Promise.resolve('Tim Duncan');
638            }
639            case 2: {
640              return Promise.resolve('Manu Ginobili');
641            }
642            case 3: {
643              return Promise.resolve('Nikhilesh Sigatapu');
644            }
645            default: {
646              return null;
647            }
648          }
649        }
650
651        const userName = await fakeUserFetcher(1);
652        await db.transactionAsync(async (tx) => {
653          await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', [userName]);
654          const result = await tx.executeSqlAsync('SELECT * FROM Users LIMIT 1');
655          const currentUser = result.rows[0].name;
656          t.expect(currentUser).toEqual('Tim Duncan');
657        });
658      });
659
660      t.it('should load crsqlite extension correctly', async () => {
661        const db = SQLite.openDatabase('test.db');
662        await db.transactionAsync(async (tx) => {
663          await tx.executeSqlAsync('DROP TABLE IF EXISTS foo;', []);
664          await tx.executeSqlAsync('create table foo (a primary key, b INTEGER);', []);
665          await tx.executeSqlAsync('select crsql_as_crr("foo");', []);
666          await tx.executeSqlAsync('insert into foo (a,b) values (?, ?);', [1, 2]);
667          await tx.executeSqlAsync('insert into foo (a,b) values (?, ?);', [3, 4]);
668          const result = await tx.executeSqlAsync('select * from crsql_changes;', []);
669          const table = result.rows[0].table;
670          const value = result.rows[0].val;
671          t.expect(table).toEqual('foo');
672          t.expect(value).toEqual(2);
673        });
674      });
675
676      t.it('should support Promise.all', async () => {
677        const db = SQLite.openDatabase('test.db');
678
679        // create table
680        await db.transactionAsync(async (tx) => {
681          await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []);
682          await tx.executeSqlAsync(
683            'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
684            []
685          );
686        });
687
688        await db.transactionAsync(async (tx) => {
689          await Promise.all([
690            tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['aaa']),
691            tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['bbb']),
692            tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['ccc']),
693          ]);
694
695          const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users');
696          const recordCount = result.rows[0]['COUNT(*)'];
697          t.expect(recordCount).toEqual(3);
698        });
699      });
700
701      t.it(
702        'should return `could not prepare ...` error when having write statements in readOnly transaction',
703        async () => {
704          const db = SQLite.openDatabase('test.db');
705
706          // create table in readOnly transaction
707          await db.transactionAsync(async (tx) => {
708            let error: Error | null = null;
709            try {
710              await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []);
711            } catch (e: unknown) {
712              if (e instanceof Error) {
713                error = e;
714              }
715            }
716            t.expect(error).toBeDefined();
717            t.expect(error.message).toContain('could not prepare ');
718          }, true);
719        }
720      );
721
722      t.it('should rollback transaction when exception happens inside a transaction', async () => {
723        const db = SQLite.openDatabase('test.db');
724
725        // create table
726        await db.transactionAsync(async (tx) => {
727          await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []);
728          await tx.executeSqlAsync(
729            'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
730            []
731          );
732        });
733        await db.transactionAsync(async (tx) => {
734          await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['aaa']);
735        });
736        await db.transactionAsync(async (tx) => {
737          const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users');
738          const recordCount = result.rows[0]['COUNT(*)'];
739          t.expect(recordCount).toEqual(1);
740        }, true);
741
742        await throws(() =>
743          db.transactionAsync(async (tx) => {
744            await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['bbb']);
745            await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['ccc']);
746            // exeuting invalid sql statement will throw an exception
747            await tx.executeSqlAsync(null);
748          })
749        );
750
751        await db.transactionAsync(async (tx) => {
752          const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users');
753          const recordCount = result.rows[0]['COUNT(*)'];
754          t.expect(recordCount).toEqual(1);
755        }, true);
756      });
757
758      t.it('should support async PRAGMA statements', async () => {
759        const db = SQLite.openDatabase('test.db');
760        await db.transactionAsync(async (tx) => {
761          await tx.executeSqlAsync('DROP TABLE IF EXISTS SomeTable;', []);
762          await tx.executeSqlAsync(
763            'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
764            []
765          );
766          // a result-returning pragma
767          let result = await tx.executeSqlAsync('PRAGMA table_info(SomeTable);', []);
768          t.expect(result.rows.length).toEqual(2);
769          t.expect(result.rows[0].name).toEqual('id');
770          t.expect(result.rows[1].name).toEqual('name');
771          // a no-result pragma
772          await tx.executeSqlAsync('PRAGMA case_sensitive_like = true;', []);
773          // a setter/getter pragma
774          await tx.executeSqlAsync('PRAGMA user_version = 123;', []);
775          result = await tx.executeSqlAsync('PRAGMA user_version;', []);
776          t.expect(result.rows.length).toEqual(1);
777          t.expect(result.rows[0].user_version).toEqual(123);
778        });
779      });
780    }); // t.describe('SQLiteAsync')
781  }
782}
783