UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

284 lines (279 loc) 11.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.RxStorageInstanceSQLite = void 0; exports.createSQLiteTrialStorageInstance = createSQLiteTrialStorageInstance; var _readOnlyError2 = _interopRequireDefault(require("@babel/runtime/helpers/readOnlyError")); var _index = require("../../index.js"); var _rxjs = require("rxjs"); var _sqliteHelpers = require("./sqlite-helpers.js"); var _rxQueryHelper = require("../../rx-query-helper.js"); var _rxError = require("../../rx-error.js"); var instanceId = 0; var RxStorageInstanceSQLite = exports.RxStorageInstanceSQLite = /*#__PURE__*/function () { function RxStorageInstanceSQLite(storage, databaseName, collectionName, schema, internals, options, settings, tableName, devMode) { this.changes$ = new _rxjs.Subject(); this.instanceId = instanceId++; this.openWriteCount$ = new _rxjs.BehaviorSubject(0); this.opCount = 0; this.storage = storage; this.databaseName = databaseName; this.collectionName = collectionName; this.schema = schema; this.internals = internals; this.options = options; this.settings = settings; this.tableName = tableName; this.devMode = devMode; this.sqliteBasics = storage.settings.sqliteBasics; this.primaryPath = (0, _index.getPrimaryFieldOfPrimaryKey)(this.schema.primaryKey); } var _proto = RxStorageInstanceSQLite.prototype; _proto.run = function run(db, queryWithParams) { if (this.devMode) { (0, _sqliteHelpers.ensureParamsCountIsCorrect)(queryWithParams); } return this.sqliteBasics.run(db, queryWithParams); }; _proto.all = function all(db, queryWithParams) { if (this.devMode) { (0, _sqliteHelpers.ensureParamsCountIsCorrect)(queryWithParams); } this.opCount = this.opCount + 1; if (this.opCount > 110) { throw (0, _rxError.newRxError)('SQL3'); } return this.sqliteBasics.all(db, queryWithParams); } /** * @link https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-insertions-971aff98eef2 */; _proto.bulkWrite = async function bulkWrite(documentWrites, context) { this.openWriteCount$.next(this.openWriteCount$.getValue() + 1); var database = await this.internals.databasePromise; var ret = { error: [] }; var writePromises = []; var categorized = {}; await (0, _sqliteHelpers.sqliteTransaction)(database, this.sqliteBasics, async () => { if (this.closed) { this.openWriteCount$.next(this.openWriteCount$.getValue() - 1); throw new Error('SQLite.bulkWrite(' + context + ') already closed ' + this.tableName + ' context: ' + context); } var result = await this.all(database, { query: "SELECT data FROM \"" + this.tableName + "\"", params: [], context: { method: 'bulkWrite', data: documentWrites } }); var docsInDb = new Map(); result.forEach(docSQLResult => { var doc = JSON.parse((0, _sqliteHelpers.getDataFromResultRow)(docSQLResult)); var id = doc[this.primaryPath]; docsInDb.set(id, doc); }); categorized = (0, _index.categorizeBulkWriteRows)(this, this.primaryPath, docsInDb, documentWrites, context); ret.error = categorized.errors; if (result.length + categorized.bulkInsertDocs.length > 300) { throw (0, _rxError.newRxError)('SQL2'); } categorized.bulkInsertDocs.forEach(row => { var insertQuery = (0, _sqliteHelpers.getSQLiteInsertSQL)(this.tableName, this.primaryPath, row.document); writePromises.push(this.all(database, { query: insertQuery.query, params: insertQuery.params, context: { method: 'bulkWrite', data: categorized } })); }); categorized.bulkUpdateDocs.forEach(row => { var updateQuery = (0, _sqliteHelpers.getSQLiteUpdateSQL)(this.tableName, this.primaryPath, row); writePromises.push(this.run(database, updateQuery)); }); await Promise.all(writePromises); // close transaction if (this.closed) { this.openWriteCount$.next(this.openWriteCount$.getValue() - 1); return 'ROLLBACK'; } else { this.openWriteCount$.next(this.openWriteCount$.getValue() - 1); return 'COMMIT'; } }, { databaseName: this.databaseName, collectionName: this.collectionName }); if (categorized && categorized.eventBulk.events.length > 0) { var lastState = (0, _index.ensureNotFalsy)(categorized.newestRow).document; categorized.eventBulk.checkpoint = { id: lastState[this.primaryPath], lwt: lastState._meta.lwt }; this.changes$.next(categorized.eventBulk); } return ret; }; _proto.query = async function query(originalPreparedQuery) { var database = await this.internals.databasePromise; var result = []; var query = originalPreparedQuery.query; var skip = query.skip ? query.skip : 0; var limit = query.limit ? query.limit : Infinity; var skipPlusLimit = skip + limit; var queryMatcher = (0, _index.getQueryMatcher)(this.schema, query); var subResult = await this.all(database, { query: 'SELECT data FROM "' + this.tableName + '"', params: [], context: { method: 'query', data: originalPreparedQuery } }); subResult.forEach(row => { var docData = JSON.parse((0, _sqliteHelpers.getDataFromResultRow)(row)); if (queryMatcher(docData)) { result.push(docData); } }); var sortComparator = (0, _rxQueryHelper.getSortComparator)(this.schema, query); result = result.sort(sortComparator); result = result.slice(skip, skipPlusLimit); return { documents: result }; }; _proto.count = async function count(originalPreparedQuery) { var results = await this.query(originalPreparedQuery); return { count: results.documents.length, mode: 'fast' }; }; _proto.findDocumentsById = async function findDocumentsById(ids, withDeleted) { var database = await this.internals.databasePromise; if (this.closed) { throw new Error('SQLite.findDocumentsById() already closed ' + this.tableName + ' context: ' + context); } var result = await this.all(database, { query: "SELECT data FROM \"" + this.tableName + "\"", params: [], context: { method: 'findDocumentsById', data: ids } }); var ret = []; for (var i = 0; i < result.length; ++i) { var resultRow = result[i]; var doc = JSON.parse((0, _sqliteHelpers.getDataFromResultRow)(resultRow)); if (ids.includes(doc[this.primaryPath]) && (withDeleted || !doc._deleted)) { ret.push(doc); } } return ret; }; _proto.changeStream = function changeStream() { return this.changes$.asObservable(); }; _proto.cleanup = async function cleanup(minimumDeletedTime) { await (0, _index.promiseWait)(0); await (0, _index.promiseWait)(0); var database = await this.internals.databasePromise; /** * Purge deleted documents */ var minTimestamp = new Date().getTime() - minimumDeletedTime; await this.all(database, { query: "\n DELETE FROM\n \"" + this.tableName + "\"\n WHERE\n deleted = 1\n AND\n lastWriteTime < ?\n ", params: [minTimestamp], context: { method: 'cleanup', data: minimumDeletedTime } }); return true; }; _proto.getAttachmentData = async function getAttachmentData(_documentId, _attachmentId) { throw (0, _rxError.newRxError)('SQL1'); }; _proto.remove = async function remove() { if (this.closed) { throw new Error('closed already'); } var database = await this.internals.databasePromise; var promises = [this.run(database, { query: "DROP TABLE IF EXISTS \"" + this.tableName + "\"", params: [], context: { method: 'remove', data: this.tableName } })]; await Promise.all(promises); return this.close(); }; _proto.close = async function close() { var queue = _sqliteHelpers.TX_QUEUE_BY_DATABASE.get(await this.internals.databasePromise); if (queue) { await queue; } if (this.closed) { return this.closed; } this.closed = (async () => { await (0, _rxjs.firstValueFrom)(this.openWriteCount$.pipe((0, _rxjs.filter)(v => v === 0))); var database = await this.internals.databasePromise; /** * First get a transaction * to ensure currently running operations * are finished */ await (0, _sqliteHelpers.sqliteTransaction)(database, this.sqliteBasics, () => { return Promise.resolve('COMMIT'); }).catch(() => {}); this.changes$.complete(); await (0, _sqliteHelpers.closeDatabaseConnection)(this.databaseName, this.storage.settings.sqliteBasics); })(); return this.closed; }; return RxStorageInstanceSQLite; }(); async function createSQLiteTrialStorageInstance(storage, params, settings) { var sqliteBasics = settings.sqliteBasics; var tableName = params.collectionName + '-' + params.schema.version; if (params.schema.attachments) { throw (0, _rxError.newRxError)('SQL1'); } var internals = {}; var useDatabaseName = (settings.databaseNamePrefix ? settings.databaseNamePrefix : '') + '_trial_' + params.databaseName; internals.databasePromise = (0, _sqliteHelpers.getDatabaseConnection)(storage.settings.sqliteBasics, useDatabaseName).then(async database => { await (0, _sqliteHelpers.sqliteTransaction)(database, sqliteBasics, async () => { var tableQuery = "\n CREATE TABLE IF NOT EXISTS \"" + tableName + "\"(\n id TEXT NOT NULL PRIMARY KEY UNIQUE,\n revision TEXT,\n deleted BOOLEAN NOT NULL CHECK (deleted IN (0, 1)),\n lastWriteTime INTEGER NOT NULL,\n data json\n );\n "; await sqliteBasics.run(database, { query: tableQuery, params: [], context: { method: 'createSQLiteStorageInstance create tables', data: params.databaseName } }); return 'COMMIT'; }, { indexCreation: false, databaseName: params.databaseName, collectionName: params.collectionName }); return database; }); var instance = new RxStorageInstanceSQLite(storage, params.databaseName, params.collectionName, params.schema, internals, params.options, settings, tableName, params.devMode); await (0, _index.addRxStorageMultiInstanceSupport)(_sqliteHelpers.RX_STORAGE_NAME_SQLITE, params, instance); return instance; } //# sourceMappingURL=sqlite-storage-instance.js.map