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