15
15
import { BigintMath } from '../base/bigint_math' ;
16
16
import { assertExists } from '../base/logging' ;
17
17
import { Engine } from '../common/engine' ;
18
+ import { QueryResult } from '../common/query_result' ;
18
19
import { Registry } from '../common/registry' ;
19
20
import { TraceTime , TrackState } from '../common/state' ;
20
21
import {
@@ -34,6 +35,10 @@ interface TrackConfig {}
34
35
35
36
type TrackConfigWithNamespace = TrackConfig & { namespace : string } ;
36
37
38
+ // Helper type that asserts that two vararg parameters have the same length
39
+ type SameLength < T extends unknown [ ] , U extends unknown [ ] > =
40
+ T extends { length : U [ 'length' ] } ? T : never ;
41
+
37
42
// TrackController is a base class overridden by track implementations (e.g.,
38
43
// sched slices, nestable slices, counters).
39
44
export abstract class TrackController <
@@ -105,6 +110,96 @@ export abstract class TrackController<
105
110
return `${ prefix } _${ idSuffix } ` ;
106
111
}
107
112
113
+ /**
114
+ * Create a dynamic table with an automatically assigned name with
115
+ * convenient clean-up and handling of attempts to query the table
116
+ * after it has been dropped.
117
+ *
118
+ * @param {string } key to distinguish this table from other tables
119
+ * and views dynamically created by the track
120
+ * @param {string|Function } ddl a function that generates the
121
+ * table DDL using the dynamically assigned name of the
122
+ * table and its dependencies from which it extracts data.
123
+ * The DDL factory must accept a parameter for each dependency,
124
+ * in order, to get its name
125
+ * @param {DynamicTable[] } withDependencies optional dynamic tables
126
+ * from which the new table extracts data, thus being dependencies
127
+ * @return {DynamicTable } the dynamic table manager
128
+ */
129
+ protected createDynamicTable
130
+ < DN extends string [ ] , DT extends DynamicTable [ ] > (
131
+ key : string ,
132
+ ddl : ( name : string , ...dependencies : DN ) => string ,
133
+ ...withDependencies : SameLength < DT , DN > ) : DynamicTable {
134
+ return this . createDynamicTableOrView ( 'table' , key , ddl , ...withDependencies ) ;
135
+ }
136
+ /**
137
+ * Create a dynamic view with an automatically assigned name with
138
+ * convenient clean-up and handling of attempts to query the view
139
+ * after it has been dropped.
140
+ *
141
+ * @param {string } key to distinguish this view from other tables
142
+ * and views dynamically created by the track
143
+ * @param {string|Function } ddl a function that generates the
144
+ * view DDL using the dynamically assigned name of the
145
+ * view and its dependencies from which it extracts data.
146
+ * The DDL factory must accept a parameter for each dependency,
147
+ * in order, to get its name
148
+ * @param {DynamicTable[] } withDependencies optional dynamic tables
149
+ * from which the new view extracts data, thus being dependencies
150
+ * @return {DynamicTable } the dynamic view manager
151
+ */
152
+ protected createDynamicView
153
+ < DN extends string [ ] , DT extends DynamicTable [ ] > (
154
+ key : string ,
155
+ ddl : ( name : string , ...dependencies : DN ) => string ,
156
+ ...withDependencies : SameLength < DT , DN > ) : DynamicTable {
157
+ return this . createDynamicTableOrView ( 'view' , key , ddl , ...withDependencies ) ;
158
+ }
159
+ private createDynamicTableOrView
160
+ < DN extends string [ ] , DT extends DynamicTable [ ] > (
161
+ type : DynamicTable [ 'type' ] ,
162
+ key : string ,
163
+ ddl : ( name : string , ...dependencies : DN ) => string ,
164
+ ...withDependencies : SameLength < DT , DN > ) : DynamicTable {
165
+ const name = this . tableName ( key ) ;
166
+ if ( withDependencies . some ( ( dep ) => ! dep . exists ) ) {
167
+ return DynamicTable . NONE ;
168
+ }
169
+
170
+ const dependencyNames = withDependencies . map ( ( dep ) => dep . name ) as DN ;
171
+
172
+ let tableExists = true ;
173
+ const pendingCreation = this . query ( ddl ( name , ...dependencyNames ) ) ;
174
+ return {
175
+ type,
176
+ name,
177
+ get exists ( ) {
178
+ return tableExists ;
179
+ } ,
180
+ drop : async ( ) => {
181
+ tableExists = false ;
182
+ await pendingCreation ;
183
+ await this . query ( `drop ${ type } if exists ${ name } ` ) ;
184
+ } ,
185
+ query : async < T , E = undefined > (
186
+ query : string | ( ( tableName : string ) => string ) ,
187
+ resultCase ?: ( result : QueryResult ) => T ,
188
+ elseCase ?: ( ) => E ) => {
189
+ if ( ! tableExists ) {
190
+ return resultCase ? false : elseCase ?. call ( this ) ;
191
+ }
192
+ const sql = typeof query === 'function' ? query ( name ) : query ;
193
+ await pendingCreation ;
194
+ const result = await this . query ( sql ) ;
195
+ if ( ! resultCase ) {
196
+ return true ;
197
+ }
198
+ return resultCase . call ( this , result ) ;
199
+ } ,
200
+ } ;
201
+ }
202
+
108
203
shouldSummarize ( resolution : number ) : boolean {
109
204
// |resolution| is in s/px (to nearest power of 10) assuming a display
110
205
// of ~1000px 0.0008 is 0.8s.
@@ -279,6 +374,77 @@ export abstract class TrackController<
279
374
}
280
375
}
281
376
377
+ /**
378
+ * A wrapper for access to a dynamic-defined table or view
379
+ * that may or may not exist at any given time.
380
+ */
381
+ export interface DynamicTable {
382
+ /** What kind of dynamic object that is encapsulated. */
383
+ readonly type : 'table' | 'view' ;
384
+ /** The name of the dynamic object that is encapsulated. */
385
+ readonly name : string ;
386
+ /** Whether the dynamic object that is encapsulated currently exists. */
387
+ readonly exists : boolean ;
388
+ /**
389
+ * Drop the dynamically-defined table or view.
390
+ * From this point on, the query APIs will shunt to the else case.
391
+ */
392
+ drop ( ) : Promise < void > ;
393
+ /**
394
+ * Perform an UPDATE, DELETE, or other statement that does not return
395
+ * results, if and only if the dynamic table exists.
396
+ */
397
+ query (
398
+ query : string | ( ( tableName : string ) => string ) ) : Promise < boolean > ;
399
+ /**
400
+ * Perform a SELECT query, if and only if the dynamic table exists, in
401
+ * which case the query result is sent to the given call-back function
402
+ * to process. There is no else-case call-back.
403
+ *
404
+ * @param query a query string or factory, which latter accepts the
405
+ * dynamically-assigned table or view name as a parameter
406
+ * @param resultCase a call-back function to process the query result
407
+ */
408
+ query < T > (
409
+ query : string | ( ( tableName : string ) => string ) ,
410
+ resultCase : ( result : QueryResult ) => T ) : Promise < T | undefined > ;
411
+ /**
412
+ * Perform a SELECT query, if and only if the dynamic table exists, in
413
+ * which case the query result is sent to the given call-back function
414
+ * to process. If the table does not exist, return the result of the
415
+ * else-case call-back function, instead.
416
+ *
417
+ * @param query a query string or factory, which latter accepts the
418
+ * dynamically-assigned table or view name as a parameter
419
+ * @param resultCase a call-back function to process the query result
420
+ * @param elseCase a call-back function to call in the eventuality
421
+ that the table has already been dropped
422
+ */
423
+ query < T , E > (
424
+ query : string | ( ( tableName : string ) => string ) ,
425
+ resultCase : ( result : QueryResult ) => T ,
426
+ elseCase : ( ) => E ) : Promise < T | E > ;
427
+ }
428
+
429
+ export namespace DynamicTable {
430
+ /** Placeholder for a dynamic table that has never (yet) been created. */
431
+ export const NONE : DynamicTable = {
432
+ type : 'view' ,
433
+ name : '<none>' ,
434
+ exists : false ,
435
+ drop : ( ) => Promise . resolve ( ) ,
436
+ query : async < T , E = undefined > (
437
+ _query : string | ( ( tableName : string ) => string ) ,
438
+ resultCase ?: ( result : QueryResult ) => T ,
439
+ elseCase ?: ( ) => E ) => {
440
+ if ( resultCase === undefined ) {
441
+ return false ;
442
+ }
443
+ return elseCase ?.( ) ;
444
+ } ,
445
+ } ;
446
+ }
447
+
282
448
export interface TrackControllerArgs {
283
449
trackId : string ;
284
450
engine : Engine ;
0 commit comments