Skip to content
File

Blob: src/workerd/util/sqlite-metadata.h

cpp82 lines
1// Copyright (c) 2024 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6 
7#include "sqlite.h"
8 
9namespace workerd {
10 
11// Class which implements a simple metadata kv storage and cache on top of SQLite. Currently used
12// to store:
13//
14// * Durable Object alarm times (hardcoded as key = 1).
15//
16// * A local development bookmark used to simulate the getCurrentBookmark API used by D1 (hardcoded
17// as key = 2). The local development bookmark is not used in production.
18//
19// The table is named `_cf_METADATA`. The naming is designed so that if the application is allowed to
20// perform direct SQL queries, we can block it from accessing any table prefixed with `_cf_`.
21class SqliteMetadata final: private SqliteDatabase::ResetListener {
22 public:
23 explicit SqliteMetadata(SqliteDatabase& db);
24 
25 // Return currently set alarm time, or none.
26 kj::Maybe<kj::Date> getAlarm();
27 
28 // Sets current alarm time, or none. Returns true if the value changed, false if it was already
29 // set to the same value.
30 bool setAlarm(kj::Maybe<kj::Date> currentTime, bool allowUnconfirmed);
31 
32 // Return the current local development bookmark, or none if no bookmark has been set.
33 kj::Maybe<uint64_t> getLocalDevelopmentBookmark();
34 
35 // Set the current ersatz bookmark.
36 void setLocalDevelopmentBookmark(uint64_t);
37 
38 private:
39 struct Uninitialized {};
40 struct Initialized {
41 SqliteDatabase& db;
42 
43 SqliteDatabase::Statement stmtGetAlarm = db.prepare(R"(
44 SELECT value FROM _cf_METADATA WHERE key = 1
45 )");
46 SqliteDatabase::Statement stmtSetAlarm = db.prepare(R"(
47 INSERT INTO _cf_METADATA VALUES(1, ?)
48 ON CONFLICT DO UPDATE SET value = excluded.value;
49 )");
50 
51 SqliteDatabase::Statement stmtGetLocalDevelopmentBookmark = db.prepare(R"(
52 SELECT value FROM _cf_METADATA WHERE key = 2
53 )");
54 SqliteDatabase::Statement stmtSetLocalDevelopmentBookmark = db.prepare(R"(
55 INSERT INTO _cf_METADATA VALUES(2, ?)
56 ON CONFLICT DO UPDATE SET value = excluded.value;
57 )");
58 
59 Initialized(SqliteDatabase& db): db(db) {}
60 };
61 
62 bool tableCreated;
63 kj::OneOf<Uninitialized, Initialized> dbState = Uninitialized{};
64 
65 struct Cache {
66 kj::Maybe<kj::Date> alarmTime;
67 };
68 kj::Maybe<Cache> cacheState;
69 
70 kj::Maybe<kj::Date> getAlarmUncached();
71 void setAlarmUncached(kj::Maybe<kj::Date> currentTime, bool allowUnconfirmed);
72 
73 Initialized& ensureInitialized(bool allowUnconfirmed);
74 // Make sure the metadata table is created and prepared statements are ready. Not called until the
75 // first write.
76 
77 // ResetListener interface:
78 void beforeSqliteReset() override;
79};
80 
81} // namespace workerd