1c52e83e3SKudo Chienimport { Asset } from 'expo-asset'; 2c52e83e3SKudo Chienimport * as FS from 'expo-file-system'; 3c52e83e3SKudo Chienimport { Platform } from 'expo-modules-core'; 4c52e83e3SKudo Chienimport * as SQLite from 'expo-sqlite'; 5c52e83e3SKudo Chien 6c52e83e3SKudo Chienexport const name = 'SQLite'; 7c52e83e3SKudo Chien 84e419217SAlan Hughes// The version here needs to be the same as both the podspec and build.gradle for expo-sqlite 94b361cb0SAlan Hughesconst VERSION = '3.42.0'; 104e419217SAlan Hughes 11c52e83e3SKudo Chien// TODO: Only tests successful cases, needs to test error cases like bad database name etc. 12c52e83e3SKudo Chienexport function test(t) { 13c52e83e3SKudo Chien t.describe('SQLite', () => { 14c52e83e3SKudo Chien t.it('should be able to drop + create a table, insert, query', async () => { 15c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 16c52e83e3SKudo Chien await new Promise((resolve, reject) => { 17c52e83e3SKudo Chien db.transaction( 18c52e83e3SKudo Chien (tx) => { 19c52e83e3SKudo Chien const nop = () => {}; 20c52e83e3SKudo Chien const onError = (tx, error) => { 21c52e83e3SKudo Chien reject(error); 22c52e83e3SKudo Chien return false; 23c52e83e3SKudo Chien }; 24c52e83e3SKudo Chien 25c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError); 26c52e83e3SKudo Chien tx.executeSql( 27c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);', 28c52e83e3SKudo Chien [], 29c52e83e3SKudo Chien nop, 30c52e83e3SKudo Chien onError 31c52e83e3SKudo Chien ); 32c52e83e3SKudo Chien tx.executeSql( 33c52e83e3SKudo Chien 'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)', 34c52e83e3SKudo Chien ['Tim Duncan', 1, 23.4], 35c52e83e3SKudo Chien nop, 36c52e83e3SKudo Chien onError 37c52e83e3SKudo Chien ); 38c52e83e3SKudo Chien tx.executeSql( 39c52e83e3SKudo Chien 'INSERT INTO Users (name, k, j) VALUES ("Manu Ginobili", 5, 72.8)', 40c52e83e3SKudo Chien [], 41c52e83e3SKudo Chien nop, 42c52e83e3SKudo Chien onError 43c52e83e3SKudo Chien ); 44c52e83e3SKudo Chien tx.executeSql( 45c52e83e3SKudo Chien 'INSERT INTO Users (name, k, j) VALUES ("Nikhilesh Sigatapu", 7, 42.14)', 46c52e83e3SKudo Chien [], 47c52e83e3SKudo Chien nop, 48c52e83e3SKudo Chien onError 49c52e83e3SKudo Chien ); 50c52e83e3SKudo Chien 51c52e83e3SKudo Chien tx.executeSql( 52c52e83e3SKudo Chien 'SELECT * FROM Users', 53c52e83e3SKudo Chien [], 54c52e83e3SKudo Chien (tx, results) => { 55c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(3); 56c52e83e3SKudo Chien t.expect(results.rows.item(0).j).toBeCloseTo(23.4); 57c52e83e3SKudo Chien }, 58c52e83e3SKudo Chien onError 59c52e83e3SKudo Chien ); 60c52e83e3SKudo Chien }, 61c52e83e3SKudo Chien reject, 62c52e83e3SKudo Chien () => { 63c52e83e3SKudo Chien resolve(null); 64c52e83e3SKudo Chien } 65c52e83e3SKudo Chien ); 66c52e83e3SKudo Chien }); 67c52e83e3SKudo Chien 68c52e83e3SKudo Chien if (Platform.OS !== 'web') { 69c52e83e3SKudo Chien const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 70c52e83e3SKudo Chien t.expect(exists).toBeTruthy(); 71c52e83e3SKudo Chien } 72c52e83e3SKudo Chien }); 73c52e83e3SKudo Chien 744e419217SAlan Hughes t.it(`should use specified SQLite version: ${VERSION}`, () => { 754e419217SAlan Hughes const db = SQLite.openDatabase('test.db'); 764e419217SAlan Hughes 774e419217SAlan Hughes db.transaction((tx) => { 784e419217SAlan Hughes tx.executeSql('SELECT sqlite_version()', [], (_, results) => { 794e419217SAlan Hughes const queryVersion = results.rows._array[0]['sqlite_version()']; 804e419217SAlan Hughes t.expect(queryVersion).toEqual(VERSION); 814e419217SAlan Hughes }); 824e419217SAlan Hughes }); 834e419217SAlan Hughes }); 844e419217SAlan Hughes 854e419217SAlan Hughes t.it(`unixepoch() is supported`, () => { 864e419217SAlan Hughes const db = SQLite.openDatabase('test.db'); 874e419217SAlan Hughes 884e419217SAlan Hughes db.transaction((tx) => { 894e419217SAlan Hughes tx.executeSql('SELECT unixepoch()', [], (_, results) => { 904e419217SAlan Hughes const epoch = results.rows._array[0]['unixepoch()']; 914e419217SAlan Hughes t.expect(epoch).toBeTruthy(); 924e419217SAlan Hughes }); 934e419217SAlan Hughes }); 944e419217SAlan Hughes }); 954e419217SAlan Hughes 96c52e83e3SKudo Chien if (Platform.OS !== 'web') { 97c52e83e3SKudo Chien t.it( 98c52e83e3SKudo Chien 'should work with a downloaded .db file', 99c52e83e3SKudo Chien async () => { 100c52e83e3SKudo Chien await FS.downloadAsync( 101c52e83e3SKudo Chien Asset.fromModule(require('../assets/asset-db.db')).uri, 102c52e83e3SKudo Chien `${FS.documentDirectory}SQLite/downloaded.db` 103c52e83e3SKudo Chien ); 104c52e83e3SKudo Chien 105c52e83e3SKudo Chien const db = SQLite.openDatabase('downloaded.db'); 106c52e83e3SKudo Chien await new Promise((resolve, reject) => { 107c52e83e3SKudo Chien db.transaction( 108c52e83e3SKudo Chien (tx) => { 109c52e83e3SKudo Chien const onError = (tx, error) => { 110c52e83e3SKudo Chien reject(error); 111c52e83e3SKudo Chien return false; 112c52e83e3SKudo Chien }; 113c52e83e3SKudo Chien tx.executeSql( 114c52e83e3SKudo Chien 'SELECT * FROM Users', 115c52e83e3SKudo Chien [], 116c52e83e3SKudo Chien (tx, results) => { 117c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(3); 118c52e83e3SKudo Chien t.expect(results.rows._array[0].j).toBeCloseTo(23.4); 119c52e83e3SKudo Chien }, 120c52e83e3SKudo Chien onError 121c52e83e3SKudo Chien ); 122c52e83e3SKudo Chien }, 123c52e83e3SKudo Chien reject, 124c52e83e3SKudo Chien () => { 125c52e83e3SKudo Chien resolve(null); 126c52e83e3SKudo Chien } 127c52e83e3SKudo Chien ); 128c52e83e3SKudo Chien }); 129c52e83e3SKudo Chien db.closeAsync(); 130c52e83e3SKudo Chien }, 131c52e83e3SKudo Chien 30000 132c52e83e3SKudo Chien ); 133c52e83e3SKudo Chien } 134c52e83e3SKudo Chien 135c52e83e3SKudo Chien t.it('should be able to recreate db from scratch by deleting file', async () => { 136c52e83e3SKudo Chien { 137c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 138c52e83e3SKudo Chien await new Promise((resolve, reject) => { 139c52e83e3SKudo Chien db.transaction( 140c52e83e3SKudo Chien (tx) => { 141c52e83e3SKudo Chien const nop = () => {}; 142c52e83e3SKudo Chien const onError = (tx, error) => { 143c52e83e3SKudo Chien reject(error); 144c52e83e3SKudo Chien return false; 145c52e83e3SKudo Chien }; 146c52e83e3SKudo Chien 147c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError); 148c52e83e3SKudo Chien tx.executeSql( 149c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);', 150c52e83e3SKudo Chien [], 151c52e83e3SKudo Chien nop, 152c52e83e3SKudo Chien onError 153c52e83e3SKudo Chien ); 154c52e83e3SKudo Chien tx.executeSql( 155c52e83e3SKudo Chien 'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)', 156c52e83e3SKudo Chien ['Tim Duncan', 1, 23.4], 157c52e83e3SKudo Chien nop, 158c52e83e3SKudo Chien onError 159c52e83e3SKudo Chien ); 160c52e83e3SKudo Chien 161c52e83e3SKudo Chien tx.executeSql( 162c52e83e3SKudo Chien 'SELECT * FROM Users', 163c52e83e3SKudo Chien [], 164c52e83e3SKudo Chien (tx, results) => { 165c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(1); 166c52e83e3SKudo Chien }, 167c52e83e3SKudo Chien onError 168c52e83e3SKudo Chien ); 169c52e83e3SKudo Chien }, 170c52e83e3SKudo Chien reject, 171c52e83e3SKudo Chien () => { 172c52e83e3SKudo Chien resolve(null); 173c52e83e3SKudo Chien } 174c52e83e3SKudo Chien ); 175c52e83e3SKudo Chien }); 176c52e83e3SKudo Chien } 177c52e83e3SKudo Chien 178c52e83e3SKudo Chien if (Platform.OS !== 'web') { 179c52e83e3SKudo Chien const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 180c52e83e3SKudo Chien t.expect(exists).toBeTruthy(); 181c52e83e3SKudo Chien } 182c52e83e3SKudo Chien 183c52e83e3SKudo Chien if (Platform.OS !== 'web') { 184c52e83e3SKudo Chien await FS.deleteAsync(`${FS.documentDirectory}SQLite/test.db`); 185c52e83e3SKudo Chien const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 186c52e83e3SKudo Chien t.expect(exists).toBeFalsy(); 187c52e83e3SKudo Chien } 188c52e83e3SKudo Chien 189c52e83e3SKudo Chien { 190c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 191c52e83e3SKudo Chien await new Promise((resolve, reject) => { 192c52e83e3SKudo Chien db.transaction( 193c52e83e3SKudo Chien (tx) => { 194c52e83e3SKudo Chien const nop = () => {}; 195c52e83e3SKudo Chien const onError = (tx, error) => { 196c52e83e3SKudo Chien reject(error); 197c52e83e3SKudo Chien return false; 198c52e83e3SKudo Chien }; 199c52e83e3SKudo Chien 200c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError); 201c52e83e3SKudo Chien tx.executeSql( 202c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64), k INT, j REAL);', 203c52e83e3SKudo Chien [], 204c52e83e3SKudo Chien nop, 205c52e83e3SKudo Chien onError 206c52e83e3SKudo Chien ); 207c52e83e3SKudo Chien tx.executeSql( 208c52e83e3SKudo Chien 'SELECT * FROM Users', 209c52e83e3SKudo Chien [], 210c52e83e3SKudo Chien (tx, results) => { 211c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(0); 212c52e83e3SKudo Chien }, 213c52e83e3SKudo Chien onError 214c52e83e3SKudo Chien ); 215c52e83e3SKudo Chien 216c52e83e3SKudo Chien tx.executeSql( 217c52e83e3SKudo Chien 'INSERT INTO Users (name, k, j) VALUES (?, ?, ?)', 218c52e83e3SKudo Chien ['Tim Duncan', 1, 23.4], 219c52e83e3SKudo Chien nop, 220c52e83e3SKudo Chien onError 221c52e83e3SKudo Chien ); 222c52e83e3SKudo Chien tx.executeSql( 223c52e83e3SKudo Chien 'SELECT * FROM Users', 224c52e83e3SKudo Chien [], 225c52e83e3SKudo Chien (tx, results) => { 226c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(1); 227c52e83e3SKudo Chien }, 228c52e83e3SKudo Chien onError 229c52e83e3SKudo Chien ); 230c52e83e3SKudo Chien }, 231c52e83e3SKudo Chien reject, 232c52e83e3SKudo Chien () => { 233c52e83e3SKudo Chien resolve(null); 234c52e83e3SKudo Chien } 235c52e83e3SKudo Chien ); 236c52e83e3SKudo Chien }); 237c52e83e3SKudo Chien } 238c52e83e3SKudo Chien }); 239c52e83e3SKudo Chien 240c52e83e3SKudo Chien t.it('should maintain correct type of potentialy null bind parameters', async () => { 241c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 242c52e83e3SKudo Chien await new Promise((resolve, reject) => { 243c52e83e3SKudo Chien db.transaction( 244c52e83e3SKudo Chien (tx) => { 245c52e83e3SKudo Chien const nop = () => {}; 246c52e83e3SKudo Chien const onError = (tx, error) => { 247c52e83e3SKudo Chien reject(error); 248c52e83e3SKudo Chien return false; 249c52e83e3SKudo Chien }; 250c52e83e3SKudo Chien 251c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Nulling;', [], nop, onError); 252c52e83e3SKudo Chien tx.executeSql( 253c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Nulling (id INTEGER PRIMARY KEY NOT NULL, x NUMERIC, y NUMERIC)', 254c52e83e3SKudo Chien [], 255c52e83e3SKudo Chien nop, 256c52e83e3SKudo Chien onError 257c52e83e3SKudo Chien ); 258c52e83e3SKudo Chien tx.executeSql('INSERT INTO Nulling (x, y) VALUES (?, ?)', [null, null], nop, onError); 259c52e83e3SKudo Chien tx.executeSql('INSERT INTO Nulling (x, y) VALUES (null, null)', [], nop, onError); 260c52e83e3SKudo Chien 261c52e83e3SKudo Chien tx.executeSql( 262c52e83e3SKudo Chien 'SELECT * FROM Nulling', 263c52e83e3SKudo Chien [], 264c52e83e3SKudo Chien (tx, results) => { 265c52e83e3SKudo Chien t.expect(results.rows.item(0).x).toBeNull(); 266c52e83e3SKudo Chien t.expect(results.rows.item(0).y).toBeNull(); 267c52e83e3SKudo Chien t.expect(results.rows.item(1).x).toBeNull(); 268c52e83e3SKudo Chien t.expect(results.rows.item(1).y).toBeNull(); 269c52e83e3SKudo Chien }, 270c52e83e3SKudo Chien onError 271c52e83e3SKudo Chien ); 272c52e83e3SKudo Chien }, 273c52e83e3SKudo Chien reject, 274c52e83e3SKudo Chien () => { 275c52e83e3SKudo Chien resolve(null); 276c52e83e3SKudo Chien } 277c52e83e3SKudo Chien ); 278c52e83e3SKudo Chien }); 279c52e83e3SKudo Chien 280c52e83e3SKudo Chien if (Platform.OS !== 'web') { 281c52e83e3SKudo Chien const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 282c52e83e3SKudo Chien t.expect(exists).toBeTruthy(); 283c52e83e3SKudo Chien } 284c52e83e3SKudo Chien }); 285c52e83e3SKudo Chien 286c52e83e3SKudo Chien // Do not try to test PRAGMA statements support in web 287c52e83e3SKudo Chien // as it is expected to not be working. 288c52e83e3SKudo Chien // See https://stackoverflow.com/a/10298712 289c52e83e3SKudo Chien if (Platform.OS !== 'web') { 290c52e83e3SKudo Chien t.it('should support PRAGMA statements', async () => { 291c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 292c52e83e3SKudo Chien await new Promise((resolve, reject) => { 293c52e83e3SKudo Chien db.transaction( 294c52e83e3SKudo Chien (tx) => { 295c52e83e3SKudo Chien const nop = () => {}; 296c52e83e3SKudo Chien const onError = (tx, error) => { 297c52e83e3SKudo Chien reject(error); 298c52e83e3SKudo Chien return false; 299c52e83e3SKudo Chien }; 300c52e83e3SKudo Chien 301c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS SomeTable;', [], nop, onError); 302c52e83e3SKudo Chien tx.executeSql( 303c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 304c52e83e3SKudo Chien [], 305c52e83e3SKudo Chien nop, 306c52e83e3SKudo Chien onError 307c52e83e3SKudo Chien ); 308c52e83e3SKudo Chien // a result-returning pragma 309c52e83e3SKudo Chien tx.executeSql( 310c52e83e3SKudo Chien 'PRAGMA table_info(SomeTable);', 311c52e83e3SKudo Chien [], 312c52e83e3SKudo Chien (tx, results) => { 313c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(2); 314c52e83e3SKudo Chien t.expect(results.rows.item(0).name).toEqual('id'); 315c52e83e3SKudo Chien t.expect(results.rows.item(1).name).toEqual('name'); 316c52e83e3SKudo Chien }, 317c52e83e3SKudo Chien onError 318c52e83e3SKudo Chien ); 319c52e83e3SKudo Chien // a no-result pragma 320c52e83e3SKudo Chien tx.executeSql('PRAGMA case_sensitive_like = true;', [], nop, onError); 321c52e83e3SKudo Chien // a setter/getter pragma 322c52e83e3SKudo Chien tx.executeSql('PRAGMA user_version = 123;', [], nop, onError); 323c52e83e3SKudo Chien tx.executeSql( 324c52e83e3SKudo Chien 'PRAGMA user_version;', 325c52e83e3SKudo Chien [], 326c52e83e3SKudo Chien (tx, results) => { 327c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(1); 328c52e83e3SKudo Chien t.expect(results.rows.item(0).user_version).toEqual(123); 329c52e83e3SKudo Chien }, 330c52e83e3SKudo Chien onError 331c52e83e3SKudo Chien ); 332c52e83e3SKudo Chien }, 333c52e83e3SKudo Chien reject, 334c52e83e3SKudo Chien () => { 335c52e83e3SKudo Chien resolve(null); 336c52e83e3SKudo Chien } 337c52e83e3SKudo Chien ); 338c52e83e3SKudo Chien }); 339c52e83e3SKudo Chien }); 340c52e83e3SKudo Chien } 341c52e83e3SKudo Chien 34250e16f8cSAlan Hughes t.it('should support the `RETURNING` clause using raw queries', async () => { 34350e16f8cSAlan Hughes const db = SQLite.openDatabase('test.db'); 34450e16f8cSAlan Hughes await new Promise((resolve, reject) => { 34550e16f8cSAlan Hughes db.transaction( 34650e16f8cSAlan Hughes (tx) => { 34750e16f8cSAlan Hughes const nop = () => {}; 34850e16f8cSAlan Hughes const onError = (_, error) => { 34950e16f8cSAlan Hughes reject(error); 35050e16f8cSAlan Hughes return false; 35150e16f8cSAlan Hughes }; 35250e16f8cSAlan Hughes 35350e16f8cSAlan Hughes tx.executeSql('DROP TABLE IF EXISTS customers;', [], nop, onError); 35450e16f8cSAlan Hughes tx.executeSql( 35550e16f8cSAlan Hughes 'CREATE TABLE customers (id PRIMARY KEY NOT NULL, name VARCHAR(255),email VARCHAR(255));', 35650e16f8cSAlan Hughes [], 35750e16f8cSAlan Hughes nop, 35850e16f8cSAlan Hughes onError 35950e16f8cSAlan Hughes ); 36050e16f8cSAlan Hughes }, 36150e16f8cSAlan Hughes reject, 36250e16f8cSAlan Hughes () => { 36350e16f8cSAlan Hughes resolve(null); 36450e16f8cSAlan Hughes } 36550e16f8cSAlan Hughes ); 36650e16f8cSAlan Hughes }); 36750e16f8cSAlan Hughes 36850e16f8cSAlan Hughes db.execRawQuery( 36950e16f8cSAlan Hughes [ 37050e16f8cSAlan Hughes { 37150e16f8cSAlan Hughes // Unsupprted on Android using the `exec` function 37250e16f8cSAlan Hughes sql: "INSERT INTO customers (id, name, email) VALUES (1, 'John Doe', '[email protected]') RETURNING name, email;", 37350e16f8cSAlan Hughes args: [], 37450e16f8cSAlan Hughes }, 37550e16f8cSAlan Hughes ], 37650e16f8cSAlan Hughes false, 37750e16f8cSAlan Hughes (tx, results) => { 37850e16f8cSAlan Hughes // @ts-expect-error 37950e16f8cSAlan Hughes t.expect(results.rows[0].email).toBe('[email protected]'); 38050e16f8cSAlan Hughes // @ts-expect-error 38150e16f8cSAlan Hughes t.expect(results.rows[0].name).toBe('John Doe'); 38250e16f8cSAlan Hughes } 38350e16f8cSAlan Hughes ); 38450e16f8cSAlan Hughes 38550e16f8cSAlan Hughes db.execRawQuery( 38650e16f8cSAlan Hughes [ 38750e16f8cSAlan Hughes { 38850e16f8cSAlan Hughes sql: "UPDATE customers SET name='Jane Doe', email='[email protected]' WHERE id=1 RETURNING name, email;", 38950e16f8cSAlan Hughes args: [], 39050e16f8cSAlan Hughes }, 39150e16f8cSAlan Hughes ], 39250e16f8cSAlan Hughes false, 39350e16f8cSAlan Hughes (tx, results) => { 39450e16f8cSAlan Hughes // @ts-expect-error 39550e16f8cSAlan Hughes t.expect(results.rows[0].email).toBe('[email protected]'); 39650e16f8cSAlan Hughes // @ts-expect-error 39750e16f8cSAlan Hughes t.expect(results.rows[0].name).toBe('Jane Doe'); 39850e16f8cSAlan Hughes } 39950e16f8cSAlan Hughes ); 40050e16f8cSAlan Hughes 40150e16f8cSAlan Hughes db.execRawQuery( 40250e16f8cSAlan Hughes [ 40350e16f8cSAlan Hughes { 40450e16f8cSAlan Hughes // Unsupprted on Android using the `exec` function 40550e16f8cSAlan Hughes sql: 'DELETE from customers WHERE id=1 RETURNING name, email;', 40650e16f8cSAlan Hughes args: [], 40750e16f8cSAlan Hughes }, 40850e16f8cSAlan Hughes ], 40950e16f8cSAlan Hughes false, 41050e16f8cSAlan Hughes (tx, results) => { 41150e16f8cSAlan Hughes // @ts-expect-error 41250e16f8cSAlan Hughes t.expect(results.rows[0].email).toBe('[email protected]'); 41350e16f8cSAlan Hughes // @ts-expect-error 41450e16f8cSAlan Hughes t.expect(results.rows[0].name).toBe('Jane Doe'); 41550e16f8cSAlan Hughes } 41650e16f8cSAlan Hughes ); 41750e16f8cSAlan Hughes }); 41850e16f8cSAlan Hughes 419c52e83e3SKudo Chien t.it('should return correct rowsAffected value', async () => { 420c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 421c52e83e3SKudo Chien await new Promise((resolve, reject) => { 422c52e83e3SKudo Chien db.transaction( 423c52e83e3SKudo Chien (tx) => { 424c52e83e3SKudo Chien const nop = () => {}; 425c52e83e3SKudo Chien const onError = (tx, error) => { 426c52e83e3SKudo Chien reject(error); 427c52e83e3SKudo Chien return false; 428c52e83e3SKudo Chien }; 429c52e83e3SKudo Chien 430c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError); 431c52e83e3SKudo Chien tx.executeSql( 432c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 433c52e83e3SKudo Chien [], 434c52e83e3SKudo Chien nop, 435c52e83e3SKudo Chien onError 436c52e83e3SKudo Chien ); 437c52e83e3SKudo Chien tx.executeSql( 438c52e83e3SKudo Chien 'INSERT INTO Users (name) VALUES (?), (?), (?)', 439c52e83e3SKudo Chien ['name1', 'name2', 'name3'], 440c52e83e3SKudo Chien nop, 441c52e83e3SKudo Chien onError 442c52e83e3SKudo Chien ); 443c52e83e3SKudo Chien }, 444c52e83e3SKudo Chien reject, 445c52e83e3SKudo Chien () => { 446c52e83e3SKudo Chien resolve(null); 447c52e83e3SKudo Chien } 448c52e83e3SKudo Chien ); 449c52e83e3SKudo Chien }); 450c52e83e3SKudo Chien await new Promise((resolve, reject) => { 451c52e83e3SKudo Chien db.transaction( 452c52e83e3SKudo Chien (tx) => { 453c52e83e3SKudo Chien const onError = (tx, error) => { 454c52e83e3SKudo Chien reject(error); 455c52e83e3SKudo Chien return false; 456c52e83e3SKudo Chien }; 457c52e83e3SKudo Chien tx.executeSql( 458c52e83e3SKudo Chien 'DELETE FROM Users WHERE name=?', 459c52e83e3SKudo Chien ['name1'], 460c52e83e3SKudo Chien (tx, results) => { 461c52e83e3SKudo Chien t.expect(results.rowsAffected).toEqual(1); 462c52e83e3SKudo Chien }, 463c52e83e3SKudo Chien onError 464c52e83e3SKudo Chien ); 465c52e83e3SKudo Chien tx.executeSql( 466c52e83e3SKudo Chien 'DELETE FROM Users WHERE name=? OR name=?', 467c52e83e3SKudo Chien ['name2', 'name3'], 468c52e83e3SKudo Chien (tx, results) => { 469c52e83e3SKudo Chien t.expect(results.rowsAffected).toEqual(2); 470c52e83e3SKudo Chien }, 471c52e83e3SKudo Chien onError 472c52e83e3SKudo Chien ); 473c52e83e3SKudo Chien tx.executeSql( 474c52e83e3SKudo Chien // ensure deletion succeedeed 475c52e83e3SKudo Chien 'SELECT * from Users', 476c52e83e3SKudo Chien [], 477c52e83e3SKudo Chien (tx, results) => { 478c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(0); 479c52e83e3SKudo Chien }, 480c52e83e3SKudo Chien onError 481c52e83e3SKudo Chien ); 482c52e83e3SKudo Chien }, 483c52e83e3SKudo Chien reject, 484c52e83e3SKudo Chien () => { 485c52e83e3SKudo Chien resolve(null); 486c52e83e3SKudo Chien } 487c52e83e3SKudo Chien ); 488c52e83e3SKudo Chien }); 489c52e83e3SKudo Chien }); 490c52e83e3SKudo Chien 491c52e83e3SKudo Chien if (Platform.OS !== 'web') { 492c52e83e3SKudo Chien // It is not expected to work on web, since we cannot execute PRAGMA to enable foreign keys support 493c52e83e3SKudo Chien t.it('should return correct rowsAffected value when deleting cascade', async () => { 494c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 495c52e83e3SKudo Chien db.exec([{ sql: 'PRAGMA foreign_keys = ON;', args: [] }], false, () => {}); 496c52e83e3SKudo Chien await new Promise((resolve, reject) => { 497c52e83e3SKudo Chien db.transaction( 498c52e83e3SKudo Chien (tx) => { 499c52e83e3SKudo Chien const nop = () => {}; 500c52e83e3SKudo Chien const onError = (tx, error) => { 501c52e83e3SKudo Chien reject(error); 502c52e83e3SKudo Chien return false; 503c52e83e3SKudo Chien }; 504c52e83e3SKudo Chien 505c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Users;', [], nop, onError); 506c52e83e3SKudo Chien tx.executeSql('DROP TABLE IF EXISTS Posts;', [], nop, onError); 507c52e83e3SKudo Chien tx.executeSql( 508c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 509c52e83e3SKudo Chien [], 510c52e83e3SKudo Chien nop, 511c52e83e3SKudo Chien onError 512c52e83e3SKudo Chien ); 513c52e83e3SKudo Chien tx.executeSql( 514c52e83e3SKudo Chien '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);', 515c52e83e3SKudo Chien [], 516c52e83e3SKudo Chien nop, 517c52e83e3SKudo Chien onError 518c52e83e3SKudo Chien ); 519c52e83e3SKudo Chien tx.executeSql( 520c52e83e3SKudo Chien 'INSERT INTO Users (name) VALUES (?), (?), (?)', 521c52e83e3SKudo Chien ['name1', 'name2', 'name3'], 522c52e83e3SKudo Chien nop, 523c52e83e3SKudo Chien onError 524c52e83e3SKudo Chien ); 525c52e83e3SKudo Chien 526c52e83e3SKudo Chien tx.executeSql( 527c52e83e3SKudo Chien 'INSERT INTO Posts (content, userposted) VALUES (?, ?), (?, ?), (?, ?)', 528c52e83e3SKudo Chien ['post1', 1, 'post2', 1, 'post3', 2], 529c52e83e3SKudo Chien nop, 530c52e83e3SKudo Chien onError 531c52e83e3SKudo Chien ); 532c52e83e3SKudo Chien tx.executeSql('PRAGMA foreign_keys=off;', [], nop, onError); 533c52e83e3SKudo Chien }, 534c52e83e3SKudo Chien reject, 535c52e83e3SKudo Chien () => { 536c52e83e3SKudo Chien resolve(null); 537c52e83e3SKudo Chien } 538c52e83e3SKudo Chien ); 539c52e83e3SKudo Chien }); 540c52e83e3SKudo Chien await new Promise((resolve, reject) => { 541c52e83e3SKudo Chien db.transaction( 542c52e83e3SKudo Chien (tx) => { 543c52e83e3SKudo Chien const nop = () => {}; 544c52e83e3SKudo Chien const onError = (tx, error) => { 545c52e83e3SKudo Chien reject(error); 546c52e83e3SKudo Chien return false; 547c52e83e3SKudo Chien }; 548c52e83e3SKudo Chien tx.executeSql('PRAGMA foreign_keys=on;', [], nop, onError); 549c52e83e3SKudo Chien tx.executeSql( 550c52e83e3SKudo Chien 'DELETE FROM Users WHERE name=?', 551c52e83e3SKudo Chien ['name1'], 552c52e83e3SKudo Chien (tx, results) => { 553c52e83e3SKudo Chien t.expect(results.rowsAffected).toEqual(1); 554c52e83e3SKudo Chien }, 555c52e83e3SKudo Chien onError 556c52e83e3SKudo Chien ); 557c52e83e3SKudo Chien tx.executeSql( 558c52e83e3SKudo Chien 'DELETE FROM Users WHERE name=? OR name=?', 559c52e83e3SKudo Chien ['name2', 'name3'], 560c52e83e3SKudo Chien (tx, results) => { 561c52e83e3SKudo Chien t.expect(results.rowsAffected).toEqual(2); 562c52e83e3SKudo Chien }, 563c52e83e3SKudo Chien onError 564c52e83e3SKudo Chien ); 565c52e83e3SKudo Chien 566c52e83e3SKudo Chien tx.executeSql( 567c52e83e3SKudo Chien // ensure deletion succeeded 568c52e83e3SKudo Chien 'SELECT * from Users', 569c52e83e3SKudo Chien [], 570c52e83e3SKudo Chien (tx, results) => { 571c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(0); 572c52e83e3SKudo Chien }, 573c52e83e3SKudo Chien onError 574c52e83e3SKudo Chien ); 575c52e83e3SKudo Chien 576c52e83e3SKudo Chien tx.executeSql( 577c52e83e3SKudo Chien 'SELECT * from Posts', 578c52e83e3SKudo Chien [], 579c52e83e3SKudo Chien (tx, results) => { 580c52e83e3SKudo Chien t.expect(results.rows.length).toEqual(0); 581c52e83e3SKudo Chien }, 582c52e83e3SKudo Chien onError 583c52e83e3SKudo Chien ); 584c52e83e3SKudo Chien tx.executeSql('PRAGMA foreign_keys=off;', [], nop, onError); 585c52e83e3SKudo Chien }, 586c52e83e3SKudo Chien reject, 587c52e83e3SKudo Chien () => { 588c52e83e3SKudo Chien resolve(null); 589c52e83e3SKudo Chien } 590c52e83e3SKudo Chien ); 591c52e83e3SKudo Chien }); 592c52e83e3SKudo Chien }); 593c52e83e3SKudo Chien } 594c52e83e3SKudo Chien 595c52e83e3SKudo Chien if (Platform.OS !== 'web') { 596c52e83e3SKudo Chien t.it('should delete db on filesystem from the `deleteAsync()` call', async () => { 597c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 598c52e83e3SKudo Chien let fileInfo = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 599c52e83e3SKudo Chien t.expect(fileInfo.exists).toBeTruthy(); 600c52e83e3SKudo Chien 601c52e83e3SKudo Chien await db.closeAsync(); 602c52e83e3SKudo Chien await db.deleteAsync(); 603c52e83e3SKudo Chien fileInfo = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`); 604c52e83e3SKudo Chien t.expect(fileInfo.exists).toBeFalsy(); 605c52e83e3SKudo Chien }); 606c52e83e3SKudo Chien } 607c52e83e3SKudo Chien }); 608c52e83e3SKudo Chien 609c52e83e3SKudo Chien if (Platform.OS !== 'web') { 610c52e83e3SKudo Chien t.describe('SQLiteAsync', () => { 611c52e83e3SKudo Chien const throws = async (run) => { 612c52e83e3SKudo Chien let error = null; 613c52e83e3SKudo Chien try { 614c52e83e3SKudo Chien await run(); 615c52e83e3SKudo Chien } catch (e) { 616c52e83e3SKudo Chien error = e; 617c52e83e3SKudo Chien } 618c52e83e3SKudo Chien t.expect(error).toBeTruthy(); 619c52e83e3SKudo Chien }; 620c52e83e3SKudo Chien 621c52e83e3SKudo Chien t.it('should support async transaction', async () => { 622c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 623c52e83e3SKudo Chien 624c52e83e3SKudo Chien // create table 625c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 626c52e83e3SKudo Chien await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []); 627c52e83e3SKudo Chien await tx.executeSqlAsync( 628c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 629c52e83e3SKudo Chien [] 630c52e83e3SKudo Chien ); 631c52e83e3SKudo Chien }); 632c52e83e3SKudo Chien 633c52e83e3SKudo Chien // fetch data from network 634c52e83e3SKudo Chien async function fakeUserFetcher(userID) { 635c52e83e3SKudo Chien switch (userID) { 636c52e83e3SKudo Chien case 1: { 637c52e83e3SKudo Chien return Promise.resolve('Tim Duncan'); 638c52e83e3SKudo Chien } 639c52e83e3SKudo Chien case 2: { 640c52e83e3SKudo Chien return Promise.resolve('Manu Ginobili'); 641c52e83e3SKudo Chien } 642c52e83e3SKudo Chien case 3: { 643c52e83e3SKudo Chien return Promise.resolve('Nikhilesh Sigatapu'); 644c52e83e3SKudo Chien } 645c52e83e3SKudo Chien default: { 646c52e83e3SKudo Chien return null; 647c52e83e3SKudo Chien } 648c52e83e3SKudo Chien } 649c52e83e3SKudo Chien } 650c52e83e3SKudo Chien 651c52e83e3SKudo Chien const userName = await fakeUserFetcher(1); 652c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 653c52e83e3SKudo Chien await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', [userName]); 654c52e83e3SKudo Chien const result = await tx.executeSqlAsync('SELECT * FROM Users LIMIT 1'); 655c52e83e3SKudo Chien const currentUser = result.rows[0].name; 656c52e83e3SKudo Chien t.expect(currentUser).toEqual('Tim Duncan'); 657c52e83e3SKudo Chien }); 658c52e83e3SKudo Chien }); 659c52e83e3SKudo Chien 66074e0b8dfSAlan Hughes t.it('should load crsqlite extension correctly', async () => { 66174e0b8dfSAlan Hughes const db = SQLite.openDatabase('test.db'); 6623273f84bSAlan Hughes await db.transactionAsync(async (tx) => { 6633273f84bSAlan Hughes await tx.executeSqlAsync('DROP TABLE IF EXISTS foo;', []); 664*36864ecaSAlan Hughes await tx.executeSqlAsync('create table foo (a primary key, b INTEGER);', []); 6653273f84bSAlan Hughes await tx.executeSqlAsync('select crsql_as_crr("foo");', []); 666c4573fffSKudo Chien await tx.executeSqlAsync('insert into foo (a,b) values (?, ?);', [1, 2]); 667c4573fffSKudo Chien await tx.executeSqlAsync('insert into foo (a,b) values (?, ?);', [3, 4]); 6683273f84bSAlan Hughes const result = await tx.executeSqlAsync('select * from crsql_changes;', []); 6693273f84bSAlan Hughes const table = result.rows[0].table; 6703273f84bSAlan Hughes const value = result.rows[0].val; 6713273f84bSAlan Hughes t.expect(table).toEqual('foo'); 6723273f84bSAlan Hughes t.expect(value).toEqual(2); 6733273f84bSAlan Hughes }); 67474e0b8dfSAlan Hughes }); 67574e0b8dfSAlan Hughes 676c52e83e3SKudo Chien t.it('should support Promise.all', async () => { 677c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 678c52e83e3SKudo Chien 679c52e83e3SKudo Chien // create table 680c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 681c52e83e3SKudo Chien await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []); 682c52e83e3SKudo Chien await tx.executeSqlAsync( 683c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 684c52e83e3SKudo Chien [] 685c52e83e3SKudo Chien ); 686c52e83e3SKudo Chien }); 687c52e83e3SKudo Chien 688c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 689c52e83e3SKudo Chien await Promise.all([ 690c52e83e3SKudo Chien tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['aaa']), 691c52e83e3SKudo Chien tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['bbb']), 692c52e83e3SKudo Chien tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['ccc']), 693c52e83e3SKudo Chien ]); 694c52e83e3SKudo Chien 695c52e83e3SKudo Chien const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users'); 696c52e83e3SKudo Chien const recordCount = result.rows[0]['COUNT(*)']; 697c52e83e3SKudo Chien t.expect(recordCount).toEqual(3); 698c52e83e3SKudo Chien }); 699c52e83e3SKudo Chien }); 700c52e83e3SKudo Chien 701c52e83e3SKudo Chien t.it( 702c52e83e3SKudo Chien 'should return `could not prepare ...` error when having write statements in readOnly transaction', 703c52e83e3SKudo Chien async () => { 704c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 705c52e83e3SKudo Chien 706c52e83e3SKudo Chien // create table in readOnly transaction 707c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 708c4573fffSKudo Chien let error: Error | null = null; 709c4573fffSKudo Chien try { 710c4573fffSKudo Chien await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []); 711c4573fffSKudo Chien } catch (e: unknown) { 712c4573fffSKudo Chien if (e instanceof Error) { 713c4573fffSKudo Chien error = e; 714c4573fffSKudo Chien } 715c4573fffSKudo Chien } 716c4573fffSKudo Chien t.expect(error).toBeDefined(); 717c4573fffSKudo Chien t.expect(error.message).toContain('could not prepare '); 718c52e83e3SKudo Chien }, true); 719c52e83e3SKudo Chien } 720c52e83e3SKudo Chien ); 721c52e83e3SKudo Chien 722c52e83e3SKudo Chien t.it('should rollback transaction when exception happens inside a transaction', async () => { 723c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 724c52e83e3SKudo Chien 725c52e83e3SKudo Chien // create table 726c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 727c52e83e3SKudo Chien await tx.executeSqlAsync('DROP TABLE IF EXISTS Users;', []); 728c52e83e3SKudo Chien await tx.executeSqlAsync( 729c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 730c52e83e3SKudo Chien [] 731c52e83e3SKudo Chien ); 732c52e83e3SKudo Chien }); 733c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 734c52e83e3SKudo Chien await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['aaa']); 735c52e83e3SKudo Chien }); 736c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 737c52e83e3SKudo Chien const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users'); 738c52e83e3SKudo Chien const recordCount = result.rows[0]['COUNT(*)']; 739c52e83e3SKudo Chien t.expect(recordCount).toEqual(1); 740c52e83e3SKudo Chien }, true); 741c52e83e3SKudo Chien 742c52e83e3SKudo Chien await throws(() => 743c52e83e3SKudo Chien db.transactionAsync(async (tx) => { 744c52e83e3SKudo Chien await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['bbb']); 745c52e83e3SKudo Chien await tx.executeSqlAsync('INSERT INTO Users (name) VALUES (?)', ['ccc']); 746c52e83e3SKudo Chien // exeuting invalid sql statement will throw an exception 7474e419217SAlan Hughes await tx.executeSqlAsync(null); 748c52e83e3SKudo Chien }) 749c52e83e3SKudo Chien ); 750c52e83e3SKudo Chien 751c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 752c52e83e3SKudo Chien const result = await tx.executeSqlAsync('SELECT COUNT(*) FROM Users'); 753c52e83e3SKudo Chien const recordCount = result.rows[0]['COUNT(*)']; 754c52e83e3SKudo Chien t.expect(recordCount).toEqual(1); 755c52e83e3SKudo Chien }, true); 756c52e83e3SKudo Chien }); 757c52e83e3SKudo Chien 758c52e83e3SKudo Chien t.it('should support async PRAGMA statements', async () => { 759c52e83e3SKudo Chien const db = SQLite.openDatabase('test.db'); 760c52e83e3SKudo Chien await db.transactionAsync(async (tx) => { 761c52e83e3SKudo Chien await tx.executeSqlAsync('DROP TABLE IF EXISTS SomeTable;', []); 762c52e83e3SKudo Chien await tx.executeSqlAsync( 763c52e83e3SKudo Chien 'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));', 764c52e83e3SKudo Chien [] 765c52e83e3SKudo Chien ); 766c52e83e3SKudo Chien // a result-returning pragma 767c52e83e3SKudo Chien let result = await tx.executeSqlAsync('PRAGMA table_info(SomeTable);', []); 768c52e83e3SKudo Chien t.expect(result.rows.length).toEqual(2); 769c52e83e3SKudo Chien t.expect(result.rows[0].name).toEqual('id'); 770c52e83e3SKudo Chien t.expect(result.rows[1].name).toEqual('name'); 771c52e83e3SKudo Chien // a no-result pragma 772c52e83e3SKudo Chien await tx.executeSqlAsync('PRAGMA case_sensitive_like = true;', []); 773c52e83e3SKudo Chien // a setter/getter pragma 774c52e83e3SKudo Chien await tx.executeSqlAsync('PRAGMA user_version = 123;', []); 775c52e83e3SKudo Chien result = await tx.executeSqlAsync('PRAGMA user_version;', []); 776c52e83e3SKudo Chien t.expect(result.rows.length).toEqual(1); 777c52e83e3SKudo Chien t.expect(result.rows[0].user_version).toEqual(123); 778c52e83e3SKudo Chien }); 779c52e83e3SKudo Chien }); 780c52e83e3SKudo Chien }); // t.describe('SQLiteAsync') 781c52e83e3SKudo Chien } 782c52e83e3SKudo Chien} 783