summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2009-05-05 18:02:11 +0000
committerTom Lane2009-05-05 18:02:11 +0000
commitfbbb8b87e5eddf6713c5d3d60c78adce97900a1b (patch)
tree4cdfef1d353166bf997c855fb7df0b4536b0abf6
parentbc07a4323684e2fb63a145d6e46a9d8b1416f240 (diff)
Avoid integer overflow in the loop that extracts histogram entries from
ANALYZE's total sample. The original coding is at risk of overflow for statistics targets exceeding about 2675; this was not a problem before 8.4 but it is now. Per bug #4793 from Dennis Noordsij.
-rw-r--r--src/backend/commands/analyze.c30
1 files changed, 27 insertions, 3 deletions
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 79e0cf81f2..cf6618b3ee 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -2260,6 +2260,10 @@ compute_scalar_stats(VacAttrStatsP stats,
MemoryContext old_context;
Datum *hist_values;
int nvals;
+ int pos,
+ posfrac,
+ delta,
+ deltafrac;
/* Sort the MCV items into position order to speed next loop */
qsort((void *) track, num_mcv,
@@ -2313,15 +2317,35 @@ compute_scalar_stats(VacAttrStatsP stats,
/* Must copy the target values into anl_context */
old_context = MemoryContextSwitchTo(stats->anl_context);
hist_values = (Datum *) palloc(num_hist * sizeof(Datum));
+
+ /*
+ * The object of this loop is to copy the first and last values[]
+ * entries along with evenly-spaced values in between. So the
+ * i'th value is values[(i * (nvals - 1)) / (num_hist - 1)]. But
+ * computing that subscript directly risks integer overflow when
+ * the stats target is more than a couple thousand. Instead we
+ * add (nvals - 1) / (num_hist - 1) to pos at each step, tracking
+ * the integral and fractional parts of the sum separately.
+ */
+ delta = (nvals - 1) / (num_hist - 1);
+ deltafrac = (nvals - 1) % (num_hist - 1);
+ pos = posfrac = 0;
+
for (i = 0; i < num_hist; i++)
{
- int pos;
-
- pos = (i * (nvals - 1)) / (num_hist - 1);
hist_values[i] = datumCopy(values[pos].value,
stats->attr->attbyval,
stats->attr->attlen);
+ pos += delta;
+ posfrac += deltafrac;
+ if (posfrac >= (num_hist - 1))
+ {
+ /* fractional part exceeds 1, carry to integer part */
+ pos++;
+ posfrac -= (num_hist - 1);
+ }
}
+
MemoryContextSwitchTo(old_context);
stats->stakind[slot_idx] = STATISTIC_KIND_HISTOGRAM;