11import { isParameterValueClause , isRowValueClause , SQLITE_TRUE , sqliteBool } from '../sql_support.js' ;
22import { TablePattern } from '../TablePattern.js' ;
3- import { ParameterMatchClause , ParameterValueClause , RowValueClause , SqliteJsonValue } from '../types.js' ;
3+ import {
4+ EvaluatedParametersResult ,
5+ ParameterMatchClause ,
6+ ParameterValueClause ,
7+ RowValueClause ,
8+ SqliteJsonValue ,
9+ SqliteRow
10+ } from '../types.js' ;
411import { isJsonValue , normalizeParameterValue } from '../utils.js' ;
512import { SqlTools } from '../sql_filters.js' ;
613import { checkJsonArray , OPERATOR_NOT } from '../sql_functions.js' ;
@@ -10,6 +17,12 @@ import { StreamVariant } from './variant.js';
1017import { SubqueryEvaluator } from './parameter.js' ;
1118import { cartesianProduct } from './utils.js' ;
1219import { NodeLocation } from 'pgsql-ast-parser' ;
20+ import {
21+ BucketParameterLookupSource ,
22+ BucketParameterLookupSourceDefinition ,
23+ CreateSourceParams
24+ } from '../BucketSource.js' ;
25+ import { SourceTableInterface } from '../SourceTableInterface.js' ;
1326
1427/**
1528 * An intermediate representation of a `WHERE` clause for stream queries.
@@ -253,19 +266,10 @@ export class Subquery {
253266
254267 const evaluator : SubqueryEvaluator = {
255268 parameterTable : this . table ,
256- lookupsForParameterRow ( sourceTable , row ) {
257- const value = column . evaluate ( { [ sourceTable . name ] : row } ) ;
258- if ( ! isJsonValue ( value ) ) {
259- return null ;
260- }
261-
262- const lookups : ParameterLookup [ ] = [ ] ;
263- for ( const [ variant , id ] of innerVariants ) {
264- for ( const instantiation of variant . instantiationsForRow ( { sourceTable, record : row } ) ) {
265- lookups . push ( ParameterLookup . normalized ( context . streamName , id , instantiation ) ) ;
266- }
267- }
268- return { value, lookups } ;
269+ lookupSources ( streamName ) {
270+ return innerVariants . map ( ( [ variant , id ] ) => {
271+ return new SubqueryParameterLookupSource ( evaluator , column , variant , id , streamName ) ;
272+ } ) ;
269273 } ,
270274 lookupsForRequest ( parameters ) {
271275 const lookups : ParameterLookup [ ] = [ ] ;
@@ -510,3 +514,75 @@ export class EvaluateSimpleCondition extends FilterOperator {
510514 ) ;
511515 }
512516}
517+
518+ export class SubqueryParameterLookupSource implements BucketParameterLookupSourceDefinition {
519+ constructor (
520+ private subquery : SubqueryEvaluator ,
521+ private column : RowValueClause ,
522+ private innerVariant : StreamVariant ,
523+ public readonly defaultQueryId : string ,
524+ private streamName : string
525+ ) { }
526+
527+ get defaultLookupName ( ) {
528+ return this . streamName ;
529+ }
530+
531+ getSourceTables ( ) : Set < TablePattern > {
532+ let result = new Set < TablePattern > ( ) ;
533+ result . add ( this . subquery . parameterTable ) ;
534+ return result ;
535+ }
536+
537+ /**
538+ * Creates lookup indices for dynamically-resolved parameters.
539+ *
540+ * Resolving dynamic parameters is a two-step process: First, for tables referenced in subqueries, we create an index
541+ * to resolve which request parameters would match rows in subqueries. Then, when resolving bucket ids for a request,
542+ * we compute subquery results by looking up results in that index.
543+ *
544+ * This implements the first step of that process.
545+ *
546+ * @param result The array into which evaluation results should be written to.
547+ * @param sourceTable A table we depend on in a subquery.
548+ * @param row Row data to index.
549+ */
550+ evaluateParameterRow ( sourceTable : SourceTableInterface , row : SqliteRow ) : EvaluatedParametersResult [ ] {
551+ if ( this . subquery . parameterTable . matches ( sourceTable ) ) {
552+ // Theoretically we're doing duplicate work by doing this for each innerVariant in a subquery.
553+ // In practice, we don't have more than one innerVariant per subquery right now, so this is fine.
554+ const value = this . column . evaluate ( { [ sourceTable . name ] : row } ) ;
555+ if ( ! isJsonValue ( value ) ) {
556+ return [ ] ;
557+ }
558+
559+ const lookups : ParameterLookup [ ] = [ ] ;
560+ for ( const instantiation of this . innerVariant . instantiationsForRow ( { sourceTable, record : row } ) ) {
561+ // TODO: dynamic lookup name and query id
562+ lookups . push ( ParameterLookup . normalized ( this . defaultLookupName , this . defaultQueryId , instantiation ) ) ;
563+ }
564+
565+ // The row of the subquery. Since we only support subqueries with a single column, we unconditionally name the
566+ // column `result` for simplicity.
567+ const resultRow = { result : value } ;
568+
569+ return lookups . map ( ( l ) => ( {
570+ lookup : l ,
571+ bucketParameters : [ resultRow ]
572+ } ) ) ;
573+ }
574+ return [ ] ;
575+ }
576+
577+ createParameterLookupSource ( params : CreateSourceParams ) : BucketParameterLookupSource {
578+ return {
579+ evaluateParameterRow : ( sourceTable , row ) => {
580+ return this . evaluateParameterRow ( sourceTable , row ) ;
581+ }
582+ } ;
583+ }
584+
585+ tableSyncsParameters ( table : SourceTableInterface ) : boolean {
586+ return this . subquery . parameterTable . matches ( table ) ;
587+ }
588+ }
0 commit comments