@@ -7,11 +7,14 @@ import {
7
7
type EventContextInterface ,
8
8
} from './db_connection_impl.ts' ;
9
9
import { stdbLogger } from './logger.ts' ;
10
+ import type { ComparablePrimitive } from './algebraic_type.ts' ;
10
11
11
12
export type Operation = {
12
13
type : 'insert' | 'delete' ;
13
- // rowId: string;
14
- rowId : string ;
14
+ // For tables with a primary key, this is the primary key value, as a primitive or string.
15
+ // Otherwise, it is an encoding of the full row.
16
+ rowId : ComparablePrimitive ;
17
+ // TODO: Refine this type to at least reflect that it is a product.
15
18
row : any ;
16
19
} ;
17
20
@@ -29,7 +32,7 @@ export type PendingCallback = {
29
32
* Builder to generate calls to query a `table` in the database
30
33
*/
31
34
export class TableCache < RowType = any > {
32
- private rows : Map < string , [ RowType , number ] > ;
35
+ private rows : Map < ComparablePrimitive , [ RowType , number ] > ;
33
36
private tableTypeInfo : TableRuntimeTypeInfo ;
34
37
private emitter : EventEmitter < 'insert' | 'delete' | 'update' > ;
35
38
@@ -65,38 +68,31 @@ export class TableCache<RowType = any> {
65
68
) : PendingCallback [ ] => {
66
69
const pendingCallbacks : PendingCallback [ ] = [ ] ;
67
70
if ( this . tableTypeInfo . primaryKeyInfo !== undefined ) {
68
- const primaryKeyCol = this . tableTypeInfo . primaryKeyInfo . colName ;
69
- const primaryKeyType = this . tableTypeInfo . primaryKeyInfo . colType ;
70
- const getPrimaryKey = ( row : any ) => {
71
- const primaryKeyValue = row [ primaryKeyCol ] ;
72
- const writer = new BinaryWriter ( 10 ) ;
73
- primaryKeyType . serialize ( writer , primaryKeyValue ) ;
74
- return writer . toBase64 ( ) ;
75
- } ;
76
- const insertMap = new OperationsMap < any , [ Operation , number ] > ( ) ;
77
- const deleteMap = new OperationsMap < any , [ Operation , number ] > ( ) ;
71
+ const insertMap = new Map < ComparablePrimitive , [ Operation , number ] > ( ) ;
72
+ const deleteMap = new Map < ComparablePrimitive , [ Operation , number ] > ( ) ;
78
73
for ( const op of operations ) {
79
- const primaryKey = getPrimaryKey ( op . row ) ;
80
74
if ( op . type === 'insert' ) {
81
- const [ _ , prevCount ] = insertMap . get ( primaryKey ) || [ op , 0 ] ;
82
- insertMap . set ( primaryKey , [ op , prevCount + 1 ] ) ;
75
+ const [ _ , prevCount ] = insertMap . get ( op . rowId ) || [ op , 0 ] ;
76
+ insertMap . set ( op . rowId , [ op , prevCount + 1 ] ) ;
83
77
} else {
84
- const [ _ , prevCount ] = deleteMap . get ( primaryKey ) || [ op , 0 ] ;
85
- deleteMap . set ( primaryKey , [ op , prevCount + 1 ] ) ;
78
+ const [ _ , prevCount ] = deleteMap . get ( op . rowId ) || [ op , 0 ] ;
79
+ deleteMap . set ( op . rowId , [ op , prevCount + 1 ] ) ;
86
80
}
87
81
}
88
- for ( const {
89
- key : primaryKey ,
90
- value : [ insertOp , refCount ] ,
91
- } of insertMap ) {
82
+ for ( const [ primaryKey , [ insertOp , refCount ] ] of insertMap ) {
92
83
const deleteEntry = deleteMap . get ( primaryKey ) ;
93
84
if ( deleteEntry ) {
94
85
const [ deleteOp , deleteCount ] = deleteEntry ;
95
86
// In most cases the refCountDelta will be either 0 or refCount, but if
96
87
// an update moves a row in or out of the result set of different queries, then
97
88
// other deltas are possible.
98
89
const refCountDelta = refCount - deleteCount ;
99
- const maybeCb = this . update ( ctx , insertOp , deleteOp , refCountDelta ) ;
90
+ const maybeCb = this . update (
91
+ ctx ,
92
+ primaryKey ,
93
+ insertOp . row ,
94
+ refCountDelta
95
+ ) ;
100
96
if ( maybeCb ) {
101
97
pendingCallbacks . push ( maybeCb ) ;
102
98
}
@@ -134,36 +130,48 @@ export class TableCache<RowType = any> {
134
130
135
131
update = (
136
132
ctx : EventContextInterface ,
137
- newDbOp : Operation ,
138
- oldDbOp : Operation ,
133
+ rowId : ComparablePrimitive ,
134
+ newRow : RowType ,
139
135
refCountDelta : number = 0
140
136
) : PendingCallback | undefined => {
141
- const [ oldRow , previousCount ] = this . rows . get ( oldDbOp . rowId ) || [
142
- oldDbOp . row ,
143
- 0 ,
144
- ] ;
137
+ const existingEntry = this . rows . get ( rowId ) ;
138
+ if ( ! existingEntry ) {
139
+ // TODO: this should throw an error and kill the connection.
140
+ stdbLogger (
141
+ 'error' ,
142
+ `Updating a row that was not present in the cache. Table: ${ this . tableTypeInfo . tableName } , RowId: ${ rowId } `
143
+ ) ;
144
+ return undefined ;
145
+ }
146
+ const [ oldRow , previousCount ] = existingEntry ;
145
147
const refCount = Math . max ( 1 , previousCount + refCountDelta ) ;
146
- this . rows . delete ( oldDbOp . rowId ) ;
147
- this . rows . set ( newDbOp . rowId , [ newDbOp . row , refCount ] ) ;
148
+ if ( previousCount + refCountDelta <= 0 ) {
149
+ stdbLogger (
150
+ 'error' ,
151
+ `Negative reference count for in table ${ this . tableTypeInfo . tableName } row ${ rowId } (${ previousCount } + ${ refCountDelta } )`
152
+ ) ;
153
+ return undefined ;
154
+ }
155
+ this . rows . set ( rowId , [ newRow , refCount ] ) ;
148
156
// This indicates something is wrong, so we could arguably crash here.
149
157
if ( previousCount === 0 ) {
150
- stdbLogger ( 'error' , 'Updating a row that was not present in the cache' ) ;
158
+ stdbLogger (
159
+ 'error' ,
160
+ `Updating a row id in table ${ this . tableTypeInfo . tableName } which was not present in the cache (rowId: ${ rowId } )`
161
+ ) ;
151
162
return {
152
163
type : 'insert' ,
153
164
table : this . tableTypeInfo . tableName ,
154
165
cb : ( ) => {
155
- this . emitter . emit ( 'insert' , ctx , newDbOp . row ) ;
166
+ this . emitter . emit ( 'insert' , ctx , newRow ) ;
156
167
} ,
157
168
} ;
158
- } else if ( previousCount + refCountDelta <= 0 ) {
159
- stdbLogger ( 'error' , 'Negative reference count for row' ) ;
160
- // TODO: We should actually error and kill the connection here.
161
169
}
162
170
return {
163
171
type : 'update' ,
164
172
table : this . tableTypeInfo . tableName ,
165
173
cb : ( ) => {
166
- this . emitter . emit ( 'update' , ctx , oldRow , newDbOp . row ) ;
174
+ this . emitter . emit ( 'update' , ctx , oldRow , newRow ) ;
167
175
} ,
168
176
} ;
169
177
} ;
@@ -187,7 +195,7 @@ export class TableCache<RowType = any> {
187
195
} ,
188
196
} ;
189
197
}
190
- console . log ( `previousCount of ${ previousCount } for ${ operation . rowId } ` ) ;
198
+ // It's possible to get a duplicate insert because rows can be returned from multiple queries.
191
199
return undefined ;
192
200
} ;
193
201
0 commit comments