@@ -150,9 +150,20 @@ test("planner estimates 10,000 rows with 10,000 rows seeded", async () => {
150150 INSERT INTO widgets (user_id, name)
151151 SELECT gen_random_uuid(), 'widget_' || i
152152 FROM generate_series(1, 10000) AS i;
153- ANALYZE;
154153 ` ) ;
155154
155+ const beforeAnalyze = await db . exec < { relpages : number ; reltuples : number } > (
156+ `SELECT relpages, reltuples::int FROM pg_class WHERE relname = 'widgets' AND relkind = 'r'` ,
157+ ) ;
158+ console . log ( `10K: BEFORE ANALYZE: relpages=${ beforeAnalyze [ 0 ] . relpages } , reltuples=${ beforeAnalyze [ 0 ] . reltuples } ` ) ;
159+
160+ await db . exec ( "ANALYZE widgets" ) ;
161+
162+ const afterAnalyze = await db . exec < { relpages : number ; reltuples : number } > (
163+ `SELECT relpages, reltuples::int FROM pg_class WHERE relname = 'widgets' AND relkind = 'r'` ,
164+ ) ;
165+ console . log ( `10K: AFTER ANALYZE: relpages=${ afterAnalyze [ 0 ] . relpages } , reltuples=${ afterAnalyze [ 0 ] . reltuples } ` ) ;
166+
156167 const mode = await buildStatsFromDatabase ( db ) ;
157168 const stats = await Statistics . fromPostgres ( db , mode ) ;
158169 const existingIndexes = await stats . getExistingIndexes ( ) ;
@@ -183,6 +194,7 @@ test("planner estimates 10,000 rows even with 50,000 rows seeded", async () => {
183194 const plan = await optimizer . testQueryWithStats ( builder ) ;
184195
185196 const estimatedRows = ( plan . Plan as Record < string , unknown > ) [ "Plan Rows" ] ;
197+ console . log ( `10K: planner estimatedRows=${ estimatedRows } ` ) ;
186198 expect ( estimatedRows ) . toBe ( 10_000 ) ;
187199} ) ;
188200
@@ -192,10 +204,22 @@ test("BUG: fromAssumption(relpages=1) inflates estimates with real data", async
192204 INSERT INTO widgets (user_id, name)
193205 SELECT gen_random_uuid(), 'widget_' || i
194206 FROM generate_series(1, 10000) AS i;
195- ANALYZE;
196207 ` ) ;
197208
209+ const beforeAnalyze = await db . exec < { relpages : number ; reltuples : number } > (
210+ `SELECT relpages, reltuples::int FROM pg_class WHERE relname = 'widgets' AND relkind = 'r'` ,
211+ ) ;
212+ console . log ( `BUG: BEFORE ANALYZE: relpages=${ beforeAnalyze [ 0 ] . relpages } , reltuples=${ beforeAnalyze [ 0 ] . reltuples } ` ) ;
213+
214+ await db . exec ( "ANALYZE widgets" ) ;
215+
216+ const afterAnalyze = await db . exec < { relpages : number ; reltuples : number } > (
217+ `SELECT relpages, reltuples::int FROM pg_class WHERE relname = 'widgets' AND relkind = 'r'` ,
218+ ) ;
219+ console . log ( `BUG: AFTER ANALYZE: relpages=${ afterAnalyze [ 0 ] . relpages } , reltuples=${ afterAnalyze [ 0 ] . reltuples } ` ) ;
220+
198221 const brokenMode = Statistics . defaultStatsMode ;
222+ console . log ( `BUG: override writes: reltuples=${ brokenMode . reltuples } , relpages=${ brokenMode . relpages } ` ) ;
199223 const stats = await Statistics . fromPostgres ( db , brokenMode ) ;
200224 const existingIndexes = await stats . getExistingIndexes ( ) ;
201225 const optimizer = new IndexOptimizer ( db , stats , existingIndexes ) ;
@@ -204,7 +228,12 @@ test("BUG: fromAssumption(relpages=1) inflates estimates with real data", async
204228 const plan = await optimizer . testQueryWithStats ( builder ) ;
205229
206230 const estimatedRows = ( plan . Plan as Record < string , unknown > ) [ "Plan Rows" ] ;
207- expect ( estimatedRows ) . toBeGreaterThan ( 100_000 ) ;
231+ console . log ( `BUG: planner estimatedRows=${ estimatedRows } ` ) ;
232+
233+ // With relpages=74 on disk and the override setting relpages=1,
234+ // PostgreSQL computes: density = 10000/1 = 10000 tuples/page,
235+ // then estimates tuples = 74 * 10000 = 740,000 instead of 10,000.
236+ expect ( estimatedRows ) . toBe ( 740_000 ) ;
208237} ) ;
209238
210239test ( "leaves columns null so ANALYZE pg_statistic entries persist" , async ( ) => {
0 commit comments