Skip to content

Commit

Permalink
Move expiration column to schema addition.
Browse files Browse the repository at this point in the history
  • Loading branch information
callumbirks committed Oct 24, 2024
1 parent c92e3da commit 26f5469
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 47 deletions.
5 changes: 0 additions & 5 deletions LiteCore/Query/SQLiteQuery.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,6 @@ namespace litecore {
error::_throw(error::NoSuchIndex, "'match' test requires a full-text index");
}

// If expiration is queried, ensure the table(s) have the expiration column:
if ( qp.usesExpiration() ) {
for ( auto ks : _keyStores ) ks->addExpiration();
}

LogTo(SQL, "Compiled {Query#%u}: %s", getObjectRef(), sql.c_str());
_statement = dataFile.compile(sql.c_str());

Expand Down
5 changes: 0 additions & 5 deletions LiteCore/Storage/BothKeyStore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ namespace litecore {

bool mayHaveExpiration() override { return _liveStore->mayHaveExpiration() || _deadStore->mayHaveExpiration(); }

void addExpiration() override {
_liveStore->addExpiration();
_deadStore->addExpiration();
}

bool setExpiration(slice key, expiration_t exp) override {
return _liveStore->setExpiration(key, exp) || _deadStore->setExpiration(key, exp);
}
Expand Down
4 changes: 1 addition & 3 deletions LiteCore/Storage/KeyStore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ namespace litecore {
/** Does this KeyStore potentially have records that expire? (May return false positives.) */
virtual bool mayHaveExpiration() = 0;

virtual void addExpiration() = 0;

/** Sets a record's expiration time. Zero means 'never'.
@return true if the time was set, false if no record with that key exists. */
virtual bool setExpiration(slice key, expiration_t) = 0;
Expand Down Expand Up @@ -210,7 +208,7 @@ namespace litecore {
// public for complicated reasons; clients should never call it
virtual ~KeyStore() = default;

KeyStore(const KeyStore&) = delete; // not copyable
KeyStore(const KeyStore&) = delete; // not copyable
KeyStore& operator=(const KeyStore&) = delete;

protected:
Expand Down
16 changes: 13 additions & 3 deletions LiteCore/Storage/SQLiteDataFile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,19 @@ namespace litecore {
error::_throw(error::CantUpgradeDatabase);
}

(void)upgradeSchema(SchemaVersion::WithExpirationColumn, "Adding `expiration` column", [&] {
// Add the 'expiration' column to every KeyStore:
for ( string& name : allKeyStoreNames() ) {
if ( name.find("::") == string::npos ) {
// Only update data tables, not FTS index tables
_exec(format(
"ALTER TABLE \"kv_%s\" ADD COLUMN expiration INTEGER; "
"CREATE INDEX \"kv_%s_expiration\" ON \"kv_%s\" (expiration) WHERE expiration not null",
name.c_str(), name.c_str(), name.c_str()));
}
}
});

(void)upgradeSchema(SchemaVersion::WithIndexesLastSeq, "Adding indexes.lastSeq column", [&] {
string sql;
if ( getSchema("indexes", "table", "indexes", sql) ) {
Expand Down Expand Up @@ -602,9 +615,6 @@ namespace litecore {
// Wrap the store in a BothKeyStore that manages it and the deleted store:
auto deletedStore = new SQLiteKeyStore(*this, kDeletedKeyStorePrefix + name, options);

keyStore->addExpiration();
deletedStore->addExpiration();

// Create a SQLite view of a union of both stores, for use in queries:
#define COLUMNS "key,sequence,flags,version,body,extra,expiration"
// Invarient: keyStore->tablaName() == kv_<tableName>
Expand Down
7 changes: 4 additions & 3 deletions LiteCore/Storage/SQLiteDataFile.hh
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,10 @@ namespace litecore {

WithNewDocs = 400, // New document/revision storage (CBL 3.0)

WithDeletedTable = 500, // Added 'deleted' KeyStore for deleted docs (CBL 3.0?)
WithIndexesLastSeq = 501, // Added 'lastSeq' column to 'indexes' table (CBL 3.2)
MaxReadable = 599, // Cannot open versions newer than this
WithExpirationColumn = 490, // Added 'expiration' column to KeyStore
WithDeletedTable = 500, // Added 'deleted' KeyStore for deleted docs (CBL 3.0?)
WithIndexesLastSeq = 501, // Added 'lastSeq' column to 'indexes' table (CBL 3.2)
MaxReadable = 599, // Cannot open versions newer than this

Current = WithDeletedTable
};
Expand Down
35 changes: 11 additions & 24 deletions LiteCore/Storage/SQLiteKeyStore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,16 @@ namespace litecore {
// more efficient in SQLite to keep large columns at the end of a row.
// Create the sequence and flags columns regardless of options, otherwise it's too
// complicated to customize all the SQL queries to conditionally use them...
db().execWithLock(subst("CREATE TABLE IF NOT EXISTS kv_@ ("
" key TEXT PRIMARY KEY,"
" sequence INTEGER,"
" flags INTEGER DEFAULT 0,"
" version BLOB,"
" body BLOB,"
" extra BLOB)"));
db().execWithLock(
subst("CREATE TABLE IF NOT EXISTS kv_@ ("
" key TEXT PRIMARY KEY,"
" sequence INTEGER,"
" flags INTEGER DEFAULT 0,"
" version BLOB,"
" body BLOB,"
" expiration INTEGER,"
" extra BLOB);"
"CREATE INDEX IF NOT EXISTS \"kv_@_expiration\" ON kv_@ (expiration) WHERE expiration not null"));
_uncommitedTable = db().inTransaction();
}

Expand Down Expand Up @@ -179,12 +182,10 @@ namespace litecore {
_purgeCountValid = false;

if ( !commit ) {
if ( _uncommittedExpirationColumn ) _hasExpirationColumn = false;
if ( _uncommitedTable ) { close(); }
}

_uncommittedExpirationColumn = false;
_uncommitedTable = false;
_uncommitedTable = false;
}

/*static*/ slice SQLiteKeyStore::columnAsSlice(const SQLite::Column& col) {
Expand Down Expand Up @@ -520,22 +521,8 @@ namespace litecore {
return _hasExpirationColumn;
}

// Adds the 'expiration' column to the table.
void SQLiteKeyStore::addExpiration() {
if ( _hasExpirationColumn ) return;
db().withFileLock([=]() {
if ( mayHaveExpiration() ) return;
db()._logVerbose("Adding the `expiration` column & index to kv_%s", name().c_str());
db().exec(subst("ALTER TABLE kv_@ ADD COLUMN expiration INTEGER; "
"CREATE INDEX \"kv_@_expiration\" ON kv_@ (expiration) WHERE expiration not null"));
_uncommittedExpirationColumn = true;
});
_hasExpirationColumn = true;
}

bool SQLiteKeyStore::setExpiration(slice key, expiration_t expTime) {
Assert(expTime >= expiration_t(0), "Invalid (negative) expiration time");
addExpiration();
auto& stmt = compileCached("UPDATE kv_@ SET expiration=? WHERE key=?");
UsingStatement u(stmt);
if ( expTime > expiration_t::None ) stmt.bind(1, (long long)expTime);
Expand Down
4 changes: 0 additions & 4 deletions LiteCore/Storage/SQLiteKeyStore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@ namespace litecore {
void createConflictsIndex();
void createBlobsIndex();

/// Adds the `expiration` column to the table. Called only by SQLiteQuery.
void addExpiration() override;

void shareSequencesWith(KeyStore&) override;

protected:
Expand Down Expand Up @@ -161,7 +158,6 @@ namespace litecore {
mutable std::optional<sequence_t> _lastSequence;
mutable std::atomic<uint64_t> _purgeCount{0};
bool _hasExpirationColumn{false};
bool _uncommittedExpirationColumn{false};
bool _uncommitedTable{false};
SQLiteKeyStore* _sequencesOwner{nullptr};
};
Expand Down

0 comments on commit 26f5469

Please sign in to comment.