@@ -168,6 +168,8 @@ static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
168
168
int rangelo , int rangehi );
169
169
static char * convert_string_datum (Datum value , Oid typid );
170
170
static double convert_timevalue_to_scalar (Datum value , Oid typid );
171
+ static void examine_simple_variable (PlannerInfo * root , Var * var ,
172
+ VariableStatData * vardata );
171
173
static bool get_variable_range (PlannerInfo * root , VariableStatData * vardata ,
172
174
Oid sortop , Datum * min , Datum * max );
173
175
static bool get_actual_variable_range (PlannerInfo * root ,
@@ -4153,46 +4155,16 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
4153
4155
(varRelid == 0 || varRelid == ((Var * ) basenode )-> varno ))
4154
4156
{
4155
4157
Var * var = (Var * ) basenode ;
4156
- RangeTblEntry * rte ;
4157
4158
4159
+ /* Set up result fields other than the stats tuple */
4158
4160
vardata -> var = basenode ; /* return Var without relabeling */
4159
4161
vardata -> rel = find_base_rel (root , var -> varno );
4160
4162
vardata -> atttype = var -> vartype ;
4161
4163
vardata -> atttypmod = var -> vartypmod ;
4162
4164
vardata -> isunique = has_unique_index (vardata -> rel , var -> varattno );
4163
4165
4164
- rte = root -> simple_rte_array [var -> varno ];
4165
-
4166
- if (get_relation_stats_hook &&
4167
- (* get_relation_stats_hook ) (root , rte , var -> varattno , vardata ))
4168
- {
4169
- /*
4170
- * The hook took control of acquiring a stats tuple. If it did
4171
- * supply a tuple, it'd better have supplied a freefunc.
4172
- */
4173
- if (HeapTupleIsValid (vardata -> statsTuple ) &&
4174
- !vardata -> freefunc )
4175
- elog (ERROR , "no function provided to release variable stats with" );
4176
- }
4177
- else if (rte -> rtekind == RTE_RELATION )
4178
- {
4179
- vardata -> statsTuple = SearchSysCache3 (STATRELATTINH ,
4180
- ObjectIdGetDatum (rte -> relid ),
4181
- Int16GetDatum (var -> varattno ),
4182
- BoolGetDatum (rte -> inh ));
4183
- vardata -> freefunc = ReleaseSysCache ;
4184
- }
4185
- else
4186
- {
4187
- /*
4188
- * XXX This means the Var comes from a JOIN or sub-SELECT. Later
4189
- * add code to dig down into the join etc and see if we can trace
4190
- * the variable to something with stats. (But beware of
4191
- * sub-SELECTs with DISTINCT/GROUP BY/etc. Perhaps there are no
4192
- * cases where this would really be useful, because we'd have
4193
- * flattened the subselect if it is??)
4194
- */
4195
- }
4166
+ /* Try to locate some stats */
4167
+ examine_simple_variable (root , var , vardata );
4196
4168
4197
4169
return ;
4198
4170
}
@@ -4334,6 +4306,109 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
4334
4306
}
4335
4307
}
4336
4308
4309
+ /*
4310
+ * examine_simple_variable
4311
+ * Handle a simple Var for examine_variable
4312
+ *
4313
+ * This is split out as a subroutine so that we can recurse to deal with
4314
+ * Vars referencing subqueries.
4315
+ *
4316
+ * We already filled in all the fields of *vardata except for the stats tuple.
4317
+ */
4318
+ static void
4319
+ examine_simple_variable (PlannerInfo * root , Var * var ,
4320
+ VariableStatData * vardata )
4321
+ {
4322
+ RangeTblEntry * rte = root -> simple_rte_array [var -> varno ];
4323
+
4324
+ Assert (IsA (rte , RangeTblEntry ));
4325
+
4326
+ if (get_relation_stats_hook &&
4327
+ (* get_relation_stats_hook ) (root , rte , var -> varattno , vardata ))
4328
+ {
4329
+ /*
4330
+ * The hook took control of acquiring a stats tuple. If it did supply
4331
+ * a tuple, it'd better have supplied a freefunc.
4332
+ */
4333
+ if (HeapTupleIsValid (vardata -> statsTuple ) &&
4334
+ !vardata -> freefunc )
4335
+ elog (ERROR , "no function provided to release variable stats with" );
4336
+ }
4337
+ else if (rte -> rtekind == RTE_RELATION )
4338
+ {
4339
+ /*
4340
+ * Plain table or parent of an inheritance appendrel, so look up the
4341
+ * column in pg_statistic
4342
+ */
4343
+ vardata -> statsTuple = SearchSysCache3 (STATRELATTINH ,
4344
+ ObjectIdGetDatum (rte -> relid ),
4345
+ Int16GetDatum (var -> varattno ),
4346
+ BoolGetDatum (rte -> inh ));
4347
+ vardata -> freefunc = ReleaseSysCache ;
4348
+ }
4349
+ else if (rte -> rtekind == RTE_SUBQUERY && !rte -> inh )
4350
+ {
4351
+ /*
4352
+ * Plain subquery (not one that was converted to an appendrel).
4353
+ *
4354
+ * Punt if subquery uses set operations, GROUP BY, or DISTINCT --- any
4355
+ * of these will mash underlying columns' stats beyond recognition.
4356
+ * (Set ops are particularly nasty; if we forged ahead, we would
4357
+ * return stats relevant to only the leftmost subselect...)
4358
+ */
4359
+ Query * subquery = rte -> subquery ;
4360
+ RelOptInfo * rel ;
4361
+ TargetEntry * ste ;
4362
+
4363
+ if (subquery -> setOperations ||
4364
+ subquery -> groupClause ||
4365
+ subquery -> distinctClause )
4366
+ return ;
4367
+
4368
+ /*
4369
+ * OK, fetch RelOptInfo for subquery. Note that we don't change the
4370
+ * rel returned in vardata, since caller expects it to be a rel of the
4371
+ * caller's query level. Because we might already be recursing, we
4372
+ * can't use that rel pointer either, but have to look up the Var's
4373
+ * rel afresh.
4374
+ */
4375
+ rel = find_base_rel (root , var -> varno );
4376
+
4377
+ /* Subquery should have been planned already */
4378
+ Assert (rel -> subroot && IsA (rel -> subroot , PlannerInfo ));
4379
+
4380
+ /* Get the subquery output expression referenced by the upper Var */
4381
+ ste = get_tle_by_resno (subquery -> targetList , var -> varattno );
4382
+ if (ste == NULL || ste -> resjunk )
4383
+ elog (ERROR , "subquery %s does not have attribute %d" ,
4384
+ rte -> eref -> aliasname , var -> varattno );
4385
+ var = (Var * ) ste -> expr ;
4386
+
4387
+ /* Can only handle a simple Var of subquery's query level */
4388
+ if (var && IsA (var , Var ) &&
4389
+ var -> varlevelsup == 0 )
4390
+ {
4391
+ /*
4392
+ * OK, recurse into the subquery. Note that the original setting
4393
+ * of vardata->isunique (which will surely be false) is left
4394
+ * unchanged in this situation. That's what we want, since even
4395
+ * if the underlying column is unique, the subquery may have
4396
+ * joined to other tables in a way that creates duplicates.
4397
+ */
4398
+ examine_simple_variable (rel -> subroot , var , vardata );
4399
+ }
4400
+ }
4401
+ else
4402
+ {
4403
+ /*
4404
+ * Otherwise, the Var comes from a FUNCTION, VALUES, or CTE RTE. (We
4405
+ * won't see RTE_JOIN here because join alias Vars have already been
4406
+ * flattened.) There's not much we can do with function outputs, but
4407
+ * maybe someday try to be smarter about VALUES and/or CTEs.
4408
+ */
4409
+ }
4410
+ }
4411
+
4337
4412
/*
4338
4413
* get_variable_numdistinct
4339
4414
* Estimate the number of distinct values of a variable.
0 commit comments