@@ -16,28 +16,34 @@ interface Row {
16
16
timestamp : number ;
17
17
}
18
18
19
+ type UninitializedState = { state : "uninitialized" } ;
20
+ type InitializedState = {
21
+ state : "initialized" ;
22
+ db : Sqlite . Database ;
23
+ getTimestampByBlockNumberStmt : Sqlite . Statement ;
24
+ getBlockNumberByTimestampStmt : Sqlite . Statement ;
25
+ saveBlockStmt : Sqlite . Statement ;
26
+ getBeforeStmt : Sqlite . Statement ;
27
+ getAfterStmt : Sqlite . Statement ;
28
+ } ;
29
+ type State = UninitializedState | InitializedState ;
30
+
19
31
export function createSqliteBlockCache ( opts : Options ) : BlockCache {
20
- let db : Sqlite . Database | null = null ;
32
+ let dbState : State = { state : "uninitialized" } ;
21
33
22
- if ( opts . tableName !== undefined && opts . tableName . match ( / [ ^ a - z A - Z 0 - 9 _ ] / ) ) {
23
- throw new Error (
24
- `Table name ${ opts . tableName } contains invalid characters. Only alphanumeric and underscore characters are allowed.`
25
- ) ;
34
+ if ( opts . tableName !== undefined && / [ ^ a - z A - Z 0 - 9 _ ] / . test ( opts . tableName ) ) {
35
+ throw new Error ( `Table name ${ opts . tableName } has invalid characters.` ) ;
26
36
}
27
37
28
38
const tableName = opts . tableName ?? defaultTableName ;
29
39
30
40
return {
31
- init ( ) : Promise < void > {
32
- if ( db ) {
41
+ async init ( ) : Promise < void > {
42
+ if ( dbState . state === "initialized" ) {
33
43
throw new Error ( "Already initialized" ) ;
34
44
}
35
45
36
- if ( "db" in opts ) {
37
- db = opts . db ;
38
- } else {
39
- db = new Sqlite ( opts . dbPath ) ;
40
- }
46
+ const db = "db" in opts ? opts . db : new Sqlite ( opts . dbPath ) ;
41
47
42
48
db . exec ( "PRAGMA journal_mode = WAL;" ) ;
43
49
@@ -56,85 +62,105 @@ export function createSqliteBlockCache(opts: Options): BlockCache {
56
62
ON ${ tableName } (chainId, timestamp, blockNumber DESC);`
57
63
) ;
58
64
65
+ dbState = {
66
+ state : "initialized" ,
67
+ db,
68
+ getTimestampByBlockNumberStmt : db . prepare (
69
+ `SELECT * FROM ${ tableName } WHERE chainId = ? AND blockNumber = ?`
70
+ ) ,
71
+ getBlockNumberByTimestampStmt : db . prepare (
72
+ `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp = ?`
73
+ ) ,
74
+ saveBlockStmt : db . prepare (
75
+ `INSERT OR REPLACE INTO ${ tableName } (chainId, blockNumber, timestamp) VALUES (?, ?, ?)`
76
+ ) ,
77
+ getBeforeStmt : db . prepare (
78
+ `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp < ? ORDER BY timestamp DESC, blockNumber DESC LIMIT 1`
79
+ ) ,
80
+ getAfterStmt : db . prepare (
81
+ `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp >= ? ORDER BY timestamp ASC, blockNumber ASC LIMIT 1`
82
+ ) ,
83
+ } ;
84
+
59
85
return Promise . resolve ( ) ;
60
86
} ,
61
87
62
- getTimestampByBlockNumber ( chainId , blockNumber ) : Promise < number | null > {
63
- if ( ! db ) {
88
+ async getTimestampByBlockNumber (
89
+ chainId ,
90
+ blockNumber
91
+ ) : Promise < number | null > {
92
+ if ( dbState . state === "uninitialized" ) {
64
93
throw new Error ( "SQLite database not initialized" ) ;
65
94
}
66
95
67
- const row = db
68
- . prepare (
69
- `SELECT * FROM ${ tableName } WHERE chainId = ? AND blockNumber = ?`
70
- )
71
- . get ( chainId , blockNumber . toString ( ) ) as Row | undefined ;
96
+ const row = dbState . getTimestampByBlockNumberStmt . get (
97
+ chainId ,
98
+ blockNumber . toString ( )
99
+ ) as Row | undefined ;
72
100
73
- return Promise . resolve ( row ? .timestamp ?? null ) ;
101
+ return Promise . resolve ( row ? row . timestamp : null ) ;
74
102
} ,
75
103
76
- getBlockNumberByTimestamp ( chainId , timestamp ) : Promise < bigint | null > {
77
- if ( ! db ) {
104
+ async getBlockNumberByTimestamp (
105
+ chainId ,
106
+ timestamp
107
+ ) : Promise < bigint | null > {
108
+ if ( dbState . state === "uninitialized" ) {
78
109
throw new Error ( "SQLite database not initialized" ) ;
79
110
}
80
111
81
- const row = db
82
- . prepare (
83
- `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp = ?`
84
- )
85
- . get ( chainId , timestamp ) as Row | undefined ;
112
+ const row = dbState . getBlockNumberByTimestampStmt . get (
113
+ chainId ,
114
+ timestamp
115
+ ) as Block | undefined ;
86
116
87
- return Promise . resolve ( row ?. blockNumber ? BigInt ( row . blockNumber ) : null ) ;
117
+ return Promise . resolve ( row ? BigInt ( row . blockNumber ) : null ) ;
88
118
} ,
89
119
90
- saveBlock ( block : Block ) : Promise < void > {
91
- if ( ! db ) {
120
+ async saveBlock ( block : Block ) : Promise < void > {
121
+ if ( dbState . state === "uninitialized" ) {
92
122
throw new Error ( "SQLite database not initialized" ) ;
93
123
}
94
124
95
- db . prepare (
96
- `INSERT OR REPLACE INTO ${ tableName } (chainId, blockNumber, timestamp) VALUES (?, ?, ?)`
97
- ) . run ( block . chainId , block . blockNumber . toString ( ) , block . timestampInSecs ) ;
125
+ dbState . saveBlockStmt . run (
126
+ block . chainId ,
127
+ block . blockNumber . toString ( ) ,
128
+ block . timestampInSecs
129
+ ) ;
98
130
99
131
return Promise . resolve ( ) ;
100
132
} ,
101
133
102
- getClosestBoundsForTimestamp (
134
+ async getClosestBoundsForTimestamp (
103
135
chainId ,
104
136
timestamp
105
- ) : Promise < {
106
- before : Block | null ;
107
- after : Block | null ;
108
- } > {
109
- if ( ! db ) {
137
+ ) : Promise < { before : Block | null ; after : Block | null } > {
138
+ if ( dbState . state === "uninitialized" ) {
110
139
throw new Error ( "SQLite database not initialized" ) ;
111
140
}
112
- const before = db
113
- . prepare (
114
- `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp < ? ORDER BY timestamp DESC, blockNumber DESC LIMIT 1`
115
- )
116
- . get ( chainId , timestamp ) as Row | undefined ;
117
141
118
- const after = db
119
- . prepare (
120
- `SELECT * FROM ${ tableName } WHERE chainId = ? AND timestamp >= ? ORDER BY timestamp ASC, blockNumber ASC LIMIT 1`
121
- )
122
- . get ( chainId , timestamp ) as Row | undefined ;
142
+ const before = dbState . getBeforeStmt . get ( chainId , timestamp ) as
143
+ | Row
144
+ | undefined ;
145
+
146
+ const after = dbState . getAfterStmt . get ( chainId , timestamp ) as
147
+ | Row
148
+ | undefined ;
123
149
124
150
return Promise . resolve ( {
125
151
before : before
126
- ? ( {
127
- ... before ,
152
+ ? {
153
+ chainId : before . chainId ,
128
154
timestampInSecs : before . timestamp ,
129
155
blockNumber : BigInt ( before . blockNumber ) ,
130
- } as Block )
156
+ }
131
157
: null ,
132
158
after : after
133
- ? ( {
134
- ... after ,
159
+ ? {
160
+ chainId : after . chainId ,
135
161
timestampInSecs : after . timestamp ,
136
162
blockNumber : BigInt ( after . blockNumber ) ,
137
- } as Block )
163
+ }
138
164
: null ,
139
165
} ) ;
140
166
} ,
0 commit comments