vacuum.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:76k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * vacuum.c
  4.  *   the postgres vacuum cleaner
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.110.2.3 1999/08/25 12:01:45 ishii Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. #include <sys/types.h>
  15. #include <sys/file.h>
  16. #include <sys/stat.h>
  17. #include <fcntl.h>
  18. #include <unistd.h>
  19. #include "postgres.h"
  20. #include "access/genam.h"
  21. #include "access/heapam.h"
  22. #include "catalog/catalog.h"
  23. #include "catalog/catname.h"
  24. #include "catalog/index.h"
  25. #include "catalog/pg_operator.h"
  26. #include "catalog/pg_statistic.h"
  27. #include "catalog/pg_type.h"
  28. #include "commands/vacuum.h"
  29. #include "miscadmin.h"
  30. #include "parser/parse_oper.h"
  31. #include "storage/smgr.h"
  32. #include "utils/builtins.h"
  33. #include "utils/inval.h"
  34. #include "utils/portal.h"
  35. #include "utils/relcache.h"
  36. #include "utils/syscache.h"
  37. #ifndef HAVE_GETRUSAGE
  38. #include "rusagestub.h"
  39. #else
  40. #include <sys/time.h>
  41. #include <sys/resource.h>
  42. #endif
  43.  /* #include <port-protos.h> *//* Why? */
  44. extern int BlowawayRelationBuffers(Relation rel, BlockNumber block);
  45. bool VacuumRunning = false;
  46. static Portal vc_portal;
  47. static int MESSAGE_LEVEL; /* message level */
  48. static TransactionId XmaxRecent;
  49. #define swapLong(a,b) {long tmp; tmp=a; a=b; b=tmp;}
  50. #define swapInt(a,b) {int tmp; tmp=a; a=b; b=tmp;}
  51. #define swapDatum(a,b) {Datum tmp; tmp=a; a=b; b=tmp;}
  52. #define VacAttrStatsEqValid(stats) ( stats->f_cmpeq.fn_addr != NULL )
  53. #define VacAttrStatsLtGtValid(stats) ( stats->f_cmplt.fn_addr != NULL && 
  54.    stats->f_cmpgt.fn_addr != NULL && 
  55.    RegProcedureIsValid(stats->outfunc) )
  56. /* non-export function prototypes */
  57. static void vc_init(void);
  58. static void vc_shutdown(void);
  59. static void vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols);
  60. static VRelList vc_getrels(NameData *VacRelP);
  61. static void vc_vacone(Oid relid, bool analyze, List *va_cols);
  62. static void vc_scanheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages);
  63. static void vc_rpfheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel);
  64. static void vc_vacheap(VRelStats *vacrelstats, Relation onerel, VPageList vpl);
  65. static void vc_vacpage(Page page, VPageDescr vpd);
  66. static void vc_vaconeind(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples);
  67. static void vc_scanoneind(Relation indrel, int num_tuples);
  68. static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple);
  69. static void vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int16 *bucket_len);
  70. static void vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats);
  71. static void vc_delhilowstats(Oid relid, int attcnt, int *attnums);
  72. static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl);
  73. static void vc_reappage(VPageList vpl, VPageDescr vpc);
  74. static void vc_vpinsert(VPageList vpl, VPageDescr vpnew);
  75. static void vc_free(VRelList vrl);
  76. static void vc_getindices(Oid relid, int *nindices, Relation **Irel);
  77. static void vc_clsindices(int nindices, Relation *Irel);
  78. static void vc_mkindesc(Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc);
  79. static void *vc_find_eq(void *bot, int nelem, int size, void *elm,
  80.    int (*compar) (const void *, const void *));
  81. static int vc_cmp_blk(const void *left, const void *right);
  82. static int vc_cmp_offno(const void *left, const void *right);
  83. static int vc_cmp_vtlinks(const void *left, const void *right);
  84. static bool vc_enough_space(VPageDescr vpd, Size len);
  85. void
  86. vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec)
  87. {
  88. char    *pname;
  89. MemoryContext old;
  90. PortalVariableMemory pmem;
  91. NameData VacRel;
  92. List    *le;
  93. List    *va_cols = NIL;
  94. /*
  95.  * Create a portal for safe memory across transctions. We need to
  96.  * palloc the name space for it because our hash function expects the
  97.  * name to be on a longword boundary.  CreatePortal copies the name to
  98.  * safe storage for us.
  99.  */
  100. pname = (char *) palloc(strlen(VACPNAME) + 1);
  101. strcpy(pname, VACPNAME);
  102. vc_portal = CreatePortal(pname);
  103. pfree(pname);
  104. if (verbose)
  105. MESSAGE_LEVEL = NOTICE;
  106. else
  107. MESSAGE_LEVEL = DEBUG;
  108. /* vacrel gets de-allocated on transaction commit */
  109. if (vacrel)
  110. strcpy(VacRel.data, vacrel);
  111. pmem = PortalGetVariableMemory(vc_portal);
  112. old = MemoryContextSwitchTo((MemoryContext) pmem);
  113. if (va_spec != NIL && !analyze)
  114. elog(ERROR, "Can't vacuum columns, only tables.  You can 'vacuum analyze' columns.");
  115. foreach(le, va_spec)
  116. {
  117. char    *col = (char *) lfirst(le);
  118. char    *dest;
  119. dest = (char *) palloc(strlen(col) + 1);
  120. strcpy(dest, col);
  121. va_cols = lappend(va_cols, dest);
  122. }
  123. MemoryContextSwitchTo(old);
  124. /* initialize vacuum cleaner */
  125. vc_init();
  126. /* vacuum the database */
  127. if (vacrel)
  128. vc_vacuum(&VacRel, analyze, va_cols);
  129. else
  130. vc_vacuum(NULL, analyze, NIL);
  131. PortalDestroy(&vc_portal);
  132. /* clean up */
  133. vc_shutdown();
  134. }
  135. /*
  136.  * vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner.
  137.  *
  138.  * We run exactly one vacuum cleaner at a time.  We use the file system
  139.  * to guarantee an exclusive lock on vacuuming, since a single vacuum
  140.  * cleaner instantiation crosses transaction boundaries, and we'd lose
  141.  * postgres-style locks at the end of every transaction.
  142.  *
  143.  * The strangeness with committing and starting transactions in the
  144.  * init and shutdown routines is due to the fact that the vacuum cleaner
  145.  * is invoked via a sql command, and so is already executing inside
  146.  * a transaction. We need to leave ourselves in a predictable state
  147.  * on entry and exit to the vacuum cleaner.  We commit the transaction
  148.  * started in PostgresMain() inside vc_init(), and start one in
  149.  * vc_shutdown() to match the commit waiting for us back in
  150.  * PostgresMain().
  151.  */
  152. static void
  153. vc_init()
  154. {
  155. int fd;
  156. #ifndef __CYGWIN32__
  157. if ((fd = open("pg_vlock", O_CREAT | O_EXCL, 0600)) < 0)
  158. #else
  159. if ((fd = open("pg_vlock", O_CREAT | O_EXCL | O_BINARY, 0600)) < 0)
  160. #endif
  161. {
  162. elog(ERROR, "Can't create lock file.  Is another vacuum cleaner running?n
  163. tIf not, you may remove the pg_vlock file in the %sn
  164. tdirectory", DatabasePath);
  165. }
  166. close(fd);
  167. /*
  168.  * By here, exclusive open on the lock file succeeded. If we abort
  169.  * for any reason during vacuuming, we need to remove the lock file.
  170.  * This global variable is checked in the transaction manager on xact
  171.  * abort, and the routine vc_abort() is called if necessary.
  172.  */
  173. VacuumRunning = true;
  174. /* matches the StartTransaction in PostgresMain() */
  175. CommitTransactionCommand();
  176. }
  177. static void
  178. vc_shutdown()
  179. {
  180. /* on entry, we are not in a transaction */
  181. /*
  182.  * Flush the init file that relcache.c uses to save startup time. The
  183.  * next backend startup will rebuild the init file with up-to-date
  184.  * information from pg_class.  This lets the optimizer see the stats
  185.  * that we've collected for certain critical system indexes.  See
  186.  * relcache.c for more details.
  187.  *
  188.  * Ignore any failure to unlink the file, since it might not be there if
  189.  * no backend has been started since the last vacuum...
  190.  */
  191. unlink(RELCACHE_INIT_FILENAME);
  192. /* remove the vacuum cleaner lock file */
  193. if (unlink("pg_vlock") < 0)
  194. elog(ERROR, "vacuum: can't destroy lock file!");
  195. /* okay, we're done */
  196. VacuumRunning = false;
  197. /* matches the CommitTransaction in PostgresMain() */
  198. StartTransactionCommand();
  199. }
  200. void
  201. vc_abort()
  202. {
  203. /* on abort, remove the vacuum cleaner lock file */
  204. unlink("pg_vlock");
  205. VacuumRunning = false;
  206. }
  207. /*
  208.  * vc_vacuum() -- vacuum the database.
  209.  *
  210.  * This routine builds a list of relations to vacuum, and then calls
  211.  * code that vacuums them one at a time.  We are careful to vacuum each
  212.  * relation in a separate transaction in order to avoid holding too many
  213.  * locks at one time.
  214.  */
  215. static void
  216. vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols)
  217. {
  218. VRelList vrl,
  219. cur;
  220. /* get list of relations */
  221. vrl = vc_getrels(VacRelP);
  222. if (analyze && VacRelP == NULL && vrl != NULL)
  223. vc_delhilowstats(InvalidOid, 0, NULL);
  224. /* vacuum each heap relation */
  225. for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
  226. vc_vacone(cur->vrl_relid, analyze, va_cols);
  227. vc_free(vrl);
  228. }
  229. static VRelList
  230. vc_getrels(NameData *VacRelP)
  231. {
  232. Relation rel;
  233. TupleDesc tupdesc;
  234. HeapScanDesc scan;
  235. HeapTuple tuple;
  236. PortalVariableMemory portalmem;
  237. MemoryContext old;
  238. VRelList vrl,
  239. cur;
  240. Datum d;
  241. char    *rname;
  242. char rkind;
  243. bool n;
  244. bool found = false;
  245. ScanKeyData key;
  246. StartTransactionCommand();
  247. if (VacRelP->data)
  248. {
  249. ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relname,
  250.    F_NAMEEQ,
  251.    PointerGetDatum(VacRelP->data));
  252. }
  253. else
  254. {
  255. ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relkind,
  256.    F_CHAREQ, CharGetDatum('r'));
  257. }
  258. portalmem = PortalGetVariableMemory(vc_portal);
  259. vrl = cur = (VRelList) NULL;
  260. rel = heap_openr(RelationRelationName);
  261. tupdesc = RelationGetDescr(rel);
  262. scan = heap_beginscan(rel, false, SnapshotNow, 1, &key);
  263. while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
  264. {
  265. found = true;
  266. d = heap_getattr(tuple, Anum_pg_class_relname, tupdesc, &n);
  267. rname = (char *) d;
  268. d = heap_getattr(tuple, Anum_pg_class_relkind, tupdesc, &n);
  269. rkind = DatumGetChar(d);
  270. if (rkind != RELKIND_RELATION)
  271. {
  272. elog(NOTICE, "Vacuum: can not process index and certain system tables");
  273. continue;
  274. }
  275. /* get a relation list entry for this guy */
  276. old = MemoryContextSwitchTo((MemoryContext) portalmem);
  277. if (vrl == (VRelList) NULL)
  278. vrl = cur = (VRelList) palloc(sizeof(VRelListData));
  279. else
  280. {
  281. cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));
  282. cur = cur->vrl_next;
  283. }
  284. MemoryContextSwitchTo(old);
  285. cur->vrl_relid = tuple->t_data->t_oid;
  286. cur->vrl_next = (VRelList) NULL;
  287. }
  288. if (found == false)
  289. elog(NOTICE, "Vacuum: table not found");
  290. heap_endscan(scan);
  291. heap_close(rel);
  292. CommitTransactionCommand();
  293. return vrl;
  294. }
  295. /*
  296.  * vc_vacone() -- vacuum one heap relation
  297.  *
  298.  * This routine vacuums a single heap, cleans out its indices, and
  299.  * updates its statistics num_pages and num_tuples statistics.
  300.  *
  301.  * Doing one heap at a time incurs extra overhead, since we need to
  302.  * check that the heap exists again just before we vacuum it. The
  303.  * reason that we do this is so that vacuuming can be spread across
  304.  * many small transactions.  Otherwise, two-phase locking would require
  305.  * us to lock the entire database during one pass of the vacuum cleaner.
  306.  */
  307. static void
  308. vc_vacone(Oid relid, bool analyze, List *va_cols)
  309. {
  310. HeapTuple tuple,
  311. typetuple;
  312. Relation onerel;
  313. VPageListData vacuum_pages; /* List of pages to vacuum and/or clean
  314.  * indices */
  315. VPageListData fraged_pages; /* List of pages with space enough for
  316.  * re-using */
  317. VPageDescr *vpp;
  318. Relation   *Irel;
  319. int32 nindices,
  320. i;
  321. VRelStats  *vacrelstats;
  322. StartTransactionCommand();
  323. /*
  324.  * Race condition -- if the pg_class tuple has gone away since the
  325.  * last time we saw it, we don't need to vacuum it.
  326.  */
  327. tuple = SearchSysCacheTuple(RELOID,
  328. ObjectIdGetDatum(relid),
  329. 0, 0, 0);
  330. if (!HeapTupleIsValid(tuple))
  331. {
  332. CommitTransactionCommand();
  333. return;
  334. }
  335. /* now open the class and vacuum it */
  336. onerel = heap_open(relid);
  337. vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));
  338. vacrelstats->relid = relid;
  339. vacrelstats->num_pages = vacrelstats->num_tuples = 0;
  340. vacrelstats->hasindex = false;
  341. if (analyze && !IsSystemRelationName((RelationGetRelationName(onerel))->data))
  342. {
  343. int attr_cnt,
  344.    *attnums = NULL;
  345. Form_pg_attribute *attr;
  346. attr_cnt = onerel->rd_att->natts;
  347. attr = onerel->rd_att->attrs;
  348. if (va_cols != NIL)
  349. {
  350. int tcnt = 0;
  351. List    *le;
  352. if (length(va_cols) > attr_cnt)
  353. elog(ERROR, "vacuum: too many attributes specified for relation %s",
  354.  (RelationGetRelationName(onerel))->data);
  355. attnums = (int *) palloc(attr_cnt * sizeof(int));
  356. foreach(le, va_cols)
  357. {
  358. char    *col = (char *) lfirst(le);
  359. for (i = 0; i < attr_cnt; i++)
  360. {
  361. if (namestrcmp(&(attr[i]->attname), col) == 0)
  362. break;
  363. }
  364. if (i < attr_cnt) /* found */
  365. attnums[tcnt++] = i;
  366. else
  367. {
  368. elog(ERROR, "vacuum: there is no attribute %s in %s",
  369.  col, (RelationGetRelationName(onerel))->data);
  370. }
  371. }
  372. attr_cnt = tcnt;
  373. }
  374. vacrelstats->vacattrstats = (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats));
  375. for (i = 0; i < attr_cnt; i++)
  376. {
  377. Operator func_operator;
  378. Form_pg_operator pgopform;
  379. VacAttrStats *stats;
  380. stats = &vacrelstats->vacattrstats[i];
  381. stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE);
  382. memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE);
  383. stats->best = stats->guess1 = stats->guess2 = 0;
  384. stats->max = stats->min = 0;
  385. stats->best_len = stats->guess1_len = stats->guess2_len = 0;
  386. stats->max_len = stats->min_len = 0;
  387. stats->initialized = false;
  388. stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0;
  389. stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0;
  390. func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true);
  391. if (func_operator != NULL)
  392. {
  393. pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
  394. fmgr_info(pgopform->oprcode, &(stats->f_cmpeq));
  395. }
  396. else
  397. stats->f_cmpeq.fn_addr = NULL;
  398. func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true);
  399. if (func_operator != NULL)
  400. {
  401. pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
  402. fmgr_info(pgopform->oprcode, &(stats->f_cmplt));
  403. }
  404. else
  405. stats->f_cmplt.fn_addr = NULL;
  406. func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true);
  407. if (func_operator != NULL)
  408. {
  409. pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
  410. fmgr_info(pgopform->oprcode, &(stats->f_cmpgt));
  411. }
  412. else
  413. stats->f_cmpgt.fn_addr = NULL;
  414. typetuple = SearchSysCacheTuple(TYPOID,
  415.  ObjectIdGetDatum(stats->attr->atttypid),
  416. 0, 0, 0);
  417. if (HeapTupleIsValid(typetuple))
  418. stats->outfunc = ((Form_pg_type) GETSTRUCT(typetuple))->typoutput;
  419. else
  420. stats->outfunc = InvalidOid;
  421. }
  422. vacrelstats->va_natts = attr_cnt;
  423. vc_delhilowstats(relid, ((attnums) ? attr_cnt : 0), attnums);
  424. if (attnums)
  425. pfree(attnums);
  426. }
  427. else
  428. {
  429. vacrelstats->va_natts = 0;
  430. vacrelstats->vacattrstats = (VacAttrStats *) NULL;
  431. }
  432. /* we require the relation to be locked until the indices are cleaned */
  433. LockRelation(onerel, AccessExclusiveLock);
  434. GetXmaxRecent(&XmaxRecent);
  435. /* scan it */
  436. vacuum_pages.vpl_num_pages = fraged_pages.vpl_num_pages = 0;
  437. vc_scanheap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);
  438. /* Now open indices */
  439. Irel = (Relation *) NULL;
  440. vc_getindices(vacrelstats->relid, &nindices, &Irel);
  441. if (nindices > 0)
  442. vacrelstats->hasindex = true;
  443. else
  444. vacrelstats->hasindex = false;
  445. /* Clean/scan index relation(s) */
  446. if (Irel != (Relation *) NULL)
  447. {
  448. if (vacuum_pages.vpl_num_pages > 0)
  449. {
  450. for (i = 0; i < nindices; i++)
  451. vc_vaconeind(&vacuum_pages, Irel[i], vacrelstats->num_tuples, 0);
  452. }
  453. else
  454. /* just scan indices to update statistic */
  455. {
  456. for (i = 0; i < nindices; i++)
  457. vc_scanoneind(Irel[i], vacrelstats->num_tuples);
  458. }
  459. }
  460. if (fraged_pages.vpl_num_pages > 0) /* Try to shrink heap */
  461. vc_rpfheap(vacrelstats, onerel, &vacuum_pages, &fraged_pages, nindices, Irel);
  462. else
  463. {
  464. if (Irel != (Relation *) NULL)
  465. vc_clsindices(nindices, Irel);
  466. if (vacuum_pages.vpl_num_pages > 0) /* Clean pages from
  467.  * vacuum_pages list */
  468. vc_vacheap(vacrelstats, onerel, &vacuum_pages);
  469. }
  470. /* ok - free vacuum_pages list of reapped pages */
  471. if (vacuum_pages.vpl_num_pages > 0)
  472. {
  473. vpp = vacuum_pages.vpl_pagedesc;
  474. for (i = 0; i < vacuum_pages.vpl_num_pages; i++, vpp++)
  475. pfree(*vpp);
  476. pfree(vacuum_pages.vpl_pagedesc);
  477. if (fraged_pages.vpl_num_pages > 0)
  478. pfree(fraged_pages.vpl_pagedesc);
  479. }
  480. /* all done with this class */
  481. heap_close(onerel);
  482. /* update statistics in pg_class */
  483. vc_updstats(vacrelstats->relid, vacrelstats->num_pages,
  484. vacrelstats->num_tuples, vacrelstats->hasindex, vacrelstats);
  485. /* next command frees attribute stats */
  486. CommitTransactionCommand();
  487. }
  488. /*
  489.  * vc_scanheap() -- scan an open heap relation
  490.  *
  491.  * This routine sets commit times, constructs vacuum_pages list of
  492.  * empty/uninitialized pages and pages with dead tuples and
  493.  * ~LP_USED line pointers, constructs fraged_pages list of pages
  494.  * appropriate for purposes of shrinking and maintains statistics
  495.  * on the number of live tuples in a heap.
  496.  */
  497. static void
  498. vc_scanheap(VRelStats *vacrelstats, Relation onerel,
  499. VPageList vacuum_pages, VPageList fraged_pages)
  500. {
  501. int nblocks,
  502. blkno;
  503. ItemId itemid;
  504. Buffer buf;
  505. HeapTupleData tuple;
  506. Page page,
  507. tempPage = NULL;
  508. OffsetNumber offnum,
  509. maxoff;
  510. bool pgchanged,
  511. tupgone,
  512. dobufrel,
  513. notup;
  514. char    *relname;
  515. VPageDescr vpc,
  516. vp;
  517. uint32 tups_vacuumed,
  518. num_tuples,
  519. nkeep,
  520. nunused,
  521. ncrash,
  522. empty_pages,
  523. new_pages,
  524. changed_pages,
  525. empty_end_pages;
  526. Size free_size,
  527. usable_free_size;
  528. Size min_tlen = MaxTupleSize;
  529. Size max_tlen = 0;
  530. int32 i;
  531. struct rusage ru0,
  532. ru1;
  533. bool do_shrinking = true;
  534. VTupleLink vtlinks = (VTupleLink) palloc(100 * sizeof(VTupleLinkData));
  535. int num_vtlinks = 0;
  536. int free_vtlinks = 100;
  537. getrusage(RUSAGE_SELF, &ru0);
  538. relname = (RelationGetRelationName(onerel))->data;
  539. elog(MESSAGE_LEVEL, "--Relation %s--", relname);
  540. tups_vacuumed = num_tuples = nkeep = nunused = ncrash = empty_pages =
  541. new_pages = changed_pages = empty_end_pages = 0;
  542. free_size = usable_free_size = 0;
  543. nblocks = RelationGetNumberOfBlocks(onerel);
  544. vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber));
  545. vpc->vpd_offsets_used = 0;
  546. for (blkno = 0; blkno < nblocks; blkno++)
  547. {
  548. buf = ReadBuffer(onerel, blkno);
  549. page = BufferGetPage(buf);
  550. vpc->vpd_blkno = blkno;
  551. vpc->vpd_offsets_free = 0;
  552. if (PageIsNew(page))
  553. {
  554. elog(NOTICE, "Rel %s: Uninitialized page %u - fixing",
  555.  relname, blkno);
  556. PageInit(page, BufferGetPageSize(buf), 0);
  557. vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower;
  558. free_size += (vpc->vpd_free - sizeof(ItemIdData));
  559. new_pages++;
  560. empty_end_pages++;
  561. vc_reappage(vacuum_pages, vpc);
  562. WriteBuffer(buf);
  563. continue;
  564. }
  565. if (PageIsEmpty(page))
  566. {
  567. vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower;
  568. free_size += (vpc->vpd_free - sizeof(ItemIdData));
  569. empty_pages++;
  570. empty_end_pages++;
  571. vc_reappage(vacuum_pages, vpc);
  572. ReleaseBuffer(buf);
  573. continue;
  574. }
  575. pgchanged = false;
  576. notup = true;
  577. maxoff = PageGetMaxOffsetNumber(page);
  578. for (offnum = FirstOffsetNumber;
  579.  offnum <= maxoff;
  580.  offnum = OffsetNumberNext(offnum))
  581. {
  582. itemid = PageGetItemId(page, offnum);
  583. /*
  584.  * Collect un-used items too - it's possible to have indices
  585.  * pointing here after crash.
  586.  */
  587. if (!ItemIdIsUsed(itemid))
  588. {
  589. vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum;
  590. nunused++;
  591. continue;
  592. }
  593. tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
  594. tuple.t_len = ItemIdGetLength(itemid);
  595. ItemPointerSet(&(tuple.t_self), blkno, offnum);
  596. tupgone = false;
  597. if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
  598. {
  599. if (tuple.t_data->t_infomask & HEAP_XMIN_INVALID)
  600. tupgone = true;
  601. else if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
  602. {
  603. if (TransactionIdDidCommit((TransactionId)
  604.    tuple.t_data->t_cmin))
  605. {
  606. tuple.t_data->t_infomask |= HEAP_XMIN_INVALID;
  607. tupgone = true;
  608. }
  609. else
  610. {
  611. tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
  612. pgchanged = true;
  613. }
  614. }
  615. else if (tuple.t_data->t_infomask & HEAP_MOVED_IN)
  616. {
  617. if (!TransactionIdDidCommit((TransactionId)
  618. tuple.t_data->t_cmin))
  619. {
  620. tuple.t_data->t_infomask |= HEAP_XMIN_INVALID;
  621. tupgone = true;
  622. }
  623. else
  624. {
  625. tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
  626. pgchanged = true;
  627. }
  628. }
  629. else
  630. {
  631. if (TransactionIdDidAbort(tuple.t_data->t_xmin))
  632. tupgone = true;
  633. else if (TransactionIdDidCommit(tuple.t_data->t_xmin))
  634. {
  635. tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
  636. pgchanged = true;
  637. }
  638. else if (!TransactionIdIsInProgress(tuple.t_data->t_xmin))
  639. {
  640. /*
  641.  * Not Aborted, Not Committed, Not in Progress -
  642.  * so it's from crashed process. - vadim 11/26/96
  643.  */
  644. ncrash++;
  645. tupgone = true;
  646. }
  647. else
  648. {
  649. elog(NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation",
  650.    relname, blkno, offnum, tuple.t_data->t_xmin);
  651. do_shrinking = false;
  652. }
  653. }
  654. }
  655. /*
  656.  * here we are concerned about tuples with xmin committed and
  657.  * xmax unknown or committed
  658.  */
  659. if (tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED &&
  660. !(tuple.t_data->t_infomask & HEAP_XMAX_INVALID))
  661. {
  662. if (tuple.t_data->t_infomask & HEAP_XMAX_COMMITTED)
  663. {
  664. if (tuple.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
  665. {
  666. pgchanged = true;
  667. tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
  668. }
  669. else
  670. tupgone = true;
  671. }
  672. else if (TransactionIdDidAbort(tuple.t_data->t_xmax))
  673. {
  674. tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
  675. pgchanged = true;
  676. }
  677. else if (TransactionIdDidCommit(tuple.t_data->t_xmax))
  678. {
  679. if (tuple.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
  680. {
  681. tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
  682. pgchanged = true;
  683. }
  684. else
  685. tupgone = true;
  686. }
  687. else if (!TransactionIdIsInProgress(tuple.t_data->t_xmax))
  688. {
  689. /*
  690.  * Not Aborted, Not Committed, Not in Progress - so it
  691.  * from crashed process. - vadim 06/02/97
  692.  */
  693. tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
  694. pgchanged = true;
  695. }
  696. else
  697. {
  698. elog(NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation",
  699.  relname, blkno, offnum, tuple.t_data->t_xmax);
  700. do_shrinking = false;
  701. }
  702. /*
  703.  * If tuple is recently deleted then we must not remove it
  704.  * from relation.
  705.  */
  706. if (tupgone && tuple.t_data->t_xmax >= XmaxRecent)
  707. {
  708. tupgone = false;
  709. nkeep++;
  710. if (!(tuple.t_data->t_infomask & HEAP_XMAX_COMMITTED))
  711. {
  712. tuple.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
  713. pgchanged = true;
  714. }
  715. /*
  716.  * If we do shrinking and this tuple is updated one
  717.  * then remember it to construct updated tuple
  718.  * dependencies.
  719.  */
  720. if (do_shrinking && !(ItemPointerEquals(&(tuple.t_self),
  721.    &(tuple.t_data->t_ctid))))
  722. {
  723. if (free_vtlinks == 0)
  724. {
  725. free_vtlinks = 1000;
  726. vtlinks = (VTupleLink) repalloc(vtlinks,
  727.    (free_vtlinks + num_vtlinks) *
  728.  sizeof(VTupleLinkData));
  729. }
  730. vtlinks[num_vtlinks].new_tid = tuple.t_data->t_ctid;
  731. vtlinks[num_vtlinks].this_tid = tuple.t_self;
  732. free_vtlinks--;
  733. num_vtlinks++;
  734. }
  735. }
  736. }
  737. /*
  738.  * Other checks...
  739.  */
  740. if (!OidIsValid(tuple.t_data->t_oid))
  741. {
  742. elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.",
  743.  relname, blkno, offnum, tupgone);
  744. }
  745. if (tupgone)
  746. {
  747. ItemId lpp;
  748. if (tempPage == (Page) NULL)
  749. {
  750. Size pageSize;
  751. pageSize = PageGetPageSize(page);
  752. tempPage = (Page) palloc(pageSize);
  753. memmove(tempPage, page, pageSize);
  754. }
  755. lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]);
  756. /* mark it unused */
  757. lpp->lp_flags &= ~LP_USED;
  758. vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum;
  759. tups_vacuumed++;
  760. }
  761. else
  762. {
  763. num_tuples++;
  764. notup = false;
  765. if (tuple.t_len < min_tlen)
  766. min_tlen = tuple.t_len;
  767. if (tuple.t_len > max_tlen)
  768. max_tlen = tuple.t_len;
  769. vc_attrstats(onerel, vacrelstats, &tuple);
  770. }
  771. }
  772. if (pgchanged)
  773. {
  774. WriteBuffer(buf);
  775. dobufrel = false;
  776. changed_pages++;
  777. }
  778. else
  779. dobufrel = true;
  780. if (tempPage != (Page) NULL)
  781. { /* Some tuples are gone */
  782. PageRepairFragmentation(tempPage);
  783. vpc->vpd_free = ((PageHeader) tempPage)->pd_upper - ((PageHeader) tempPage)->pd_lower;
  784. free_size += vpc->vpd_free;
  785. vc_reappage(vacuum_pages, vpc);
  786. pfree(tempPage);
  787. tempPage = (Page) NULL;
  788. }
  789. else if (vpc->vpd_offsets_free > 0)
  790. { /* there are only ~LP_USED line pointers */
  791. vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower;
  792. free_size += vpc->vpd_free;
  793. vc_reappage(vacuum_pages, vpc);
  794. }
  795. if (dobufrel)
  796. ReleaseBuffer(buf);
  797. if (notup)
  798. empty_end_pages++;
  799. else
  800. empty_end_pages = 0;
  801. }
  802. pfree(vpc);
  803. /* save stats in the rel list for use later */
  804. vacrelstats->num_tuples = num_tuples;
  805. vacrelstats->num_pages = nblocks;
  806. /*   vacrelstats->natts = attr_cnt;*/
  807. if (num_tuples == 0)
  808. min_tlen = max_tlen = 0;
  809. vacrelstats->min_tlen = min_tlen;
  810. vacrelstats->max_tlen = max_tlen;
  811. vacuum_pages->vpl_empty_end_pages = empty_end_pages;
  812. fraged_pages->vpl_empty_end_pages = empty_end_pages;
  813. /*
  814.  * Try to make fraged_pages keeping in mind that we can't use free
  815.  * space of "empty" end-pages and last page if it reapped.
  816.  */
  817. if (do_shrinking && vacuum_pages->vpl_num_pages - empty_end_pages > 0)
  818. {
  819. int nusf; /* blocks usefull for re-using */
  820. nusf = vacuum_pages->vpl_num_pages - empty_end_pages;
  821. if ((vacuum_pages->vpl_pagedesc[nusf - 1])->vpd_blkno == nblocks - empty_end_pages - 1)
  822. nusf--;
  823. for (i = 0; i < nusf; i++)
  824. {
  825. vp = vacuum_pages->vpl_pagedesc[i];
  826. if (vc_enough_space(vp, min_tlen))
  827. {
  828. vc_vpinsert(fraged_pages, vp);
  829. usable_free_size += vp->vpd_free;
  830. }
  831. }
  832. }
  833. if (usable_free_size > 0 && num_vtlinks > 0)
  834. {
  835. qsort((char *) vtlinks, num_vtlinks, sizeof(VTupleLinkData),
  836.   vc_cmp_vtlinks);
  837. vacrelstats->vtlinks = vtlinks;
  838. vacrelstats->num_vtlinks = num_vtlinks;
  839. }
  840. else
  841. {
  842. vacrelstats->vtlinks = NULL;
  843. vacrelstats->num_vtlinks = 0;
  844. pfree(vtlinks);
  845. }
  846. getrusage(RUSAGE_SELF, &ru1);
  847. elog(MESSAGE_LEVEL, "Pages %u: Changed %u, Reapped %u, Empty %u, New %u; 
  848. Tup %u: Vac %u, Keep/VTL %u/%u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; 
  849. Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. 
  850. Elapsed %u/%u sec.",
  851.  nblocks, changed_pages, vacuum_pages->vpl_num_pages, empty_pages,
  852.  new_pages, num_tuples, tups_vacuumed,
  853.  nkeep, vacrelstats->num_vtlinks, ncrash,
  854.  nunused, min_tlen, max_tlen, free_size, usable_free_size,
  855.  empty_end_pages, fraged_pages->vpl_num_pages,
  856.  ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
  857.  ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
  858. } /* vc_scanheap */
  859. /*
  860.  * vc_rpfheap() -- try to repaire relation' fragmentation
  861.  *
  862.  * This routine marks dead tuples as unused and tries re-use dead space
  863.  * by moving tuples (and inserting indices if needed). It constructs
  864.  * Nvpl list of free-ed pages (moved tuples) and clean indices
  865.  * for them after committing (in hack-manner - without losing locks
  866.  * and freeing memory!) current transaction. It truncates relation
  867.  * if some end-blocks are gone away.
  868.  */
  869. static void
  870. vc_rpfheap(VRelStats *vacrelstats, Relation onerel,
  871.    VPageList vacuum_pages, VPageList fraged_pages, int nindices, Relation *Irel)
  872. {
  873. TransactionId myXID;
  874. CommandId myCID;
  875. Buffer buf,
  876. cur_buffer;
  877. int nblocks,
  878. blkno;
  879. Page page,
  880. ToPage = NULL;
  881. OffsetNumber offnum = 0,
  882. maxoff = 0,
  883. newoff,
  884. max_offset;
  885. ItemId itemid,
  886. newitemid;
  887. HeapTupleData tuple,
  888. newtup;
  889. TupleDesc tupdesc = NULL;
  890. Datum    *idatum = NULL;
  891. char    *inulls = NULL;
  892. InsertIndexResult iresult;
  893. VPageListData Nvpl;
  894. VPageDescr cur_page = NULL,
  895. last_fraged_page,
  896. last_vacuum_page,
  897. vpc,
  898.    *vpp;
  899. int cur_item = 0;
  900. IndDesc    *Idesc,
  901.    *idcur;
  902. int last_fraged_block,
  903. last_vacuum_block,
  904. i = 0;
  905. Size tuple_len;
  906. int num_moved,
  907. num_fraged_pages,
  908. vacuumed_pages;
  909. int checked_moved,
  910. num_tuples,
  911. keep_tuples = 0;
  912. bool isempty,
  913. dowrite,
  914. chain_tuple_moved;
  915. struct rusage ru0,
  916. ru1;
  917. getrusage(RUSAGE_SELF, &ru0);
  918. myXID = GetCurrentTransactionId();
  919. myCID = GetCurrentCommandId();
  920. if (Irel != (Relation *) NULL) /* preparation for index' inserts */
  921. {
  922. vc_mkindesc(onerel, nindices, Irel, &Idesc);
  923. tupdesc = RelationGetDescr(onerel);
  924. idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof(*idatum));
  925. inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof(*inulls));
  926. }
  927. Nvpl.vpl_num_pages = 0;
  928. num_fraged_pages = fraged_pages->vpl_num_pages;
  929. last_fraged_page = fraged_pages->vpl_pagedesc[num_fraged_pages - 1];
  930. last_fraged_block = last_fraged_page->vpd_blkno;
  931. Assert(vacuum_pages->vpl_num_pages > vacuum_pages->vpl_empty_end_pages);
  932. vacuumed_pages = vacuum_pages->vpl_num_pages - vacuum_pages->vpl_empty_end_pages;
  933. last_vacuum_page = vacuum_pages->vpl_pagedesc[vacuumed_pages - 1];
  934. last_vacuum_block = last_vacuum_page->vpd_blkno;
  935. Assert(last_vacuum_block >= last_fraged_block);
  936. cur_buffer = InvalidBuffer;
  937. num_moved = 0;
  938. vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber));
  939. vpc->vpd_offsets_used = vpc->vpd_offsets_free = 0;
  940. nblocks = vacrelstats->num_pages;
  941. for (blkno = nblocks - vacuum_pages->vpl_empty_end_pages - 1;; blkno--)
  942. {
  943. /* if it's reapped page and it was used by me - quit */
  944. if (blkno == last_fraged_block && last_fraged_page->vpd_offsets_used > 0)
  945. break;
  946. buf = ReadBuffer(onerel, blkno);
  947. page = BufferGetPage(buf);
  948. vpc->vpd_offsets_free = 0;
  949. isempty = PageIsEmpty(page);
  950. dowrite = false;
  951. if (blkno == last_vacuum_block) /* it's reapped page */
  952. {
  953. if (last_vacuum_page->vpd_offsets_free > 0) /* there are dead tuples */
  954. { /* on this page - clean */
  955. Assert(!isempty);
  956. vc_vacpage(page, last_vacuum_page);
  957. dowrite = true;
  958. }
  959. else
  960. Assert(isempty);
  961. --vacuumed_pages;
  962. Assert(vacuumed_pages > 0);
  963. /* get prev reapped page from vacuum_pages */
  964. last_vacuum_page = vacuum_pages->vpl_pagedesc[vacuumed_pages - 1];
  965. last_vacuum_block = last_vacuum_page->vpd_blkno;
  966. if (blkno == last_fraged_block) /* this page in
  967.  * fraged_pages too */
  968. {
  969. --num_fraged_pages;
  970. Assert(num_fraged_pages > 0);
  971. Assert(last_fraged_page->vpd_offsets_used == 0);
  972. /* get prev reapped page from fraged_pages */
  973. last_fraged_page = fraged_pages->vpl_pagedesc[num_fraged_pages - 1];
  974. last_fraged_block = last_fraged_page->vpd_blkno;
  975. }
  976. Assert(last_fraged_block <= last_vacuum_block);
  977. if (isempty)
  978. {
  979. ReleaseBuffer(buf);
  980. continue;
  981. }
  982. }
  983. else
  984. Assert(!isempty);
  985. chain_tuple_moved = false; /* no one chain-tuple was moved
  986.  * off this page, yet */
  987. vpc->vpd_blkno = blkno;
  988. maxoff = PageGetMaxOffsetNumber(page);
  989. for (offnum = FirstOffsetNumber;
  990.  offnum <= maxoff;
  991.  offnum = OffsetNumberNext(offnum))
  992. {
  993. itemid = PageGetItemId(page, offnum);
  994. if (!ItemIdIsUsed(itemid))
  995. continue;
  996. tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
  997. tuple_len = tuple.t_len = ItemIdGetLength(itemid);
  998. ItemPointerSet(&(tuple.t_self), blkno, offnum);
  999. if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
  1000. {
  1001. if ((TransactionId) tuple.t_data->t_cmin != myXID)
  1002. elog(ERROR, "Invalid XID in t_cmin");
  1003. if (tuple.t_data->t_infomask & HEAP_MOVED_IN)
  1004. elog(ERROR, "HEAP_MOVED_IN was not expected");
  1005. /*
  1006.  * If this (chain) tuple is moved by me already then I
  1007.  * have to check is it in vpc or not - i.e. is it moved
  1008.  * while cleaning this page or some previous one.
  1009.  */
  1010. if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
  1011. {
  1012. if (keep_tuples == 0)
  1013. continue;
  1014. if (chain_tuple_moved) /* some chains was moved
  1015.  * while */
  1016. { /* cleaning this page */
  1017. Assert(vpc->vpd_offsets_free > 0);
  1018. for (i = 0; i < vpc->vpd_offsets_free; i++)
  1019. {
  1020. if (vpc->vpd_offsets[i] == offnum)
  1021. break;
  1022. }
  1023. if (i >= vpc->vpd_offsets_free) /* not found */
  1024. {
  1025. vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum;
  1026. keep_tuples--;
  1027. }
  1028. }
  1029. else
  1030. {
  1031. vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum;
  1032. keep_tuples--;
  1033. }
  1034. continue;
  1035. }
  1036. elog(ERROR, "HEAP_MOVED_OFF was expected");
  1037. }
  1038. /*
  1039.  * If this tuple is in the chain of tuples created in updates
  1040.  * by "recent" transactions then we have to move all chain of
  1041.  * tuples to another places.
  1042.  */
  1043. if ((tuple.t_data->t_infomask & HEAP_UPDATED &&
  1044.  tuple.t_data->t_xmin >= XmaxRecent) ||
  1045. (!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) &&
  1046.  !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid)))))
  1047. {
  1048. Buffer Cbuf = buf;
  1049. Page Cpage;
  1050. ItemId Citemid;
  1051. ItemPointerData Ctid;
  1052. HeapTupleData tp = tuple;
  1053. Size tlen = tuple_len;
  1054. VTupleMove vtmove = (VTupleMove)
  1055. palloc(100 * sizeof(VTupleMoveData));
  1056. int num_vtmove = 0;
  1057. int free_vtmove = 100;
  1058. VPageDescr to_vpd = fraged_pages->vpl_pagedesc[0];
  1059. int to_item = 0;
  1060. bool freeCbuf = false;
  1061. int ti;
  1062. if (vacrelstats->vtlinks == NULL)
  1063. elog(ERROR, "No one parent tuple was found");
  1064. if (cur_buffer != InvalidBuffer)
  1065. {
  1066. WriteBuffer(cur_buffer);
  1067. cur_buffer = InvalidBuffer;
  1068. }
  1069. /*
  1070.  * If this tuple is in the begin/middle of the chain then
  1071.  * we have to move to the end of chain.
  1072.  */
  1073. while (!(tp.t_data->t_infomask & HEAP_XMAX_INVALID) &&
  1074. !(ItemPointerEquals(&(tp.t_self), &(tp.t_data->t_ctid))))
  1075. {
  1076. Ctid = tp.t_data->t_ctid;
  1077. if (freeCbuf)
  1078. ReleaseBuffer(Cbuf);
  1079. freeCbuf = true;
  1080. Cbuf = ReadBuffer(onerel,
  1081.   ItemPointerGetBlockNumber(&Ctid));
  1082. Cpage = BufferGetPage(Cbuf);
  1083. Citemid = PageGetItemId(Cpage,
  1084.   ItemPointerGetOffsetNumber(&Ctid));
  1085. if (!ItemIdIsUsed(Citemid))
  1086. {
  1087. /*
  1088.  * This means that in the middle of chain there was
  1089.  * tuple updated by older (than XmaxRecent) xaction
  1090.  * and this tuple is already deleted by me. Actually,
  1091.  * upper part of chain should be removed and seems 
  1092.  * that this should be handled in vc_scanheap(), but
  1093.  * it's not implemented at the moment and so we
  1094.  * just stop shrinking here.
  1095.  */
  1096. ReleaseBuffer(Cbuf);
  1097. pfree(vtmove);
  1098. vtmove = NULL;
  1099. elog(NOTICE, "Child itemid in update-chain marked as unused - can't continue vc_rpfheap");
  1100. break;
  1101. }
  1102. tp.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);
  1103. tp.t_self = Ctid;
  1104. tlen = tp.t_len = ItemIdGetLength(Citemid);
  1105. }
  1106. if (vtmove == NULL)
  1107. break;
  1108. /* first, can chain be moved ? */
  1109. for (;;)
  1110. {
  1111. if (!vc_enough_space(to_vpd, tlen))
  1112. {
  1113. if (to_vpd != last_fraged_page &&
  1114.  !vc_enough_space(to_vpd, vacrelstats->min_tlen))
  1115. {
  1116. Assert(num_fraged_pages > to_item + 1);
  1117. memmove(fraged_pages->vpl_pagedesc + to_item,
  1118. fraged_pages->vpl_pagedesc + to_item + 1,
  1119. sizeof(VPageDescr *) * (num_fraged_pages - to_item - 1));
  1120. num_fraged_pages--;
  1121. Assert(last_fraged_page == fraged_pages->vpl_pagedesc[num_fraged_pages - 1]);
  1122. }
  1123. for (i = 0; i < num_fraged_pages; i++)
  1124. {
  1125. if (vc_enough_space(fraged_pages->vpl_pagedesc[i], tlen))
  1126. break;
  1127. }
  1128. if (i == num_fraged_pages) /* can't move item
  1129.  * anywhere */
  1130. {
  1131. for (i = 0; i < num_vtmove; i++)
  1132. {
  1133. Assert(vtmove[i].vpd->vpd_offsets_used > 0);
  1134. (vtmove[i].vpd->vpd_offsets_used)--;
  1135. }
  1136. num_vtmove = 0;
  1137. break;
  1138. }
  1139. to_item = i;
  1140. to_vpd = fraged_pages->vpl_pagedesc[to_item];
  1141. }
  1142. to_vpd->vpd_free -= MAXALIGN(tlen);
  1143. if (to_vpd->vpd_offsets_used >= to_vpd->vpd_offsets_free)
  1144. to_vpd->vpd_free -= MAXALIGN(sizeof(ItemIdData));
  1145. (to_vpd->vpd_offsets_used)++;
  1146. if (free_vtmove == 0)
  1147. {
  1148. free_vtmove = 1000;
  1149. vtmove = (VTupleMove) repalloc(vtmove,
  1150.  (free_vtmove + num_vtmove) *
  1151.  sizeof(VTupleMoveData));
  1152. }
  1153. vtmove[num_vtmove].tid = tp.t_self;
  1154. vtmove[num_vtmove].vpd = to_vpd;
  1155. if (to_vpd->vpd_offsets_used == 1)
  1156. vtmove[num_vtmove].cleanVpd = true;
  1157. else
  1158. vtmove[num_vtmove].cleanVpd = false;
  1159. free_vtmove--;
  1160. num_vtmove++;
  1161. /*
  1162.  * All done ?
  1163.  */
  1164. if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
  1165. tp.t_data->t_xmin < XmaxRecent)
  1166. break;
  1167. /*
  1168.  * Well, try to find tuple with old row version
  1169.  */
  1170. for (;;)
  1171. {
  1172. Buffer Pbuf;
  1173. Page Ppage;
  1174. ItemId Pitemid;
  1175. HeapTupleData Ptp;
  1176. VTupleLinkData vtld,
  1177.    *vtlp;
  1178. vtld.new_tid = tp.t_self;
  1179. vtlp = (VTupleLink)
  1180. vc_find_eq((void *) (vacrelstats->vtlinks),
  1181.    vacrelstats->num_vtlinks,
  1182.    sizeof(VTupleLinkData),
  1183.    (void *) &vtld,
  1184.    vc_cmp_vtlinks);
  1185. if (vtlp == NULL)
  1186. elog(ERROR, "Parent tuple was not found");
  1187. tp.t_self = vtlp->this_tid;
  1188. Pbuf = ReadBuffer(onerel,
  1189. ItemPointerGetBlockNumber(&(tp.t_self)));
  1190. Ppage = BufferGetPage(Pbuf);
  1191. Pitemid = PageGetItemId(Ppage,
  1192.    ItemPointerGetOffsetNumber(&(tp.t_self)));
  1193. if (!ItemIdIsUsed(Pitemid))
  1194. elog(ERROR, "Parent itemid marked as unused");
  1195. Ptp.t_data = (HeapTupleHeader) PageGetItem(Ppage, Pitemid);
  1196. Assert(ItemPointerEquals(&(vtld.new_tid),
  1197. &(Ptp.t_data->t_ctid)));
  1198. /*
  1199.  * Read above about cases when !ItemIdIsUsed(Citemid)
  1200.  * (child item is removed)... Due to the fact that
  1201.  * at the moment we don't remove unuseful part of 
  1202.  * update-chain, it's possible to get too old
  1203.  * parent row here. Like as in the case which 
  1204.  * caused this problem, we stop shrinking here.
  1205.  * I could try to find real parent row but want
  1206.  * not to do it because of real solution will
  1207.  * be implemented anyway, latter, and we are too
  1208.  * close to 6.5 release. - vadim 06/11/99
  1209.  */
  1210. if (Ptp.t_data->t_xmax != tp.t_data->t_xmin)
  1211. {
  1212. if (freeCbuf)
  1213. ReleaseBuffer(Cbuf);
  1214. freeCbuf = false;
  1215. ReleaseBuffer(Pbuf);
  1216. for (i = 0; i < num_vtmove; i++)
  1217. {
  1218. Assert(vtmove[i].vpd->vpd_offsets_used > 0);
  1219. (vtmove[i].vpd->vpd_offsets_used)--;
  1220. }
  1221. num_vtmove = 0;
  1222. elog(NOTICE, "Too old parent tuple found - can't continue vc_rpfheap");
  1223. break;
  1224. }
  1225. #ifdef NOT_USED /* I'm not sure that this will wotk properly... */
  1226. /*
  1227.  * If this tuple is updated version of row and it
  1228.  * was created by the same transaction then no one
  1229.  * is interested in this tuple - mark it as
  1230.  * removed.
  1231.  */
  1232. if (Ptp.t_data->t_infomask & HEAP_UPDATED &&
  1233. Ptp.t_data->t_xmin == Ptp.t_data->t_xmax)
  1234. {
  1235. TransactionIdStore(myXID,
  1236. (TransactionId *) &(Ptp.t_data->t_cmin));
  1237. Ptp.t_data->t_infomask &=
  1238. ~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
  1239. Ptp.t_data->t_infomask |= HEAP_MOVED_OFF;
  1240. WriteBuffer(Pbuf);
  1241. continue;
  1242. }
  1243. #endif
  1244. tp.t_data = Ptp.t_data;
  1245. tlen = tp.t_len = ItemIdGetLength(Pitemid);
  1246. if (freeCbuf)
  1247. ReleaseBuffer(Cbuf);
  1248. Cbuf = Pbuf;
  1249. freeCbuf = true;
  1250. break;
  1251. }
  1252. if (num_vtmove == 0)
  1253. break;
  1254. }
  1255. if (freeCbuf)
  1256. ReleaseBuffer(Cbuf);
  1257. if (num_vtmove == 0) /* chain can't be moved */
  1258. {
  1259. pfree(vtmove);
  1260. break;
  1261. }
  1262. ItemPointerSetInvalid(&Ctid);
  1263. for (ti = 0; ti < num_vtmove; ti++)
  1264. {
  1265. /* Get tuple from chain */
  1266. tuple.t_self = vtmove[ti].tid;
  1267. Cbuf = ReadBuffer(onerel,
  1268.  ItemPointerGetBlockNumber(&(tuple.t_self)));
  1269. Cpage = BufferGetPage(Cbuf);
  1270. Citemid = PageGetItemId(Cpage,
  1271. ItemPointerGetOffsetNumber(&(tuple.t_self)));
  1272. tuple.t_data = (HeapTupleHeader) PageGetItem(Cpage, Citemid);
  1273. tuple_len = tuple.t_len = ItemIdGetLength(Citemid);
  1274. /* Get page to move in */
  1275. cur_buffer = ReadBuffer(onerel, vtmove[ti].vpd->vpd_blkno);
  1276. /*
  1277.  * We should LockBuffer(cur_buffer) but don't, at the
  1278.  * moment. If you'll do LockBuffer then UNLOCK it
  1279.  * before index_insert: unique btree-s call heap_fetch
  1280.  * to get t_infomask of inserted heap tuple !!!
  1281.  */
  1282. ToPage = BufferGetPage(cur_buffer);
  1283. /* if this page was not used before - clean it */
  1284. if (!PageIsEmpty(ToPage) && vtmove[ti].cleanVpd)
  1285. vc_vacpage(ToPage, vtmove[ti].vpd);
  1286. heap_copytuple_with_tuple(&tuple, &newtup);
  1287. RelationInvalidateHeapTuple(onerel, &tuple);
  1288. TransactionIdStore(myXID, (TransactionId *) &(newtup.t_data->t_cmin));
  1289. newtup.t_data->t_infomask &=
  1290. ~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_OFF);
  1291. newtup.t_data->t_infomask |= HEAP_MOVED_IN;
  1292. newoff = PageAddItem(ToPage, (Item) newtup.t_data, tuple_len,
  1293.  InvalidOffsetNumber, LP_USED);
  1294. if (newoff == InvalidOffsetNumber)
  1295. {
  1296. elog(ERROR, "
  1297. moving chain: failed to add item with len = %u to page %u",
  1298.  tuple_len, vtmove[ti].vpd->vpd_blkno);
  1299. }
  1300. newitemid = PageGetItemId(ToPage, newoff);
  1301. pfree(newtup.t_data);
  1302. newtup.t_data = (HeapTupleHeader) PageGetItem(ToPage, newitemid);
  1303. ItemPointerSet(&(newtup.t_self), vtmove[ti].vpd->vpd_blkno, newoff);
  1304. /*
  1305.  * Set t_ctid pointing to itself for last tuple in
  1306.  * chain and to next tuple in chain otherwise.
  1307.  */
  1308. if (!ItemPointerIsValid(&Ctid))
  1309. newtup.t_data->t_ctid = newtup.t_self;
  1310. else
  1311. newtup.t_data->t_ctid = Ctid;
  1312. Ctid = newtup.t_self;
  1313. TransactionIdStore(myXID, (TransactionId *) &(tuple.t_data->t_cmin));
  1314. tuple.t_data->t_infomask &=
  1315. ~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
  1316. tuple.t_data->t_infomask |= HEAP_MOVED_OFF;
  1317. num_moved++;
  1318. /*
  1319.  * Remember that we moved tuple from the current page
  1320.  * (corresponding index tuple will be cleaned).
  1321.  */
  1322. if (Cbuf == buf)
  1323. vpc->vpd_offsets[vpc->vpd_offsets_free++] =
  1324. ItemPointerGetOffsetNumber(&(tuple.t_self));
  1325. else
  1326. keep_tuples++;
  1327. if (Irel != (Relation *) NULL)
  1328. {
  1329. for (i = 0, idcur = Idesc; i < nindices; i++, idcur++)
  1330. {
  1331. FormIndexDatum(idcur->natts,
  1332.    (AttrNumber *) &(idcur->tform->indkey[0]),
  1333.    &newtup,
  1334.    tupdesc,
  1335.    idatum,
  1336.    inulls,
  1337.    idcur->finfoP);
  1338. iresult = index_insert(Irel[i],
  1339.    idatum,
  1340.    inulls,
  1341.    &newtup.t_self,
  1342.    onerel);
  1343. if (iresult)
  1344. pfree(iresult);
  1345. }
  1346. }
  1347. WriteBuffer(cur_buffer);
  1348. if (Cbuf == buf)
  1349. ReleaseBuffer(Cbuf);
  1350. else
  1351. WriteBuffer(Cbuf);
  1352. }
  1353. cur_buffer = InvalidBuffer;
  1354. pfree(vtmove);
  1355. chain_tuple_moved = true;
  1356. continue;
  1357. }
  1358. /* try to find new page for this tuple */
  1359. if (cur_buffer == InvalidBuffer ||
  1360. !vc_enough_space(cur_page, tuple_len))
  1361. {
  1362. if (cur_buffer != InvalidBuffer)
  1363. {
  1364. WriteBuffer(cur_buffer);
  1365. cur_buffer = InvalidBuffer;
  1366. /*
  1367.  * If no one tuple can't be added to this page -
  1368.  * remove page from fraged_pages. - vadim 11/27/96
  1369.  *
  1370.  * But we can't remove last page - this is our
  1371.  * "show-stopper" !!! - vadim 02/25/98
  1372.  */
  1373. if (cur_page != last_fraged_page &&
  1374. !vc_enough_space(cur_page, vacrelstats->min_tlen))
  1375. {
  1376. Assert(num_fraged_pages > cur_item + 1);
  1377. memmove(fraged_pages->vpl_pagedesc + cur_item,
  1378. fraged_pages->vpl_pagedesc + cur_item + 1,
  1379. sizeof(VPageDescr *) * (num_fraged_pages - cur_item - 1));
  1380. num_fraged_pages--;
  1381. Assert(last_fraged_page == fraged_pages->vpl_pagedesc[num_fraged_pages - 1]);
  1382. }
  1383. }
  1384. for (i = 0; i < num_fraged_pages; i++)
  1385. {
  1386. if (vc_enough_space(fraged_pages->vpl_pagedesc[i], tuple_len))
  1387. break;
  1388. }
  1389. if (i == num_fraged_pages)
  1390. break; /* can't move item anywhere */
  1391. cur_item = i;
  1392. cur_page = fraged_pages->vpl_pagedesc[cur_item];
  1393. cur_buffer = ReadBuffer(onerel, cur_page->vpd_blkno);
  1394. ToPage = BufferGetPage(cur_buffer);
  1395. /* if this page was not used before - clean it */
  1396. if (!PageIsEmpty(ToPage) && cur_page->vpd_offsets_used == 0)
  1397. vc_vacpage(ToPage, cur_page);
  1398. }
  1399. /* copy tuple */
  1400. heap_copytuple_with_tuple(&tuple, &newtup);
  1401. RelationInvalidateHeapTuple(onerel, &tuple);
  1402. /*
  1403.  * Mark new tuple as moved_in by vacuum and store vacuum XID
  1404.  * in t_cmin !!!
  1405.  */
  1406. TransactionIdStore(myXID, (TransactionId *) &(newtup.t_data->t_cmin));
  1407. newtup.t_data->t_infomask &=
  1408. ~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_OFF);
  1409. newtup.t_data->t_infomask |= HEAP_MOVED_IN;
  1410. /* add tuple to the page */
  1411. newoff = PageAddItem(ToPage, (Item) newtup.t_data, tuple_len,
  1412.  InvalidOffsetNumber, LP_USED);
  1413. if (newoff == InvalidOffsetNumber)
  1414. {
  1415. elog(ERROR, "
  1416. failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)",
  1417.  tuple_len, cur_page->vpd_blkno, cur_page->vpd_free,
  1418.  cur_page->vpd_offsets_used, cur_page->vpd_offsets_free);
  1419. }
  1420. newitemid = PageGetItemId(ToPage, newoff);
  1421. pfree(newtup.t_data);
  1422. newtup.t_data = (HeapTupleHeader) PageGetItem(ToPage, newitemid);
  1423. ItemPointerSet(&(newtup.t_data->t_ctid), cur_page->vpd_blkno, newoff);
  1424. newtup.t_self = newtup.t_data->t_ctid;
  1425. /*
  1426.  * Mark old tuple as moved_off by vacuum and store vacuum XID
  1427.  * in t_cmin !!!
  1428.  */
  1429. TransactionIdStore(myXID, (TransactionId *) &(tuple.t_data->t_cmin));
  1430. tuple.t_data->t_infomask &=
  1431. ~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
  1432. tuple.t_data->t_infomask |= HEAP_MOVED_OFF;
  1433. cur_page->vpd_offsets_used++;
  1434. num_moved++;
  1435. cur_page->vpd_free = ((PageHeader) ToPage)->pd_upper - ((PageHeader) ToPage)->pd_lower;
  1436. vpc->vpd_offsets[vpc->vpd_offsets_free++] = offnum;
  1437. /* insert index' tuples if needed */
  1438. if (Irel != (Relation *) NULL)
  1439. {
  1440. for (i = 0, idcur = Idesc; i < nindices; i++, idcur++)
  1441. {
  1442. FormIndexDatum(idcur->natts,
  1443.    (AttrNumber *) &(idcur->tform->indkey[0]),
  1444.    &newtup,
  1445.    tupdesc,
  1446.    idatum,
  1447.    inulls,
  1448.    idcur->finfoP);
  1449. iresult = index_insert(Irel[i],
  1450.    idatum,
  1451.    inulls,
  1452.    &newtup.t_self,
  1453.    onerel);
  1454. if (iresult)
  1455. pfree(iresult);
  1456. }
  1457. }
  1458. } /* walk along page */
  1459. if (offnum < maxoff && keep_tuples > 0)
  1460. {
  1461. OffsetNumber off;
  1462. for (off = OffsetNumberNext(offnum);
  1463.  off <= maxoff;
  1464.  off = OffsetNumberNext(off))
  1465. {
  1466. itemid = PageGetItemId(page, off);
  1467. if (!ItemIdIsUsed(itemid))
  1468. continue;
  1469. tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
  1470. if (tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED)
  1471. continue;
  1472. if ((TransactionId) tuple.t_data->t_cmin != myXID)
  1473. elog(ERROR, "Invalid XID in t_cmin (4)");
  1474. if (tuple.t_data->t_infomask & HEAP_MOVED_IN)
  1475. elog(ERROR, "HEAP_MOVED_IN was not expected (2)");
  1476. if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
  1477. {
  1478. if (chain_tuple_moved) /* some chains was moved
  1479.  * while */
  1480. { /* cleaning this page */
  1481. Assert(vpc->vpd_offsets_free > 0);
  1482. for (i = 0; i < vpc->vpd_offsets_free; i++)
  1483. {
  1484. if (vpc->vpd_offsets[i] == off)
  1485. break;
  1486. }
  1487. if (i >= vpc->vpd_offsets_free) /* not found */
  1488. {
  1489. vpc->vpd_offsets[vpc->vpd_offsets_free++] = off;
  1490. Assert(keep_tuples > 0);
  1491. keep_tuples--;
  1492. }
  1493. }
  1494. else
  1495. {
  1496. vpc->vpd_offsets[vpc->vpd_offsets_free++] = off;
  1497. Assert(keep_tuples > 0);
  1498. keep_tuples--;
  1499. }
  1500. }
  1501. }
  1502. }
  1503. if (vpc->vpd_offsets_free > 0) /* some tuples were moved */
  1504. {
  1505. if (chain_tuple_moved) /* else - they are ordered */
  1506. {
  1507. qsort((char *) (vpc->vpd_offsets), vpc->vpd_offsets_free,
  1508.   sizeof(OffsetNumber), vc_cmp_offno);
  1509. }
  1510. vc_reappage(&Nvpl, vpc);
  1511. WriteBuffer(buf);
  1512. }
  1513. else if (dowrite)
  1514. WriteBuffer(buf);
  1515. else
  1516. ReleaseBuffer(buf);
  1517. if (offnum <= maxoff)
  1518. break; /* some item(s) left */
  1519. } /* walk along relation */
  1520. blkno++; /* new number of blocks */
  1521. if (cur_buffer != InvalidBuffer)
  1522. {
  1523. Assert(num_moved > 0);
  1524. WriteBuffer(cur_buffer);
  1525. }
  1526. if (num_moved > 0)
  1527. {
  1528. /*
  1529.  * We have to commit our tuple' movings before we'll truncate
  1530.  * relation, but we shouldn't lose our locks. And so - quick hack:
  1531.  * flush buffers and record status of current transaction as
  1532.  * committed, and continue. - vadim 11/13/96
  1533.  */
  1534. FlushBufferPool(!TransactionFlushEnabled());
  1535. TransactionIdCommit(myXID);
  1536. FlushBufferPool(!TransactionFlushEnabled());
  1537. }
  1538. /*
  1539.  * Clean uncleaned reapped pages from vacuum_pages list list and set
  1540.  * xmin committed for inserted tuples
  1541.  */
  1542. checked_moved = 0;
  1543. for (i = 0, vpp = vacuum_pages->vpl_pagedesc; i < vacuumed_pages; i++, vpp++)
  1544. {
  1545. Assert((*vpp)->vpd_blkno < blkno);
  1546. buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
  1547. page = BufferGetPage(buf);
  1548. if ((*vpp)->vpd_offsets_used == 0) /* this page was not used */
  1549. {
  1550. if (!PageIsEmpty(page))
  1551. vc_vacpage(page, *vpp);
  1552. }
  1553. else
  1554. /* this page was used */
  1555. {
  1556. num_tuples = 0;
  1557. max_offset = PageGetMaxOffsetNumber(page);
  1558. for (newoff = FirstOffsetNumber;
  1559.  newoff <= max_offset;
  1560.  newoff = OffsetNumberNext(newoff))
  1561. {
  1562. itemid = PageGetItemId(page, newoff);
  1563. if (!ItemIdIsUsed(itemid))
  1564. continue;
  1565. tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
  1566. if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
  1567. {
  1568. if ((TransactionId) tuple.t_data->t_cmin != myXID)
  1569. elog(ERROR, "Invalid XID in t_cmin (2)");
  1570. if (tuple.t_data->t_infomask & HEAP_MOVED_IN)
  1571. {
  1572. tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
  1573. num_tuples++;
  1574. }
  1575. else if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
  1576. tuple.t_data->t_infomask |= HEAP_XMIN_INVALID;
  1577. else
  1578. elog(ERROR, "HEAP_MOVED_OFF/HEAP_MOVED_IN was expected");
  1579. }
  1580. }
  1581. Assert((*vpp)->vpd_offsets_used == num_tuples);
  1582. checked_moved += num_tuples;
  1583. }
  1584. WriteBuffer(buf);
  1585. }
  1586. Assert(num_moved == checked_moved);
  1587. getrusage(RUSAGE_SELF, &ru1);
  1588. elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. 
  1589. Elapsed %u/%u sec.",
  1590.  (RelationGetRelationName(onerel))->data,
  1591.  nblocks, blkno, num_moved,
  1592.  ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
  1593.  ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
  1594. if (Nvpl.vpl_num_pages > 0)
  1595. {
  1596. /* vacuum indices again if needed */
  1597. if (Irel != (Relation *) NULL)
  1598. {
  1599. VPageDescr *vpleft,
  1600.    *vpright,
  1601. vpsave;
  1602. /* re-sort Nvpl.vpl_pagedesc */
  1603. for (vpleft = Nvpl.vpl_pagedesc,
  1604.  vpright = Nvpl.vpl_pagedesc + Nvpl.vpl_num_pages - 1;
  1605.  vpleft < vpright; vpleft++, vpright--)
  1606. {
  1607. vpsave = *vpleft;
  1608. *vpleft = *vpright;
  1609. *vpright = vpsave;
  1610. }
  1611. Assert(keep_tuples >= 0);
  1612. for (i = 0; i < nindices; i++)
  1613. vc_vaconeind(&Nvpl, Irel[i],
  1614.  vacrelstats->num_tuples, keep_tuples);
  1615. }
  1616. /*
  1617.  * clean moved tuples from last page in Nvpl list
  1618.  */
  1619. if (vpc->vpd_blkno == blkno - 1 && vpc->vpd_offsets_free > 0)
  1620. {
  1621. buf = ReadBuffer(onerel, vpc->vpd_blkno);
  1622. page = BufferGetPage(buf);
  1623. num_tuples = 0;
  1624. for (offnum = FirstOffsetNumber;
  1625.  offnum <= maxoff;
  1626.  offnum = OffsetNumberNext(offnum))
  1627. {
  1628. itemid = PageGetItemId(page, offnum);
  1629. if (!ItemIdIsUsed(itemid))
  1630. continue;
  1631. tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
  1632. if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
  1633. {
  1634. if ((TransactionId) tuple.t_data->t_cmin != myXID)
  1635. elog(ERROR, "Invalid XID in t_cmin (3)");
  1636. if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
  1637. {
  1638. itemid->lp_flags &= ~LP_USED;
  1639. num_tuples++;
  1640. }
  1641. else
  1642. elog(ERROR, "HEAP_MOVED_OFF was expected (2)");
  1643. }
  1644. }
  1645. Assert(vpc->vpd_offsets_free == num_tuples);
  1646. PageRepairFragmentation(page);
  1647. WriteBuffer(buf);
  1648. }
  1649. /* now - free new list of reapped pages */
  1650. vpp = Nvpl.vpl_pagedesc;
  1651. for (i = 0; i < Nvpl.vpl_num_pages; i++, vpp++)
  1652. pfree(*vpp);
  1653. pfree(Nvpl.vpl_pagedesc);
  1654. }
  1655. /* truncate relation */
  1656. if (blkno < nblocks)
  1657. {
  1658. i = BlowawayRelationBuffers(onerel, blkno);
  1659. if (i < 0)
  1660. elog(FATAL, "VACUUM (vc_rpfheap): BlowawayRelationBuffers returned %d", i);
  1661. blkno = smgrtruncate(DEFAULT_SMGR, onerel, blkno);
  1662. Assert(blkno >= 0);
  1663. vacrelstats->num_pages = blkno; /* set new number of blocks */
  1664. }
  1665. if (Irel != (Relation *) NULL) /* pfree index' allocations */
  1666. {
  1667. pfree(Idesc);
  1668. pfree(idatum);
  1669. pfree(inulls);
  1670. vc_clsindices(nindices, Irel);
  1671. }
  1672. pfree(vpc);
  1673. if (vacrelstats->vtlinks != NULL)
  1674. pfree(vacrelstats->vtlinks);
  1675. } /* vc_rpfheap */
  1676. /*
  1677.  * vc_vacheap() -- free dead tuples
  1678.  *
  1679.  * This routine marks dead tuples as unused and truncates relation
  1680.  * if there are "empty" end-blocks.
  1681.  */
  1682. static void
  1683. vc_vacheap(VRelStats *vacrelstats, Relation onerel, VPageList vacuum_pages)
  1684. {
  1685. Buffer buf;
  1686. Page page;
  1687. VPageDescr *vpp;
  1688. int nblocks;
  1689. int i;
  1690. nblocks = vacuum_pages->vpl_num_pages;
  1691. nblocks -= vacuum_pages->vpl_empty_end_pages; /* nothing to do with
  1692.  * them */
  1693. for (i = 0, vpp = vacuum_pages->vpl_pagedesc; i < nblocks; i++, vpp++)
  1694. {
  1695. if ((*vpp)->vpd_offsets_free > 0)
  1696. {
  1697. buf = ReadBuffer(onerel, (*vpp)->vpd_blkno);
  1698. page = BufferGetPage(buf);
  1699. vc_vacpage(page, *vpp);
  1700. WriteBuffer(buf);
  1701. }
  1702. }
  1703. /* truncate relation if there are some empty end-pages */
  1704. if (vacuum_pages->vpl_empty_end_pages > 0)
  1705. {
  1706. Assert(vacrelstats->num_pages >= vacuum_pages->vpl_empty_end_pages);
  1707. nblocks = vacrelstats->num_pages - vacuum_pages->vpl_empty_end_pages;
  1708. elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.",
  1709.  (RelationGetRelationName(onerel))->data,
  1710.  vacrelstats->num_pages, nblocks);
  1711. /*
  1712.  * we have to flush "empty" end-pages (if changed, but who knows
  1713.  * it) before truncation
  1714.  */
  1715. FlushBufferPool(!TransactionFlushEnabled());
  1716. i = BlowawayRelationBuffers(onerel, nblocks);
  1717. if (i < 0)
  1718. elog(FATAL, "VACUUM (vc_vacheap): BlowawayRelationBuffers returned %d", i);
  1719. nblocks = smgrtruncate(DEFAULT_SMGR, onerel, nblocks);
  1720. Assert(nblocks >= 0);
  1721. vacrelstats->num_pages = nblocks; /* set new number of
  1722.  * blocks */
  1723. }
  1724. } /* vc_vacheap */
  1725. /*
  1726.  * vc_vacpage() -- free dead tuples on a page
  1727.  *  and repaire its fragmentation.
  1728.  */
  1729. static void
  1730. vc_vacpage(Page page, VPageDescr vpd)
  1731. {
  1732. ItemId itemid;
  1733. int i;
  1734. for (i = 0; i < vpd->vpd_offsets_free; i++)
  1735. {
  1736. itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_offsets[i] - 1]);
  1737. itemid->lp_flags &= ~LP_USED;
  1738. }
  1739. PageRepairFragmentation(page);
  1740. } /* vc_vacpage */
  1741. /*
  1742.  * _vc_scanoneind() -- scan one index relation to update statistic.
  1743.  *
  1744.  */
  1745. static void
  1746. vc_scanoneind(Relation indrel, int num_tuples)
  1747. {
  1748. RetrieveIndexResult res;
  1749. IndexScanDesc iscan;
  1750. int nitups;
  1751. int nipages;
  1752. struct rusage ru0,
  1753. ru1;
  1754. getrusage(RUSAGE_SELF, &ru0);
  1755. /* walk through the entire index */
  1756. iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
  1757. nitups = 0;
  1758. while ((res = index_getnext(iscan, ForwardScanDirection))
  1759.    != (RetrieveIndexResult) NULL)
  1760. {
  1761. nitups++;
  1762. pfree(res);
  1763. }
  1764. index_endscan(iscan);
  1765. /* now update statistics in pg_class */
  1766. nipages = RelationGetNumberOfBlocks(indrel);
  1767. vc_updstats(RelationGetRelid(indrel), nipages, nitups, false, NULL);
  1768. getrusage(RUSAGE_SELF, &ru1);
  1769. elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u. Elapsed %u/%u sec.",
  1770.  indrel->rd_rel->relname.data, nipages, nitups,
  1771.  ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
  1772.  ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
  1773. if (nitups != num_tuples)
  1774. elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)",
  1775.  indrel->rd_rel->relname.data, nitups, num_tuples);
  1776. } /* vc_scanoneind */
  1777. /*
  1778.  * vc_vaconeind() -- vacuum one index relation.
  1779.  *
  1780.  * Vpl is the VPageList of the heap we're currently vacuuming.
  1781.  * It's locked. Indrel is an index relation on the vacuumed heap.
  1782.  * We don't set locks on the index relation here, since the indexed
  1783.  * access methods support locking at different granularities.
  1784.  * We let them handle it.
  1785.  *
  1786.  * Finally, we arrange to update the index relation's statistics in
  1787.  * pg_class.
  1788.  */
  1789. static void
  1790. vc_vaconeind(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples)
  1791. {
  1792. RetrieveIndexResult res;
  1793. IndexScanDesc iscan;
  1794. ItemPointer heapptr;
  1795. int tups_vacuumed;
  1796. int num_index_tuples;
  1797. int num_pages;
  1798. VPageDescr vp;
  1799. struct rusage ru0,
  1800. ru1;
  1801. getrusage(RUSAGE_SELF, &ru0);
  1802. /* walk through the entire index */
  1803. iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
  1804. tups_vacuumed = 0;
  1805. num_index_tuples = 0;
  1806. while ((res = index_getnext(iscan, ForwardScanDirection))
  1807.    != (RetrieveIndexResult) NULL)
  1808. {
  1809. heapptr = &res->heap_iptr;
  1810. if ((vp = vc_tidreapped(heapptr, vpl)) != (VPageDescr) NULL)
  1811. {
  1812. #ifdef NOT_USED
  1813. elog(DEBUG, "<%x,%x> -> <%x,%x>",
  1814.  ItemPointerGetBlockNumber(&(res->index_iptr)),
  1815.  ItemPointerGetOffsetNumber(&(res->index_iptr)),
  1816.  ItemPointerGetBlockNumber(&(res->heap_iptr)),
  1817.  ItemPointerGetOffsetNumber(&(res->heap_iptr)));
  1818. #endif
  1819. if (vp->vpd_offsets_free == 0)
  1820. { /* this is EmptyPage !!! */
  1821. elog(NOTICE, "Index %s: pointer to EmptyPage (blk %u off %u) - fixing",
  1822.  indrel->rd_rel->relname.data,
  1823.  vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr));
  1824. }
  1825. ++tups_vacuumed;
  1826. index_delete(indrel, &res->index_iptr);
  1827. }
  1828. else
  1829. num_index_tuples++;
  1830. pfree(res);
  1831. }
  1832. index_endscan(iscan);
  1833. /* now update statistics in pg_class */
  1834. num_pages = RelationGetNumberOfBlocks(indrel);
  1835. vc_updstats(RelationGetRelid(indrel), num_pages, num_index_tuples, false, NULL);
  1836. getrusage(RUSAGE_SELF, &ru1);
  1837. elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.",
  1838.  indrel->rd_rel->relname.data, num_pages,
  1839.  num_index_tuples - keep_tuples, tups_vacuumed,
  1840.  ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec,
  1841.  ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec);
  1842. if (num_index_tuples != num_tuples + keep_tuples)
  1843. elog(NOTICE, "Index %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)",
  1844.  indrel->rd_rel->relname.data, num_index_tuples, num_tuples);
  1845. } /* vc_vaconeind */
  1846. /*
  1847.  * vc_tidreapped() -- is a particular tid reapped?
  1848.  *
  1849.  * vpl->VPageDescr_array is sorted in right order.
  1850.  */
  1851. static VPageDescr
  1852. vc_tidreapped(ItemPointer itemptr, VPageList vpl)
  1853. {
  1854. OffsetNumber ioffno;
  1855. OffsetNumber *voff;
  1856. VPageDescr vp,
  1857.    *vpp;
  1858. VPageDescrData vpd;
  1859. vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr);
  1860. ioffno = ItemPointerGetOffsetNumber(itemptr);
  1861. vp = &vpd;
  1862. vpp = (VPageDescr *) vc_find_eq((void *) (vpl->vpl_pagedesc),
  1863. vpl->vpl_num_pages, sizeof(VPageDescr), (void *) &vp,
  1864. vc_cmp_blk);
  1865. if (vpp == (VPageDescr *) NULL)
  1866. return (VPageDescr) NULL;
  1867. vp = *vpp;
  1868. /* ok - we are on true page */
  1869. if (vp->vpd_offsets_free == 0)
  1870. { /* this is EmptyPage !!! */
  1871. return vp;
  1872. }
  1873. voff = (OffsetNumber *) vc_find_eq((void *) (vp->vpd_offsets),
  1874. vp->vpd_offsets_free, sizeof(OffsetNumber), (void *) &ioffno,
  1875.    vc_cmp_offno);
  1876. if (voff == (OffsetNumber *) NULL)
  1877. return (VPageDescr) NULL;
  1878. return vp;
  1879. } /* vc_tidreapped */
  1880. /*
  1881.  * vc_attrstats() -- compute column statistics used by the optimzer
  1882.  *
  1883.  * We compute the column min, max, null and non-null counts.
  1884.  * Plus we attempt to find the count of the value that occurs most
  1885.  * frequently in each column
  1886.  * These figures are used to compute the selectivity of the column
  1887.  *
  1888.  * We use a three-bucked cache to get the most frequent item
  1889.  * The 'guess' buckets count hits.  A cache miss causes guess1
  1890.  * to get the most hit 'guess' item in the most recent cycle, and
  1891.  * the new item goes into guess2. Whenever the total count of hits
  1892.  * of a 'guess' entry is larger than 'best', 'guess' becomes 'best'.
  1893.  *
  1894.  * This method works perfectly for columns with unique values, and columns
  1895.  * with only two unique values, plus nulls.
  1896.  *
  1897.  * It becomes less perfect as the number of unique values increases and
  1898.  * their distribution in the table becomes more random.
  1899.  *
  1900.  */
  1901. static void
  1902. vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple)
  1903. {
  1904. int i,
  1905. attr_cnt = vacrelstats->va_natts;
  1906. VacAttrStats *vacattrstats = vacrelstats->vacattrstats;
  1907. TupleDesc tupDesc = onerel->rd_att;
  1908. Datum value;
  1909. bool isnull;
  1910. for (i = 0; i < attr_cnt; i++)
  1911. {
  1912. VacAttrStats *stats = &vacattrstats[i];
  1913. bool value_hit = true;
  1914. value = heap_getattr(tuple,
  1915.  stats->attr->attnum, tupDesc, &isnull);
  1916. if (!VacAttrStatsEqValid(stats))
  1917. continue;
  1918. if (isnull)
  1919. stats->null_cnt++;
  1920. else
  1921. {
  1922. stats->nonnull_cnt++;
  1923. if (stats->initialized == false)
  1924. {
  1925. vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len);
  1926. /* best_cnt gets incremented later */
  1927. vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len);
  1928. stats->guess1_cnt = stats->guess1_hits = 1;
  1929. vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len);
  1930. stats->guess2_hits = 1;
  1931. if (VacAttrStatsLtGtValid(stats))
  1932. {
  1933. vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len);
  1934. vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len);
  1935. }
  1936. stats->initialized = true;
  1937. }
  1938. if (VacAttrStatsLtGtValid(stats))
  1939. {
  1940. if ((*fmgr_faddr(&stats->f_cmplt)) (value, stats->min))
  1941. {
  1942. vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len);
  1943. stats->min_cnt = 0;
  1944. }
  1945. if ((*fmgr_faddr(&stats->f_cmpgt)) (value, stats->max))
  1946. {
  1947. vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len);
  1948. stats->max_cnt = 0;
  1949. }
  1950. if ((*fmgr_faddr(&stats->f_cmpeq)) (value, stats->min))
  1951. stats->min_cnt++;
  1952. else if ((*fmgr_faddr(&stats->f_cmpeq)) (value, stats->max))
  1953. stats->max_cnt++;
  1954. }
  1955. if ((*fmgr_faddr(&stats->f_cmpeq)) (value, stats->best))
  1956. stats->best_cnt++;
  1957. else if ((*fmgr_faddr(&stats->f_cmpeq)) (value, stats->guess1))
  1958. {
  1959. stats->guess1_cnt++;
  1960. stats->guess1_hits++;
  1961. }
  1962. else if ((*fmgr_faddr(&stats->f_cmpeq)) (value, stats->guess2))
  1963. stats->guess2_hits++;
  1964. else
  1965. value_hit = false;
  1966. if (stats->guess2_hits > stats->guess1_hits)
  1967. {
  1968. swapDatum(stats->guess1, stats->guess2);
  1969. swapInt(stats->guess1_len, stats->guess2_len);
  1970. stats->guess1_cnt = stats->guess2_hits;
  1971. swapLong(stats->guess1_hits, stats->guess2_hits);
  1972. }
  1973. if (stats->guess1_cnt > stats->best_cnt)
  1974. {
  1975. swapDatum(stats->best, stats->guess1);
  1976. swapInt(stats->best_len, stats->guess1_len);
  1977. swapLong(stats->best_cnt, stats->guess1_cnt);
  1978. stats->guess1_hits = 1;
  1979. stats->guess2_hits = 1;
  1980. }
  1981. if (!value_hit)
  1982. {
  1983. vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len);
  1984. stats->guess1_hits = 1;
  1985. stats->guess2_hits = 1;
  1986. }
  1987. }
  1988. }
  1989. return;
  1990. }
  1991. /*
  1992.  * vc_bucketcpy() -- update pg_class statistics for one relation
  1993.  *
  1994.  */
  1995. static void
  1996. vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int16 *bucket_len)
  1997. {
  1998. if (attr->attbyval && attr->attlen != -1)
  1999. *bucket = value;
  2000. else
  2001. {
  2002. int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value));
  2003. if (len > *bucket_len)
  2004. {
  2005. if (*bucket_len != 0)
  2006. pfree(DatumGetPointer(*bucket));
  2007. *bucket = PointerGetDatum(palloc(len));
  2008. *bucket_len = len;
  2009. }
  2010. memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len);
  2011. }
  2012. }
  2013. /*
  2014.  * vc_updstats() -- update pg_class statistics for one relation
  2015.  *
  2016.  * This routine works for both index and heap relation entries in
  2017.  * pg_class.  We violate no-overwrite semantics here by storing new
  2018.  * values for num_tuples, num_pages, and hasindex directly in the pg_class
  2019.  * tuple that's already on the page.  The reason for this is that if
  2020.  * we updated these tuples in the usual way, then every tuple in pg_class
  2021.  * would be replaced every day.  This would make planning and executing
  2022.  * historical queries very expensive. Note that we also don't use
  2023.  * any locking while doing updation.
  2024.  */
  2025. static void
  2026. vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats)
  2027. {
  2028. Relation rd,
  2029. ad,
  2030. sd;
  2031. HeapScanDesc scan;
  2032. HeapTupleData rtup;
  2033. HeapTuple ctup,
  2034. atup,
  2035. stup;
  2036. Form_pg_class pgcform;
  2037. ScanKeyData askey;
  2038. Form_pg_attribute attp;
  2039. Buffer buffer;
  2040. /*
  2041.  * update number of tuples and number of pages in pg_class
  2042.  */
  2043. ctup = SearchSysCacheTupleCopy(RELOID,
  2044.    ObjectIdGetDatum(relid),
  2045.    0, 0, 0);
  2046. if (!HeapTupleIsValid(ctup))
  2047. elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
  2048.  relid);
  2049. rd = heap_openr(RelationRelationName);
  2050. /* get the buffer cache tuple */
  2051. rtup.t_self = ctup->t_self;
  2052. heap_fetch(rd, SnapshotNow, &rtup, &buffer);
  2053. pfree(ctup);
  2054. /* overwrite the existing statistics in the tuple */
  2055. pgcform = (Form_pg_class) GETSTRUCT(&rtup);
  2056. pgcform->reltuples = num_tuples;
  2057. pgcform->relpages = num_pages;
  2058. pgcform->relhasindex = hasindex;
  2059. if (vacrelstats != NULL && vacrelstats->va_natts > 0)
  2060. {
  2061. VacAttrStats *vacattrstats = vacrelstats->vacattrstats;
  2062. int natts = vacrelstats->va_natts;
  2063. ad = heap_openr(AttributeRelationName);
  2064. sd = heap_openr(StatisticRelationName);
  2065. ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid,
  2066.    F_INT4EQ, relid);
  2067. scan = heap_beginscan(ad, false, SnapshotNow, 1, &askey);
  2068. while (HeapTupleIsValid(atup = heap_getnext(scan, 0)))
  2069. {
  2070. int i;
  2071. float32data selratio; /* average ratio of rows selected
  2072.  * for a random constant */
  2073. VacAttrStats *stats;
  2074. Datum values[Natts_pg_statistic];
  2075. char nulls[Natts_pg_statistic];
  2076. attp = (Form_pg_attribute) GETSTRUCT(atup);
  2077. if (attp->attnum <= 0) /* skip system attributes for now, */
  2078. /* they are unique anyway */
  2079. continue;
  2080. for (i = 0; i < natts; i++)
  2081. {
  2082. if (attp->attnum == vacattrstats[i].attr->attnum)
  2083. break;
  2084. }
  2085. if (i >= natts)
  2086. continue;
  2087. stats = &(vacattrstats[i]);
  2088. /* overwrite the existing statistics in the tuple */
  2089. if (VacAttrStatsEqValid(stats))
  2090. {
  2091. if (stats->nonnull_cnt + stats->null_cnt == 0 ||
  2092. (stats->null_cnt <= 1 && stats->best_cnt == 1))
  2093. selratio = 0;
  2094. else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt)
  2095. {
  2096. double min_cnt_d = stats->min_cnt,
  2097. max_cnt_d = stats->max_cnt,
  2098. null_cnt_d = stats->null_cnt,
  2099. nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */
  2100. selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) /
  2101. (nonnullcnt_d + null_cnt_d) / (nonnullcnt_d + null_cnt_d);
  2102. }
  2103. else
  2104. {
  2105. double most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt);
  2106. double total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt);
  2107. /*
  2108.  * we assume count of other values are 20% of best
  2109.  * count in table
  2110.  */
  2111. selratio = (most * most + 0.20 * most * (total - most)) / total / total;
  2112. }
  2113. if (selratio > 1.0)
  2114. selratio = 1.0;
  2115. attp->attdisbursion = selratio;
  2116. /*
  2117.  * Invalidate the cache for the tuple and write the buffer
  2118.  */
  2119. RelationInvalidateHeapTuple(ad, atup);
  2120. WriteNoReleaseBuffer(scan->rs_cbuf);
  2121. /* DO PG_STATISTIC INSERTS */
  2122. /*
  2123.  * doing system relations, especially pg_statistic is a
  2124.  * problem
  2125.  */
  2126. if (VacAttrStatsLtGtValid(stats) && stats->initialized /* &&
  2127.  * !IsSystemRelationName(
  2128.  *
  2129.  pgcform->relname.data) */ )
  2130. {
  2131. FmgrInfo out_function;
  2132. char    *out_string;
  2133. for (i = 0; i < Natts_pg_statistic; ++i)
  2134. nulls[i] = ' ';
  2135. /* ----------------
  2136.  * initialize values[]
  2137.  * ----------------
  2138.  */
  2139. i = 0;
  2140. values[i++] = (Datum) relid; /* 1 */
  2141. values[i++] = (Datum) attp->attnum; /* 2 */
  2142. values[i++] = (Datum) InvalidOid; /* 3 */
  2143. fmgr_info(stats->outfunc, &out_function);
  2144. out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->attr->atttypid);
  2145. values[i++] = (Datum) fmgr(F_TEXTIN, out_string);
  2146. pfree(out_string);
  2147. out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->attr->atttypid);
  2148. values[i++] = (Datum) fmgr(F_TEXTIN, out_string);
  2149. pfree(out_string);
  2150. stup = heap_formtuple(sd->rd_att, values, nulls);
  2151. /* ----------------
  2152.  * insert the tuple in the relation and get the tuple's oid.
  2153.  * ----------------
  2154.  */
  2155. heap_insert(sd, stup);
  2156. pfree(DatumGetPointer(values[3]));
  2157. pfree(DatumGetPointer(values[4]));
  2158. pfree(stup);
  2159. }
  2160. }
  2161. }
  2162. heap_endscan(scan);
  2163. heap_close(ad);
  2164. heap_close(sd);
  2165. }
  2166. /*
  2167.  * Invalidate the cached pg_class tuple and write the buffer
  2168.  */
  2169. RelationInvalidateHeapTuple(rd, &rtup);
  2170. WriteBuffer(buffer);
  2171. heap_close(rd);
  2172. }
  2173. /*
  2174.  * vc_delhilowstats() -- delete pg_statistics rows
  2175.  *
  2176.  */
  2177. static void
  2178. vc_delhilowstats(Oid relid, int attcnt, int *attnums)
  2179. {
  2180. Relation pgstatistic;
  2181. HeapScanDesc scan;
  2182. HeapTuple tuple;
  2183. ScanKeyData key;
  2184. pgstatistic = heap_openr(StatisticRelationName);
  2185. if (relid != InvalidOid)
  2186. {
  2187. ScanKeyEntryInitialize(&key, 0x0, Anum_pg_statistic_starelid,
  2188.    F_OIDEQ,
  2189.    ObjectIdGetDatum(relid));
  2190. scan = heap_beginscan(pgstatistic, false, SnapshotNow, 1, &key);
  2191. }
  2192. else
  2193. scan = heap_beginscan(pgstatistic, false, SnapshotNow, 0, NULL);
  2194. while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
  2195. {
  2196. if (attcnt > 0)
  2197. {
  2198. Form_pg_statistic pgs = (Form_pg_statistic) GETSTRUCT(tuple);
  2199. int i;
  2200. for (i = 0; i < attcnt; i++)
  2201. {
  2202. if (pgs->staattnum == attnums[i] + 1)
  2203. break;
  2204. }
  2205. if (i >= attcnt)
  2206. continue; /* don't delete it */
  2207. }
  2208. heap_delete(pgstatistic, &tuple->t_self, NULL);
  2209. }
  2210. heap_endscan(scan);
  2211. heap_close(pgstatistic);
  2212. }
  2213. /*
  2214.  * vc_reappage() -- save a page on the array of reapped pages.
  2215.  *
  2216.  * As a side effect of the way that the vacuuming loop for a given
  2217.  * relation works, higher pages come after lower pages in the array
  2218.  * (and highest tid on a page is last).
  2219.  */
  2220. static void
  2221. vc_reappage(VPageList vpl, VPageDescr vpc)
  2222. {
  2223. VPageDescr newvpd;
  2224. /* allocate a VPageDescrData entry */
  2225. newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_offsets_free * sizeof(OffsetNumber));
  2226. /* fill it in */
  2227. if (vpc->vpd_offsets_free > 0)
  2228. memmove(newvpd->vpd_offsets, vpc->vpd_offsets, vpc->vpd_offsets_free * sizeof(OffsetNumber));
  2229. newvpd->vpd_blkno = vpc->vpd_blkno;
  2230. newvpd->vpd_free = vpc->vpd_free;
  2231. newvpd->vpd_offsets_used = vpc->vpd_offsets_used;
  2232. newvpd->vpd_offsets_free = vpc->vpd_offsets_free;
  2233. /* insert this page into vpl list */
  2234. vc_vpinsert(vpl, newvpd);
  2235. } /* vc_reappage */
  2236. static void
  2237. vc_vpinsert(VPageList vpl, VPageDescr vpnew)
  2238. {
  2239. #define PG_NPAGEDESC 1024
  2240. /* allocate a VPageDescr entry if needed */
  2241. if (vpl->vpl_num_pages == 0)
  2242. {
  2243. vpl->vpl_pagedesc = (VPageDescr *) palloc(PG_NPAGEDESC * sizeof(VPageDescr));
  2244. vpl->vpl_num_allocated_pages = PG_NPAGEDESC;
  2245. }
  2246. else if (vpl->vpl_num_pages >= vpl->vpl_num_allocated_pages)
  2247. {
  2248. vpl->vpl_num_allocated_pages *= 2;
  2249. vpl->vpl_pagedesc = (VPageDescr *) repalloc(vpl->vpl_pagedesc, vpl->vpl_num_allocated_pages * sizeof(VPageDescr));
  2250. }
  2251. vpl->vpl_pagedesc[vpl->vpl_num_pages] = vpnew;
  2252. (vpl->vpl_num_pages)++;
  2253. }
  2254. static void
  2255. vc_free(VRelList vrl)
  2256. {
  2257. VRelList p_vrl;
  2258. MemoryContext old;
  2259. PortalVariableMemory pmem;
  2260. pmem = PortalGetVariableMemory(vc_portal);
  2261. old = MemoryContextSwitchTo((MemoryContext) pmem);
  2262. while (vrl != (VRelList) NULL)
  2263. {
  2264. /* free rel list entry */
  2265. p_vrl = vrl;
  2266. vrl = vrl->vrl_next;
  2267. pfree(p_vrl);
  2268. }
  2269. MemoryContextSwitchTo(old);
  2270. }
  2271. static void *
  2272. vc_find_eq(void *bot, int nelem, int size, void *elm,
  2273.    int (*compar) (const void *, const void *))
  2274. {
  2275. int res;
  2276. int last = nelem - 1;
  2277. int celm = nelem / 2;
  2278. bool last_move,
  2279. first_move;
  2280. last_move = first_move = true;
  2281. for (;;)
  2282. {
  2283. if (first_move == true)
  2284. {
  2285. res = compar(bot, elm);
  2286. if (res > 0)
  2287. return NULL;
  2288. if (res == 0)
  2289. return bot;
  2290. first_move = false;
  2291. }
  2292. if (last_move == true)
  2293. {
  2294. res = compar(elm, (void *) ((char *) bot + last * size));
  2295. if (res > 0)
  2296. return NULL;
  2297. if (res == 0)
  2298. return (void *) ((char *) bot + last * size);
  2299. last_move = false;
  2300. }
  2301. res = compar(elm, (void *) ((char *) bot + celm * size));
  2302. if (res == 0)
  2303. return (void *) ((char *) bot + celm * size);
  2304. if (res < 0)
  2305. {
  2306. if (celm == 0)
  2307. return NULL;
  2308. last = celm - 1;
  2309. celm = celm / 2;
  2310. last_move = true;
  2311. continue;
  2312. }
  2313. if (celm == last)
  2314. return NULL;
  2315. last = last - celm - 1;
  2316. bot = (void *) ((char *) bot + (celm + 1) * size);
  2317. celm = (last + 1) / 2;
  2318. first_move = true;
  2319. }
  2320. } /* vc_find_eq */
  2321. static int
  2322. vc_cmp_blk(const void *left, const void *right)
  2323. {
  2324. BlockNumber lblk,
  2325. rblk;
  2326. lblk = (*((VPageDescr *) left))->vpd_blkno;
  2327. rblk = (*((VPageDescr *) right))->vpd_blkno;
  2328. if (lblk < rblk)
  2329. return -1;
  2330. if (lblk == rblk)
  2331. return 0;
  2332. return 1;
  2333. } /* vc_cmp_blk */
  2334. static int
  2335. vc_cmp_offno(const void *left, const void *right)
  2336. {
  2337. if (*(OffsetNumber *) left < *(OffsetNumber *) right)
  2338. return -1;
  2339. if (*(OffsetNumber *) left == *(OffsetNumber *) right)
  2340. return 0;
  2341. return 1;
  2342. } /* vc_cmp_offno */
  2343. static int
  2344. vc_cmp_vtlinks(const void *left, const void *right)
  2345. {
  2346. if (((VTupleLink) left)->new_tid.ip_blkid.bi_hi <
  2347. ((VTupleLink) right)->new_tid.ip_blkid.bi_hi)
  2348. return -1;
  2349. if (((VTupleLink) left)->new_tid.ip_blkid.bi_hi >
  2350. ((VTupleLink) right)->new_tid.ip_blkid.bi_hi)
  2351. return 1;
  2352. /* bi_hi-es are equal */
  2353. if (((VTupleLink) left)->new_tid.ip_blkid.bi_lo <
  2354. ((VTupleLink) right)->new_tid.ip_blkid.bi_lo)
  2355. return -1;
  2356. if (((VTupleLink) left)->new_tid.ip_blkid.bi_lo >
  2357. ((VTupleLink) right)->new_tid.ip_blkid.bi_lo)
  2358. return 1;
  2359. /* bi_lo-es are equal */
  2360. if (((VTupleLink) left)->new_tid.ip_posid <
  2361. ((VTupleLink) right)->new_tid.ip_posid)
  2362. return -1;
  2363. if (((VTupleLink) left)->new_tid.ip_posid >
  2364. ((VTupleLink) right)->new_tid.ip_posid)
  2365. return 1;
  2366. return 0;
  2367. }
  2368. static void
  2369. vc_getindices(Oid relid, int *nindices, Relation **Irel)
  2370. {
  2371. Relation pgindex;
  2372. Relation irel;
  2373. TupleDesc tupdesc;
  2374. HeapTuple tuple;
  2375. HeapScanDesc scan;
  2376. Datum d;
  2377. int i,
  2378. k;
  2379. bool n;
  2380. ScanKeyData key;
  2381. Oid    *ioid;
  2382. *nindices = i = 0;
  2383. ioid = (Oid *) palloc(10 * sizeof(Oid));
  2384. /* prepare a heap scan on the pg_index relation */
  2385. pgindex = heap_openr(IndexRelationName);
  2386. tupdesc = RelationGetDescr(pgindex);
  2387. ScanKeyEntryInitialize(&key, 0x0, Anum_pg_index_indrelid,
  2388.    F_OIDEQ,
  2389.    ObjectIdGetDatum(relid));
  2390. scan = heap_beginscan(pgindex, false, SnapshotNow, 1, &key);
  2391. while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
  2392. {
  2393. d = heap_getattr(tuple, Anum_pg_index_indexrelid,
  2394.  tupdesc, &n);
  2395. i++;
  2396. if (i % 10 == 0)
  2397. ioid = (Oid *) repalloc(ioid, (i + 10) * sizeof(Oid));
  2398. ioid[i - 1] = DatumGetObjectId(d);
  2399. }
  2400. heap_endscan(scan);
  2401. heap_close(pgindex);
  2402. if (i == 0)
  2403. { /* No one index found */
  2404. pfree(ioid);
  2405. return;
  2406. }
  2407. if (Irel != (Relation **) NULL)
  2408. *Irel = (Relation *) palloc(i * sizeof(Relation));
  2409. for (k = 0; i > 0;)
  2410. {
  2411. irel = index_open(ioid[--i]);
  2412. if (irel != (Relation) NULL)
  2413. {
  2414. if (Irel != (Relation **) NULL)
  2415. (*Irel)[k] = irel;
  2416. else
  2417. index_close(irel);
  2418. k++;
  2419. }
  2420. else
  2421. elog(NOTICE, "CAN'T OPEN INDEX %u - SKIP IT", ioid[i]);
  2422. }
  2423. *nindices = k;
  2424. pfree(ioid);
  2425. if (Irel != (Relation **) NULL && *nindices == 0)
  2426. {
  2427. pfree(*Irel);
  2428. *Irel = (Relation *) NULL;
  2429. }
  2430. } /* vc_getindices */
  2431. static void
  2432. vc_clsindices(int nindices, Relation *Irel)
  2433. {
  2434. if (Irel == (Relation *) NULL)
  2435. return;
  2436. while (nindices--)
  2437. index_close(Irel[nindices]);
  2438. pfree(Irel);
  2439. } /* vc_clsindices */
  2440. static void
  2441. vc_mkindesc(Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc)
  2442. {
  2443. IndDesc    *idcur;
  2444. HeapTuple cachetuple;
  2445. AttrNumber *attnumP;
  2446. int natts;
  2447. int i;
  2448. *Idesc = (IndDesc *) palloc(nindices * sizeof(IndDesc));
  2449. for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++)
  2450. {
  2451. cachetuple = SearchSysCacheTupleCopy(INDEXRELID,
  2452.  ObjectIdGetDatum(RelationGetRelid(Irel[i])),
  2453.  0, 0, 0);
  2454. Assert(cachetuple);
  2455. /*
  2456.  * we never free the copy we make, because Idesc needs it for
  2457.  * later
  2458.  */
  2459. idcur->tform = (Form_pg_index) GETSTRUCT(cachetuple);
  2460. for (attnumP = &(idcur->tform->indkey[0]), natts = 0;
  2461.  natts < INDEX_MAX_KEYS && *attnumP != InvalidAttrNumber;
  2462.  attnumP++, natts++);
  2463. if (idcur->tform->indproc != InvalidOid)
  2464. {
  2465. idcur->finfoP = &(idcur->finfo);
  2466. FIgetnArgs(idcur->finfoP) = natts;
  2467. natts = 1;
  2468. FIgetProcOid(idcur->finfoP) = idcur->tform->indproc;
  2469. *(FIgetname(idcur->finfoP)) = '';
  2470. }
  2471. else
  2472. idcur->finfoP = (FuncIndexInfo *) NULL;
  2473. idcur->natts = natts;
  2474. }
  2475. } /* vc_mkindesc */
  2476. static bool
  2477. vc_enough_space(VPageDescr vpd, Size len)
  2478. {
  2479. len = MAXALIGN(len);
  2480. if (len > vpd->vpd_free)
  2481. return false;
  2482. if (vpd->vpd_offsets_used < vpd->vpd_offsets_free) /* there are free
  2483.  * itemid(s) */
  2484. return true; /* and len <= free_space */
  2485. /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */
  2486. if (len + MAXALIGN(sizeof(ItemIdData)) <= vpd->vpd_free)
  2487. return true;
  2488. return false;
  2489. } /* vc_enough_space */