@@ -3193,3 +3193,146 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
31933193 as -> d .agg_strict_trans_check .jumpnull = state -> steps_len ;
31943194 }
31953195}
3196+
3197+ /*
3198+ * Build equality expression that can be evaluated using ExecQual(), returning
3199+ * true if the expression context's inner/outer tuple are NOT DISTINCT. I.e
3200+ * two nulls match, a null and a not-null don't match.
3201+ *
3202+ * desc: tuple descriptor of the to-be-compared tuples
3203+ * numCols: the number of attributes to be examined
3204+ * keyColIdx: array of attribute column numbers
3205+ * eqFunctions: array of function oids of the equality functions to use
3206+ * parent: parent executor node
3207+ */
3208+ ExprState *
3209+ ExecBuildGroupingEqual (TupleDesc ldesc , TupleDesc rdesc ,
3210+ int numCols ,
3211+ AttrNumber * keyColIdx ,
3212+ Oid * eqfunctions ,
3213+ PlanState * parent )
3214+ {
3215+ ExprState * state = makeNode (ExprState );
3216+ ExprEvalStep scratch = {0 };
3217+ int natt ;
3218+ int maxatt = -1 ;
3219+ List * adjust_jumps = NIL ;
3220+ ListCell * lc ;
3221+
3222+ /*
3223+ * When no columns are actually compared, the result's always true. See
3224+ * special case in ExecQual().
3225+ */
3226+ if (numCols == 0 )
3227+ return NULL ;
3228+
3229+ state -> expr = NULL ;
3230+ state -> flags = EEO_FLAG_IS_QUAL ;
3231+ state -> parent = parent ;
3232+
3233+ scratch .resvalue = & state -> resvalue ;
3234+ scratch .resnull = & state -> resnull ;
3235+
3236+ /* compute max needed attribute */
3237+ for (natt = 0 ; natt < numCols ; natt ++ )
3238+ {
3239+ int attno = keyColIdx [natt ];
3240+
3241+ if (attno > maxatt )
3242+ maxatt = attno ;
3243+ }
3244+ Assert (maxatt >= 0 );
3245+
3246+ /* push deform steps */
3247+ scratch .opcode = EEOP_INNER_FETCHSOME ;
3248+ scratch .d .fetch .last_var = maxatt ;
3249+ ExprEvalPushStep (state , & scratch );
3250+
3251+ scratch .opcode = EEOP_OUTER_FETCHSOME ;
3252+ scratch .d .fetch .last_var = maxatt ;
3253+ ExprEvalPushStep (state , & scratch );
3254+
3255+ /*
3256+ * Start comparing at the last field (least significant sort key). That's
3257+ * the most likely to be different if we are dealing with sorted input.
3258+ */
3259+ for (natt = numCols ; -- natt >= 0 ;)
3260+ {
3261+ int attno = keyColIdx [natt ];
3262+ Form_pg_attribute latt = TupleDescAttr (ldesc , attno - 1 );
3263+ Form_pg_attribute ratt = TupleDescAttr (rdesc , attno - 1 );
3264+ Oid foid = eqfunctions [natt ];
3265+ FmgrInfo * finfo ;
3266+ FunctionCallInfo fcinfo ;
3267+ AclResult aclresult ;
3268+
3269+ /* Check permission to call function */
3270+ aclresult = pg_proc_aclcheck (foid , GetUserId (), ACL_EXECUTE );
3271+ if (aclresult != ACLCHECK_OK )
3272+ aclcheck_error (aclresult , OBJECT_FUNCTION , get_func_name (foid ));
3273+
3274+ InvokeFunctionExecuteHook (foid );
3275+
3276+ /* Set up the primary fmgr lookup information */
3277+ finfo = palloc0 (sizeof (FmgrInfo ));
3278+ fcinfo = palloc0 (sizeof (FunctionCallInfoData ));
3279+ fmgr_info (foid , finfo );
3280+ fmgr_info_set_expr (NULL , finfo );
3281+ InitFunctionCallInfoData (* fcinfo , finfo , 2 ,
3282+ InvalidOid , NULL , NULL );
3283+
3284+ /* left arg */
3285+ scratch .opcode = EEOP_INNER_VAR ;
3286+ scratch .d .var .attnum = attno - 1 ;
3287+ scratch .d .var .vartype = latt -> atttypid ;
3288+ scratch .resvalue = & fcinfo -> arg [0 ];
3289+ scratch .resnull = & fcinfo -> argnull [0 ];
3290+ ExprEvalPushStep (state , & scratch );
3291+
3292+ /* right arg */
3293+ scratch .opcode = EEOP_OUTER_VAR ;
3294+ scratch .d .var .attnum = attno - 1 ;
3295+ scratch .d .var .vartype = ratt -> atttypid ;
3296+ scratch .resvalue = & fcinfo -> arg [1 ];
3297+ scratch .resnull = & fcinfo -> argnull [1 ];
3298+ ExprEvalPushStep (state , & scratch );
3299+
3300+ /* evaluate distinctness */
3301+ scratch .opcode = EEOP_NOT_DISTINCT ;
3302+ scratch .d .func .finfo = finfo ;
3303+ scratch .d .func .fcinfo_data = fcinfo ;
3304+ scratch .d .func .fn_addr = finfo -> fn_addr ;
3305+ scratch .d .func .nargs = 2 ;
3306+ scratch .resvalue = & state -> resvalue ;
3307+ scratch .resnull = & state -> resnull ;
3308+ ExprEvalPushStep (state , & scratch );
3309+
3310+ /* then emit EEOP_QUAL to detect if result is false (or null) */
3311+ scratch .opcode = EEOP_QUAL ;
3312+ scratch .d .qualexpr .jumpdone = -1 ;
3313+ scratch .resvalue = & state -> resvalue ;
3314+ scratch .resnull = & state -> resnull ;
3315+ ExprEvalPushStep (state , & scratch );
3316+ adjust_jumps = lappend_int (adjust_jumps ,
3317+ state -> steps_len - 1 );
3318+ }
3319+
3320+ /* adjust jump targets */
3321+ foreach (lc , adjust_jumps )
3322+ {
3323+ ExprEvalStep * as = & state -> steps [lfirst_int (lc )];
3324+
3325+ Assert (as -> opcode == EEOP_QUAL );
3326+ Assert (as -> d .qualexpr .jumpdone == -1 );
3327+ as -> d .qualexpr .jumpdone = state -> steps_len ;
3328+ }
3329+
3330+ scratch .resvalue = NULL ;
3331+ scratch .resnull = NULL ;
3332+ scratch .opcode = EEOP_DONE ;
3333+ ExprEvalPushStep (state , & scratch );
3334+
3335+ ExecReadyExpr (state );
3336+
3337+ return state ;
3338+ }
0 commit comments