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