| 
									
										
										
										
											2017-10-21 21:10:33 -04:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 22:17:48 -04:00
										 |  |  | const log = require('./log'); | 
					
						
							| 
									
										
										
										
											2018-03-28 23:41:22 -04:00
										 |  |  | const cls = require('./cls'); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-02 21:25:20 -04:00
										 |  |  | let dbConnection; | 
					
						
							| 
									
										
										
										
											2017-12-10 12:56:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-02 21:25:20 -04:00
										 |  |  | function setDbConnection(connection) { | 
					
						
							|  |  |  |     dbConnection = connection; | 
					
						
							| 
									
										
										
										
											2017-12-03 22:29:23 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-14 22:15:55 +02:00
										 |  |  | [`exit`, `SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach(eventType => { | 
					
						
							|  |  |  |     process.on(eventType, () => { | 
					
						
							|  |  |  |         if (dbConnection) { | 
					
						
							|  |  |  |             // closing connection is especially important to fold -wal file into the main DB file
 | 
					
						
							|  |  |  |             // (see https://sqlite.org/tempfiles.html for details)
 | 
					
						
							|  |  |  |             dbConnection.close(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-25 22:06:17 +01:00
										 |  |  | async function insert(tableName, rec, replace = false) { | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  |     const keys = Object.keys(rec); | 
					
						
							|  |  |  |     if (keys.length === 0) { | 
					
						
							| 
									
										
										
										
											2019-03-25 22:06:17 +01:00
										 |  |  |         log.error("Can't insert empty object into table " + tableName); | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const columns = keys.join(", "); | 
					
						
							|  |  |  |     const questionMarks = keys.map(p => "?").join(", "); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-25 22:06:17 +01:00
										 |  |  |     const query = "INSERT " + (replace ? "OR REPLACE" : "") + " INTO " + tableName + "(" + columns + ") VALUES (" + questionMarks + ")"; | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  |     const res = await execute(query, Object.values(rec)); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return res.lastID; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-25 22:06:17 +01:00
										 |  |  | async function replace(tableName, rec) { | 
					
						
							|  |  |  |     return await insert(tableName, rec, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function upsert(tableName, primaryKey, rec) { | 
					
						
							|  |  |  |     const keys = Object.keys(rec); | 
					
						
							|  |  |  |     if (keys.length === 0) { | 
					
						
							|  |  |  |         log.error("Can't upsert empty object into table " + tableName); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const columns = keys.join(", "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const questionMarks = keys.map(p => ":" + i++).join(", "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const updateMarks = keys.map(key => `${key} = :${i++}`).join(", "); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const query = `INSERT INTO ${tableName} (${columns}) VALUES (${questionMarks}) 
 | 
					
						
							|  |  |  |                    ON CONFLICT (${primaryKey}) DO UPDATE SET ${updateMarks}`;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await execute(query, Object.values(rec)); | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | async function beginTransaction() { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     return await dbConnection.run("BEGIN"); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | async function commit() { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     return await dbConnection.run("COMMIT"); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | async function rollback() { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     return await dbConnection.run("ROLLBACK"); | 
					
						
							| 
									
										
										
										
											2017-10-25 22:39:21 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  | async function getRow(query, params = []) { | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |     return await wrap(async db => db.get(query, ...params), query); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  | async function getRowOrNull(query, params = []) { | 
					
						
							| 
									
										
										
										
											2018-04-02 21:34:28 -04:00
										 |  |  |     const all = await getRows(query, params); | 
					
						
							| 
									
										
										
										
											2017-10-28 19:55:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  |     return all.length > 0 ? all[0] : null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  | async function getValue(query, params = []) { | 
					
						
							|  |  |  |     const row = await getRowOrNull(query, params); | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!row) { | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return row[Object.keys(row)[0]]; | 
					
						
							| 
									
										
										
										
											2017-10-28 19:55:55 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-30 20:28:10 -04:00
										 |  |  | const PARAM_LIMIT = 900; // actual limit is 999
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // this is to overcome 999 limit of number of query parameters
 | 
					
						
							|  |  |  | async function getManyRows(query, params) { | 
					
						
							|  |  |  |     let results = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (params.length > 0) { | 
					
						
							| 
									
										
										
										
											2019-05-13 20:40:00 +02:00
										 |  |  |         const curParams = params.slice(0, Math.min(params.length, PARAM_LIMIT)); | 
					
						
							| 
									
										
										
										
											2018-05-30 20:28:10 -04:00
										 |  |  |         params = params.slice(curParams.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let i = 1; | 
					
						
							|  |  |  |         const questionMarks = curParams.map(() => "?" + i++).join(","); | 
					
						
							|  |  |  |         const curQuery = query.replace(/\?\?\?/g, questionMarks); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         results = results.concat(await getRows(curQuery, curParams)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return results; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  | async function getRows(query, params = []) { | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |     return await wrap(async db => db.all(query, ...params), query); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 22:55:22 -04:00
										 |  |  | async function getMap(query, params = []) { | 
					
						
							|  |  |  |     const map = {}; | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  |     const results = await getRows(query, params); | 
					
						
							| 
									
										
										
										
											2017-11-02 22:55:22 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (const row of results) { | 
					
						
							|  |  |  |         const keys = Object.keys(row); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         map[row[keys[0]]] = row[keys[1]]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return map; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  | async function getColumn(query, params = []) { | 
					
						
							| 
									
										
										
										
											2017-10-24 23:14:26 -04:00
										 |  |  |     const list = []; | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  |     const result = await getRows(query, params); | 
					
						
							| 
									
										
										
										
											2017-10-24 23:14:26 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-14 22:16:26 -05:00
										 |  |  |     if (result.length === 0) { | 
					
						
							|  |  |  |         return list; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const key = Object.keys(result[0])[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 23:14:26 -04:00
										 |  |  |     for (const row of result) { | 
					
						
							|  |  |  |         list.push(row[key]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return list; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | async function execute(query, params = []) { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     await startTransactionIfNecessary(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |     return await wrap(async db => db.run(query, ...params), query); | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 21:55:08 +02:00
										 |  |  | async function executeNoWrap(query, params = []) { | 
					
						
							|  |  |  |     await dbConnection.run(query, ...params); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 22:09:51 +01:00
										 |  |  | async function executeMany(query, params) { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     await startTransactionIfNecessary(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 22:09:51 +01:00
										 |  |  |     // essentially just alias
 | 
					
						
							|  |  |  |     await getManyRows(query, params); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | async function executeScript(query) { | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     await startTransactionIfNecessary(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |     return await wrap(async db => db.exec(query), query); | 
					
						
							| 
									
										
										
										
											2017-10-15 17:31:49 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  | async function wrap(func, query) { | 
					
						
							| 
									
										
										
										
											2020-05-03 21:27:24 +02:00
										 |  |  |     if (!dbConnection) { | 
					
						
							|  |  |  |         throw new Error("DB connection not initialized yet"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-21 22:11:27 -05:00
										 |  |  |     const thisError = new Error(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |         const startTimestamp = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const result = await func(dbConnection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const milliseconds = Date.now() - startTimestamp; | 
					
						
							| 
									
										
										
										
											2019-12-01 13:29:39 +01:00
										 |  |  |         if (milliseconds >= 300) { | 
					
						
							| 
									
										
										
										
											2019-12-01 12:51:47 +01:00
										 |  |  |             if (query.includes("WITH RECURSIVE")) { | 
					
						
							|  |  |  |                 log.info(`Slow recursive query took ${milliseconds}ms.`); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 log.info(`Slow query took ${milliseconds}ms: ${query}`); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result; | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     catch (e) { | 
					
						
							| 
									
										
										
										
											2017-11-18 18:57:50 -05:00
										 |  |  |         log.error("Error executing query. Inner exception: " + e.stack + thisError.stack); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 22:33:44 -05:00
										 |  |  |         thisError.message = e.stack; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-18 18:57:50 -05:00
										 |  |  |         throw thisError; | 
					
						
							| 
									
										
										
										
											2017-11-01 20:31:44 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  | // true if transaction is active globally.
 | 
					
						
							|  |  |  | // cls.namespace.get('isTransactional') OTOH indicates active transaction in active CLS
 | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  | let transactionActive = false; | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  | // resolves when current transaction ends with either COMMIT or ROLLBACK
 | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | let transactionPromise = null; | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  | let transactionPromiseResolve = null; | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  | async function startTransactionIfNecessary() { | 
					
						
							|  |  |  |     if (!cls.namespace.get('isTransactional') | 
					
						
							|  |  |  |         || cls.namespace.get('isInTransaction')) { | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2018-03-28 23:41:22 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  |     while (transactionActive) { | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  |         await transactionPromise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     await beginTransaction(); | 
					
						
							|  |  |  |     cls.namespace.set('isInTransaction', true); | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  |     transactionActive = true; | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     transactionPromise = new Promise(res => transactionPromiseResolve = res); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function transactional(func) { | 
					
						
							|  |  |  |     // if the CLS is already transactional then the whole transaction is handled by higher level transactional() call
 | 
					
						
							|  |  |  |     if (cls.namespace.get('isTransactional')) { | 
					
						
							|  |  |  |         return await func(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     cls.namespace.set('isTransactional', true); // we will need a transaction if there's a write operation
 | 
					
						
							| 
									
										
										
										
											2018-07-22 19:56:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |     try { | 
					
						
							|  |  |  |         const ret = await func(); | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |         if (cls.namespace.get('isInTransaction')) { | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  |             await commit(); | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 13:40:42 +02:00
										 |  |  |             // note that sync rows sent from this action will be sent again by scheduled periodic ping
 | 
					
						
							| 
									
										
										
										
											2020-01-31 22:32:24 +01:00
										 |  |  |             require('./ws.js').sendPingToAllClients(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  |             transactionActive = false; | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |             cls.namespace.set('isInTransaction', false); | 
					
						
							|  |  |  |             transactionPromiseResolve(); | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-10-31 00:15:49 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     catch (e) { | 
					
						
							|  |  |  |         if (transactionActive) { | 
					
						
							|  |  |  |             await rollback(); | 
					
						
							| 
									
										
										
										
											2017-11-28 17:24:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |             transactionActive = false; | 
					
						
							| 
									
										
										
										
											2018-03-28 23:41:22 -04:00
										 |  |  |             cls.namespace.set('isInTransaction', false); | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |             // resolving since this is just semaphore for allowing another write transaction to proceed
 | 
					
						
							|  |  |  |             transactionPromiseResolve(); | 
					
						
							| 
									
										
										
										
											2018-03-28 23:41:22 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 10:23:36 +02:00
										 |  |  |         throw e; | 
					
						
							| 
									
										
										
										
											2017-11-28 18:33:23 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-29 18:50:28 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  | module.exports = { | 
					
						
							| 
									
										
										
										
											2018-04-02 21:25:20 -04:00
										 |  |  |     setDbConnection, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     insert, | 
					
						
							| 
									
										
										
										
											2017-10-29 22:22:30 -04:00
										 |  |  |     replace, | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  |     getValue, | 
					
						
							|  |  |  |     getRow, | 
					
						
							|  |  |  |     getRowOrNull, | 
					
						
							|  |  |  |     getRows, | 
					
						
							| 
									
										
										
										
											2018-05-30 20:28:10 -04:00
										 |  |  |     getManyRows, | 
					
						
							| 
									
										
										
										
											2017-11-02 22:55:22 -04:00
										 |  |  |     getMap, | 
					
						
							| 
									
										
										
										
											2018-01-29 17:41:59 -05:00
										 |  |  |     getColumn, | 
					
						
							| 
									
										
										
										
											2017-10-14 23:31:44 -04:00
										 |  |  |     execute, | 
					
						
							| 
									
										
										
										
											2020-05-29 21:55:08 +02:00
										 |  |  |     executeNoWrap, | 
					
						
							| 
									
										
										
										
											2019-11-01 22:09:51 +01:00
										 |  |  |     executeMany, | 
					
						
							| 
									
										
										
										
											2017-10-15 17:31:49 -04:00
										 |  |  |     executeScript, | 
					
						
							| 
									
										
										
										
											2019-03-25 22:06:17 +01:00
										 |  |  |     transactional, | 
					
						
							|  |  |  |     upsert | 
					
						
							| 
									
										
										
										
											2020-05-12 13:40:42 +02:00
										 |  |  | }; |