txn.execute(db, function (err) { /* istanbul ignore if */ if (err) { return callback(err); } db._docCount += docCountDelta; db._updateSeq = newUpdateSeq; levelChanges.notify(name); nextTick(function () { callback(null, results); }); });
api._destroy = function (opts, callback) { websqlChanges.removeAllListeners(api._name); db.transaction(function (tx) { var stores = [DOC_STORE, BY_SEQ_STORE, ATTACH_STORE, META_STORE, LOCAL_STORE, ATTACH_AND_SEQ_STORE]; stores.forEach(function (store) { tx.executeSql('DROP TABLE IF EXISTS ' + store, []); }); }, websqlError(callback), function () { if (hasLocalStorage()) { delete window.localStorage['_pouch__websqldb_' + api._name]; delete window.localStorage[api._name]; } callback(null, {'ok': true}); }); };
api._destroy = function (opts, callback) { idbChanges.removeAllListeners(dbName); //Close open request for "dbName" database to fix ie delay. var openReq = openReqList.get(dbName); if (openReq && openReq.result) { openReq.result.close(); cachedDBs.delete(dbName); } var req = indexedDB.deleteDatabase(dbName); req.onsuccess = function () { //Remove open request from the list. openReqList.delete(dbName); if (hasLocalStorage() && (dbName in localStorage)) { delete localStorage[dbName]; } callback(null, { 'ok': true }); }; req.onerror = idbError(callback); };
api._destroy = function (opts, callback) { var dbStore; var leveldownName = functionName(leveldown); /* istanbul ignore else */ if (dbStores.has(leveldownName)) { dbStore = dbStores.get(leveldownName); } else { return callDestroy(name, callback); } /* istanbul ignore else */ if (dbStore.has(name)) { levelChanges.removeAllListeners(name); dbStore.get(name).close(function () { dbStore.delete(name); callDestroy(name, callback); }); } else { callDestroy(name, callback); } };
cancel: function () { websqlChanges.removeListener(api._name, id); }
api._changes = function (opts) { opts = clone(opts); if (opts.continuous) { var id = api._name + ':' + uuid(); websqlChanges.addListener(api._name, id, api, opts); websqlChanges.notify(api._name); return { cancel: function () { websqlChanges.removeListener(api._name, id); } }; } var descending = opts.descending; // Ignore the `since` parameter when `descending` is true opts.since = opts.since && !descending ? opts.since : 0; var limit = 'limit' in opts ? opts.limit : -1; if (limit === 0) { limit = 1; // per CouchDB _changes spec } var returnDocs; if ('return_docs' in opts) { returnDocs = opts.return_docs; } else if ('returnDocs' in opts) { // TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release returnDocs = opts.returnDocs; } else { returnDocs = true; } var results = []; var numResults = 0; function fetchChanges() { var selectStmt = DOC_STORE + '.json AS metadata, ' + DOC_STORE + '.max_seq AS maxSeq, ' + BY_SEQ_STORE + '.json AS winningDoc, ' + BY_SEQ_STORE + '.rev AS winningRev '; var from = DOC_STORE + ' JOIN ' + BY_SEQ_STORE; var joiner = DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id' + ' AND ' + DOC_STORE + '.winningseq=' + BY_SEQ_STORE + '.seq'; var criteria = ['maxSeq > ?']; var sqlArgs = [opts.since]; if (opts.doc_ids) { criteria.push(DOC_STORE + '.id IN ' + qMarks(opts.doc_ids.length)); sqlArgs = sqlArgs.concat(opts.doc_ids); } var orderBy = 'maxSeq ' + (descending ? 'DESC' : 'ASC'); var sql = select(selectStmt, from, joiner, criteria, orderBy); var filter = filterChange(opts); if (!opts.view && !opts.filter) { // we can just limit in the query sql += ' LIMIT ' + limit; } var lastSeq = opts.since || 0; db.readTransaction(function (tx) { tx.executeSql(sql, sqlArgs, function (tx, result) { function reportChange(change) { return function () { opts.onChange(change); }; } for (var i = 0, l = result.rows.length; i < l; i++) { var item = result.rows.item(i); var metadata = safeJsonParse(item.metadata); lastSeq = item.maxSeq; var doc = unstringifyDoc(item.winningDoc, metadata.id, item.winningRev); var change = opts.processChange(doc, metadata, opts); change.seq = item.maxSeq; var filtered = filter(change); if (typeof filtered === 'object') { return opts.complete(filtered); } if (filtered) { numResults++; if (returnDocs) { results.push(change); } // process the attachment immediately // for the benefit of live listeners if (opts.attachments && opts.include_docs) { fetchAttachmentsIfNecessary(doc, opts, api, tx, reportChange(change)); } else { reportChange(change)(); } } if (numResults === limit) { break; } } }); }, websqlError(opts.complete), function () { if (!opts.continuous) { opts.complete(null, { results: results, last_seq: lastSeq }); } }); } fetchChanges(); };
cancel: function () { idbChanges.removeListener(dbName, id); }
api._changes = function (opts) { opts = clone(opts); if (opts.continuous) { var id = dbName + ':' + uuid(); idbChanges.addListener(dbName, id, api, opts); idbChanges.notify(dbName); return { cancel: function () { idbChanges.removeListener(dbName, id); } }; } var docIds = opts.doc_ids && new Set(opts.doc_ids); opts.since = opts.since || 0; var lastSeq = opts.since; var limit = 'limit' in opts ? opts.limit : -1; if (limit === 0) { limit = 1; // per CouchDB _changes spec } var returnDocs; if ('return_docs' in opts) { returnDocs = opts.return_docs; } else if ('returnDocs' in opts) { // TODO: Remove 'returnDocs' in favor of 'return_docs' in a future release returnDocs = opts.returnDocs; } else { returnDocs = true; } var results = []; var numResults = 0; var filter = filterChange(opts); var docIdsToMetadata = new Map(); var txn; var bySeqStore; var docStore; var docIdRevIndex; function onGetCursor(cursor) { var doc = decodeDoc(cursor.value); var seq = cursor.key; if (docIds && !docIds.has(doc._id)) { return cursor.continue(); } var metadata; function onGetMetadata() { if (metadata.seq !== seq) { // some other seq is later return cursor.continue(); } lastSeq = seq; if (metadata.winningRev === doc._rev) { return onGetWinningDoc(doc); } fetchWinningDoc(); } function fetchWinningDoc() { var docIdRev = doc._id + '::' + metadata.winningRev; var req = docIdRevIndex.get(docIdRev); req.onsuccess = function (e) { onGetWinningDoc(decodeDoc(e.target.result)); }; } function onGetWinningDoc(winningDoc) { var change = opts.processChange(winningDoc, metadata, opts); change.seq = metadata.seq; var filtered = filter(change); if (typeof filtered === 'object') { return opts.complete(filtered); } if (filtered) { numResults++; if (returnDocs) { results.push(change); } // process the attachment immediately // for the benefit of live listeners if (opts.attachments && opts.include_docs) { fetchAttachmentsIfNecessary(winningDoc, opts, txn, function () { postProcessAttachments([change], opts.binary).then(function () { opts.onChange(change); }); }); } else { opts.onChange(change); } } if (numResults !== limit) { cursor.continue(); } } metadata = docIdsToMetadata.get(doc._id); if (metadata) { // cached return onGetMetadata(); } // metadata not cached, have to go fetch it docStore.get(doc._id).onsuccess = function (event) { metadata = decodeMetadata(event.target.result); docIdsToMetadata.set(doc._id, metadata); onGetMetadata(); }; } function onsuccess(event) { var cursor = event.target.result; if (!cursor) { return; } onGetCursor(cursor); } function fetchChanges() { var objectStores = [DOC_STORE, BY_SEQ_STORE]; if (opts.attachments) { objectStores.push(ATTACH_STORE); } var txnResult = openTransactionSafely(idb, objectStores, 'readonly'); if (txnResult.error) { return opts.complete(txnResult.error); } txn = txnResult.txn; txn.onabort = idbError(opts.complete); txn.oncomplete = onTxnComplete; bySeqStore = txn.objectStore(BY_SEQ_STORE); docStore = txn.objectStore(DOC_STORE); docIdRevIndex = bySeqStore.index('_doc_id_rev'); var req; if (opts.descending) { req = bySeqStore.openCursor(null, 'prev'); } else { req = bySeqStore.openCursor(IDBKeyRange.lowerBound(opts.since, true)); } req.onsuccess = onsuccess; } fetchChanges(); function onTxnComplete() { function finish() { opts.complete(null, { results: results, last_seq: lastSeq }); } if (!opts.continuous && opts.attachments) { // cannot guarantee that postProcessing was already done, // so do it again postProcessAttachments(results).then(finish); } else { finish(); } } };
api._changes = function (opts) { opts = clone(opts); if (opts.continuous) { var id = name + ':' + uuid(); levelChanges.addListener(name, id, api, opts); levelChanges.notify(name); return { cancel: function () { levelChanges.removeListener(name, id); } }; } var descending = opts.descending; var results = []; var lastSeq = opts.since || 0; var called = 0; var streamOpts = { reverse: descending }; var limit; if ('limit' in opts && opts.limit > 0) { limit = opts.limit; } if (!streamOpts.reverse) { streamOpts.start = formatSeq(opts.since || 0); } var docIds = opts.doc_ids && new Set(opts.doc_ids); var filter = filterChange(opts); var docIdsToMetadata = new Map(); function complete() { opts.done = true; if (opts.return_docs && opts.limit) { /* istanbul ignore if */ if (opts.limit < results.length) { results.length = opts.limit; } } changeStream.unpipe(throughStream); changeStream.destroy(); if (!opts.continuous && !opts.cancelled) { if (opts.include_docs && opts.attachments && opts.return_docs) { fetchAttachments(results, stores, opts).then(function () { opts.complete(null, {results: results, last_seq: lastSeq}); }); } else { opts.complete(null, {results: results, last_seq: lastSeq}); } } } var changeStream = stores.bySeqStore.readStream(streamOpts); var throughStream = through(function (data, _, next) { if (limit && called >= limit) { complete(); return next(); } if (opts.cancelled || opts.done) { return next(); } var seq = parseSeq(data.key); var doc = data.value; if (seq === opts.since && !descending) { // couchdb ignores `since` if descending=true return next(); } if (docIds && !docIds.has(doc._id)) { return next(); } var metadata; function onGetMetadata(metadata) { var winningRev = getWinningRev(metadata); function onGetWinningDoc(winningDoc) { var change = opts.processChange(winningDoc, metadata, opts); change.seq = metadata.seq; var filtered = filter(change); if (typeof filtered === 'object') { return opts.complete(filtered); } if (filtered) { called++; if (opts.attachments && opts.include_docs) { // fetch attachment immediately for the benefit // of live listeners fetchAttachments([change], stores, opts).then(function () { opts.onChange(change); }); } else { opts.onChange(change); } if (opts.return_docs) { results.push(change); } } next(); } if (metadata.seq !== seq) { // some other seq is later return next(); } lastSeq = seq; if (winningRev === doc._rev) { return onGetWinningDoc(doc); } // fetch the winner var winningSeq = metadata.rev_map[winningRev]; stores.bySeqStore.get(formatSeq(winningSeq), function (err, doc) { onGetWinningDoc(doc); }); } metadata = docIdsToMetadata.get(doc._id); if (metadata) { // cached return onGetMetadata(metadata); } // metadata not cached, have to go fetch it stores.docStore.get(doc._id, function (err, metadata) { /* istanbul ignore if */ if (opts.cancelled || opts.done || db.isClosed() || isLocalId(metadata.id)) { return next(); } docIdsToMetadata.set(doc._id, metadata); onGetMetadata(metadata); }); }, function (next) { if (opts.cancelled) { return next(); } if (opts.return_docs && opts.limit) { /* istanbul ignore if */ if (opts.limit < results.length) { results.length = opts.limit; } } next(); }).on('unpipe', function () { throughStream.end(); complete(); }); changeStream.pipe(throughStream); return { cancel: function () { opts.cancelled = true; complete(); } }; };
cancel: function () { levelChanges.removeListener(name, id); }