Skip to content

Commit fb0919f

Browse files
committed
Don't assume that max offset number stays fixed on a page when we're
not holding a pin on the page. Use double instead of long to count rows in relation, so that code still works for > LONG_MAX rows in rel.
1 parent 6497a7f commit fb0919f

File tree

1 file changed

+67
-66
lines changed

1 file changed

+67
-66
lines changed

src/backend/commands/analyze.c

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.21 2001/06/22 19:16:21 wieck Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.22 2001/07/05 19:33:35 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -97,8 +97,8 @@ typedef struct
9797
} ScalarMCVItem;
9898

9999

100-
#define swapInt(a,b) {int _tmp; _tmp=a; a=b; b=_tmp;}
101-
#define swapDatum(a,b) {Datum _tmp; _tmp=a; a=b; b=_tmp;}
100+
#define swapInt(a,b) do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)
101+
#define swapDatum(a,b) do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)
102102

103103

104104
static int MESSAGE_LEVEL;
@@ -111,20 +111,18 @@ static int *datumCmpTupnoLink;
111111

112112
static VacAttrStats *examine_attribute(Relation onerel, int attnum);
113113
static int acquire_sample_rows(Relation onerel, HeapTuple *rows,
114-
int targrows, long *totalrows);
114+
int targrows, double *totalrows);
115115
static double random_fract(void);
116116
static double init_selection_state(int n);
117-
static long select_next_random_record(long t, int n, double *stateptr);
117+
static double select_next_random_record(double t, int n, double *stateptr);
118118
static int compare_rows(const void *a, const void *b);
119119
static int compare_scalars(const void *a, const void *b);
120120
static int compare_mcvs(const void *a, const void *b);
121-
static OffsetNumber get_page_max_offset(Relation relation,
122-
BlockNumber blocknumber);
123121
static void compute_minimal_stats(VacAttrStats *stats,
124-
TupleDesc tupDesc, long totalrows,
122+
TupleDesc tupDesc, double totalrows,
125123
HeapTuple *rows, int numrows);
126124
static void compute_scalar_stats(VacAttrStats *stats,
127-
TupleDesc tupDesc, long totalrows,
125+
TupleDesc tupDesc, double totalrows,
128126
HeapTuple *rows, int numrows);
129127
static void update_attstats(Oid relid, int natts, VacAttrStats **vacattrstats);
130128

@@ -143,7 +141,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
143141
VacAttrStats **vacattrstats;
144142
int targrows,
145143
numrows;
146-
long totalrows;
144+
double totalrows;
147145
HeapTuple *rows;
148146
HeapTuple tuple;
149147

@@ -298,7 +296,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
298296
if (!vacstmt->vacuum)
299297
vac_update_relstats(RelationGetRelid(onerel),
300298
onerel->rd_nblocks,
301-
(double) totalrows,
299+
totalrows,
302300
RelationGetForm(onerel)->relhasindex);
303301

304302
/*
@@ -488,7 +486,7 @@ examine_attribute(Relation onerel, int attnum)
488486
*/
489487
static int
490488
acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
491-
long *totalrows)
489+
double *totalrows)
492490
{
493491
int numrows = 0;
494492
HeapScanDesc scan;
@@ -499,7 +497,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
499497
OffsetNumber lastoffset;
500498
int numest;
501499
double tuplesperpage;
502-
long t;
500+
double t;
503501
double rstate;
504502

505503
Assert(targrows > 1);
@@ -520,7 +518,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
520518
*/
521519
if (!HeapTupleIsValid(tuple))
522520
{
523-
*totalrows = numrows;
521+
*totalrows = (double) numrows;
524522
return numrows;
525523
}
526524
/*
@@ -565,20 +563,22 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
565563
}
566564
tuplesperpage = (double) numest / (double) estblock;
567565

568-
t = numrows; /* t is the # of records processed so far */
566+
t = (double) numrows; /* t is the # of records processed so far */
569567
rstate = init_selection_state(targrows);
570568
for (;;)
571569
{
572570
double targpos;
573571
BlockNumber targblock;
572+
Buffer targbuffer;
573+
Page targpage;
574574
OffsetNumber targoffset,
575575
maxoffset;
576576

577577
t = select_next_random_record(t, targrows, &rstate);
578578
/* Try to read the t'th record in the table */
579-
targpos = (double) t / tuplesperpage;
579+
targpos = t / tuplesperpage;
580580
targblock = (BlockNumber) targpos;
581-
targoffset = ((int) (targpos - targblock) * tuplesperpage) +
581+
targoffset = ((int) ((targpos - targblock) * tuplesperpage)) +
582582
FirstOffsetNumber;
583583
/* Make sure we are past the last selected record */
584584
if (targblock <= lastblock)
@@ -595,21 +595,37 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
595595
*/
596596
if (targblock >= onerel->rd_nblocks)
597597
break;
598-
maxoffset = get_page_max_offset(onerel, targblock);
598+
/*
599+
* We must maintain a pin on the target page's buffer to ensure that
600+
* the maxoffset value stays good (else concurrent VACUUM might
601+
* delete tuples out from under us). Hence, pin the page until we
602+
* are done looking at it. We don't maintain a lock on the page,
603+
* so tuples could get added to it, but we ignore such tuples.
604+
*/
605+
targbuffer = ReadBuffer(onerel, targblock);
606+
if (!BufferIsValid(targbuffer))
607+
elog(ERROR, "acquire_sample_rows: ReadBuffer(%s,%u) failed",
608+
RelationGetRelationName(onerel), targblock);
609+
LockBuffer(targbuffer, BUFFER_LOCK_SHARE);
610+
targpage = BufferGetPage(targbuffer);
611+
maxoffset = PageGetMaxOffsetNumber(targpage);
612+
LockBuffer(targbuffer, BUFFER_LOCK_UNLOCK);
613+
599614
for (;;)
600615
{
601616
HeapTupleData targtuple;
602-
Buffer targbuffer;
617+
Buffer tupbuffer;
603618

604619
if (targoffset > maxoffset)
605620
{
606621
/* Fell off end of this page, try next */
622+
ReleaseBuffer(targbuffer);
607623
targblock++;
608624
targoffset = FirstOffsetNumber;
609625
goto pageloop;
610626
}
611627
ItemPointerSet(&targtuple.t_self, targblock, targoffset);
612-
heap_fetch(onerel, SnapshotNow, &targtuple, &targbuffer, NULL);
628+
heap_fetch(onerel, SnapshotNow, &targtuple, &tupbuffer, NULL);
613629
if (targtuple.t_data != NULL)
614630
{
615631
/*
@@ -621,6 +637,9 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
621637
Assert(k >= 0 && k < targrows);
622638
heap_freetuple(rows[k]);
623639
rows[k] = heap_copytuple(&targtuple);
640+
/* this releases the second pin acquired by heap_fetch: */
641+
ReleaseBuffer(tupbuffer);
642+
/* this releases the initial pin: */
624643
ReleaseBuffer(targbuffer);
625644
lastblock = targblock;
626645
lastoffset = targoffset;
@@ -639,7 +658,7 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
639658
/*
640659
* Estimate total number of valid rows in relation.
641660
*/
642-
*totalrows = (long) (onerel->rd_nblocks * tuplesperpage + 0.5);
661+
*totalrows = floor((double) onerel->rd_nblocks * tuplesperpage + 0.5);
643662

644663
return numrows;
645664
}
@@ -667,6 +686,12 @@ random_fract(void)
667686
* of the last record processed and next record to process. The only extra
668687
* state needed between calls is W, a random state variable.
669688
*
689+
* Note: the original algorithm defines t, S, numer, and denom as integers.
690+
* Here we express them as doubles to avoid overflow if the number of rows
691+
* in the table exceeds INT_MAX. The algorithm should work as long as the
692+
* row count does not become so large that it is not represented accurately
693+
* in a double (on IEEE-math machines this would be around 2^52 rows).
694+
*
670695
* init_selection_state computes the initial W value.
671696
*
672697
* Given that we've already processed t records (t >= n),
@@ -680,36 +705,36 @@ init_selection_state(int n)
680705
return exp(- log(random_fract())/n);
681706
}
682707

683-
static long
684-
select_next_random_record(long t, int n, double *stateptr)
708+
static double
709+
select_next_random_record(double t, int n, double *stateptr)
685710
{
686711
/* The magic constant here is T from Vitter's paper */
687-
if (t <= (22 * n))
712+
if (t <= (22.0 * n))
688713
{
689714
/* Process records using Algorithm X until t is large enough */
690715
double V,
691716
quot;
692717

693718
V = random_fract(); /* Generate V */
694-
t++;
695-
quot = (double) (t - n) / (double) t;
719+
t += 1;
720+
quot = (t - (double) n) / t;
696721
/* Find min S satisfying (4.1) */
697722
while (quot > V)
698723
{
699-
t++;
700-
quot *= (double) (t - n) / (double) t;
724+
t += 1;
725+
quot *= (t - (double) n) / t;
701726
}
702727
}
703728
else
704729
{
705730
/* Now apply Algorithm Z */
706731
double W = *stateptr;
707-
long term = t - n + 1;
708-
int S;
732+
double term = t - (double) n + 1;
733+
double S;
709734

710735
for (;;)
711736
{
712-
long numer,
737+
double numer,
713738
numer_lim,
714739
denom;
715740
double U,
@@ -722,9 +747,9 @@ select_next_random_record(long t, int n, double *stateptr)
722747
/* Generate U and X */
723748
U = random_fract();
724749
X = t * (W - 1.0);
725-
S = X; /* S is tentatively set to floor(X) */
750+
S = floor(X); /* S is tentatively set to floor(X) */
726751
/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
727-
tmp = (double) (t + 1) / (double) term;
752+
tmp = (t + 1) / term;
728753
lhs = exp(log(((U * tmp * tmp) * (term + S))/(t + X))/n);
729754
rhs = (((t + X)/(term + S)) * term)/t;
730755
if (lhs <= rhs)
@@ -734,20 +759,20 @@ select_next_random_record(long t, int n, double *stateptr)
734759
}
735760
/* Test if U <= f(S)/cg(X) */
736761
y = (((U * (t + 1))/term) * (t + S + 1))/(t + X);
737-
if (n < S)
762+
if ((double) n < S)
738763
{
739764
denom = t;
740765
numer_lim = term + S;
741766
}
742767
else
743768
{
744-
denom = t - n + S;
769+
denom = t - (double) n + S;
745770
numer_lim = t + 1;
746771
}
747-
for (numer = t + S; numer >= numer_lim; numer--)
772+
for (numer = t + S; numer >= numer_lim; numer -= 1)
748773
{
749-
y *= (double) numer / (double) denom;
750-
denom--;
774+
y *= numer / denom;
775+
denom -= 1;
751776
}
752777
W = exp(- log(random_fract())/n); /* Generate W in advance */
753778
if (exp(log(y)/n) <= (t + X)/t)
@@ -783,30 +808,6 @@ compare_rows(const void *a, const void *b)
783808
return 0;
784809
}
785810

786-
/*
787-
* Discover the largest valid tuple offset number on the given page
788-
*
789-
* This code probably ought to live in some other module.
790-
*/
791-
static OffsetNumber
792-
get_page_max_offset(Relation relation, BlockNumber blocknumber)
793-
{
794-
Buffer buffer;
795-
Page p;
796-
OffsetNumber offnum;
797-
798-
buffer = ReadBuffer(relation, blocknumber);
799-
if (!BufferIsValid(buffer))
800-
elog(ERROR, "get_page_max_offset: %s relation: ReadBuffer(%ld) failed",
801-
RelationGetRelationName(relation), (long) blocknumber);
802-
LockBuffer(buffer, BUFFER_LOCK_SHARE);
803-
p = BufferGetPage(buffer);
804-
offnum = PageGetMaxOffsetNumber(p);
805-
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
806-
ReleaseBuffer(buffer);
807-
return offnum;
808-
}
809-
810811

811812
/*
812813
* compute_minimal_stats() -- compute minimal column statistics
@@ -825,7 +826,7 @@ get_page_max_offset(Relation relation, BlockNumber blocknumber)
825826
*/
826827
static void
827828
compute_minimal_stats(VacAttrStats *stats,
828-
TupleDesc tupDesc, long totalrows,
829+
TupleDesc tupDesc, double totalrows,
829830
HeapTuple *rows, int numrows)
830831
{
831832
int i;
@@ -1002,7 +1003,7 @@ compute_minimal_stats(VacAttrStats *stats,
10021003

10031004
if (f1 < 1)
10041005
f1 = 1;
1005-
term1 = sqrt((double) totalrows / (double) numrows) * f1;
1006+
term1 = sqrt(totalrows / (double) numrows) * f1;
10061007
stats->stadistinct = floor(term1 + nmultiple + 0.5);
10071008
}
10081009

@@ -1104,7 +1105,7 @@ compute_minimal_stats(VacAttrStats *stats,
11041105
*/
11051106
static void
11061107
compute_scalar_stats(VacAttrStats *stats,
1107-
TupleDesc tupDesc, long totalrows,
1108+
TupleDesc tupDesc, double totalrows,
11081109
HeapTuple *rows, int numrows)
11091110
{
11101111
int i;
@@ -1298,7 +1299,7 @@ compute_scalar_stats(VacAttrStats *stats,
12981299

12991300
if (f1 < 1)
13001301
f1 = 1;
1301-
term1 = sqrt((double) totalrows / (double) numrows) * f1;
1302+
term1 = sqrt(totalrows / (double) numrows) * f1;
13021303
stats->stadistinct = floor(term1 + nmultiple + 0.5);
13031304
}
13041305

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy