2828/* Minimum tree height for application of fastpath optimization */
2929#define BTREE_FASTPATH_MIN_LEVEL 2
3030
31- typedef struct
32- {
33- /* context data for _bt_checksplitloc */
34- Size newitemsz ; /* size of new item to be inserted */
35- int fillfactor ; /* needed when splitting rightmost page */
36- bool is_leaf ; /* T if splitting a leaf page */
37- bool is_rightmost ; /* T if splitting a rightmost page */
38- OffsetNumber newitemoff ; /* where the new item is to be inserted */
39- int leftspace ; /* space available for items on left page */
40- int rightspace ; /* space available for items on right page */
41- int olddataitemstotal ; /* space taken by old items */
42-
43- bool have_split ; /* found a valid split? */
44-
45- /* these fields valid only if have_split is true */
46- bool newitemonleft ; /* new item on left or right of best split */
47- OffsetNumber firstright ; /* best split point */
48- int best_delta ; /* best size delta so far */
49- } FindSplitData ;
50-
5131
5232static Buffer _bt_newroot (Relation rel , Buffer lbuf , Buffer rbuf );
5333
@@ -73,13 +53,6 @@ static Buffer _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf,
7353 Size newitemsz , IndexTuple newitem , bool newitemonleft );
7454static void _bt_insert_parent (Relation rel , Buffer buf , Buffer rbuf ,
7555 BTStack stack , bool is_root , bool is_only );
76- static OffsetNumber _bt_findsplitloc (Relation rel , Page page ,
77- OffsetNumber newitemoff ,
78- Size newitemsz ,
79- bool * newitemonleft );
80- static void _bt_checksplitloc (FindSplitData * state ,
81- OffsetNumber firstoldonright , bool newitemonleft ,
82- int dataitemstoleft , Size firstoldonrightsz );
8356static bool _bt_pgaddtup (Page page , Size itemsize , IndexTuple itup ,
8457 OffsetNumber itup_off );
8558static bool _bt_isequal (TupleDesc itupdesc , BTScanInsert itup_key ,
@@ -1003,7 +976,7 @@ _bt_insertonpg(Relation rel,
1003976
1004977 /* Choose the split point */
1005978 firstright = _bt_findsplitloc (rel , page ,
1006- newitemoff , itemsz ,
979+ newitemoff , itemsz , itup ,
1007980 & newitemonleft );
1008981
1009982 /* split the buffer into left and right halves */
@@ -1687,264 +1660,6 @@ _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf, Buffer cbuf,
16871660 return rbuf ;
16881661}
16891662
1690- /*
1691- * _bt_findsplitloc() -- find an appropriate place to split a page.
1692- *
1693- * The idea here is to equalize the free space that will be on each split
1694- * page, *after accounting for the inserted tuple*. (If we fail to account
1695- * for it, we might find ourselves with too little room on the page that
1696- * it needs to go into!)
1697- *
1698- * If the page is the rightmost page on its level, we instead try to arrange
1699- * to leave the left split page fillfactor% full. In this way, when we are
1700- * inserting successively increasing keys (consider sequences, timestamps,
1701- * etc) we will end up with a tree whose pages are about fillfactor% full,
1702- * instead of the 50% full result that we'd get without this special case.
1703- * This is the same as nbtsort.c produces for a newly-created tree. Note
1704- * that leaf and nonleaf pages use different fillfactors.
1705- *
1706- * We are passed the intended insert position of the new tuple, expressed as
1707- * the offsetnumber of the tuple it must go in front of. (This could be
1708- * maxoff+1 if the tuple is to go at the end.)
1709- *
1710- * We return the index of the first existing tuple that should go on the
1711- * righthand page, plus a boolean indicating whether the new tuple goes on
1712- * the left or right page. The bool is necessary to disambiguate the case
1713- * where firstright == newitemoff.
1714- */
1715- static OffsetNumber
1716- _bt_findsplitloc (Relation rel ,
1717- Page page ,
1718- OffsetNumber newitemoff ,
1719- Size newitemsz ,
1720- bool * newitemonleft )
1721- {
1722- BTPageOpaque opaque ;
1723- OffsetNumber offnum ;
1724- OffsetNumber maxoff ;
1725- ItemId itemid ;
1726- FindSplitData state ;
1727- int leftspace ,
1728- rightspace ,
1729- goodenough ,
1730- olddataitemstotal ,
1731- olddataitemstoleft ;
1732- bool goodenoughfound ;
1733-
1734- opaque = (BTPageOpaque ) PageGetSpecialPointer (page );
1735-
1736- /* Passed-in newitemsz is MAXALIGNED but does not include line pointer */
1737- newitemsz += sizeof (ItemIdData );
1738-
1739- /* Total free space available on a btree page, after fixed overhead */
1740- leftspace = rightspace =
1741- PageGetPageSize (page ) - SizeOfPageHeaderData -
1742- MAXALIGN (sizeof (BTPageOpaqueData ));
1743-
1744- /* The right page will have the same high key as the old page */
1745- if (!P_RIGHTMOST (opaque ))
1746- {
1747- itemid = PageGetItemId (page , P_HIKEY );
1748- rightspace -= (int ) (MAXALIGN (ItemIdGetLength (itemid )) +
1749- sizeof (ItemIdData ));
1750- }
1751-
1752- /* Count up total space in data items without actually scanning 'em */
1753- olddataitemstotal = rightspace - (int ) PageGetExactFreeSpace (page );
1754-
1755- state .newitemsz = newitemsz ;
1756- state .is_leaf = P_ISLEAF (opaque );
1757- state .is_rightmost = P_RIGHTMOST (opaque );
1758- state .have_split = false;
1759- if (state .is_leaf )
1760- state .fillfactor = RelationGetFillFactor (rel ,
1761- BTREE_DEFAULT_FILLFACTOR );
1762- else
1763- state .fillfactor = BTREE_NONLEAF_FILLFACTOR ;
1764- state .newitemonleft = false; /* these just to keep compiler quiet */
1765- state .firstright = 0 ;
1766- state .best_delta = 0 ;
1767- state .leftspace = leftspace ;
1768- state .rightspace = rightspace ;
1769- state .olddataitemstotal = olddataitemstotal ;
1770- state .newitemoff = newitemoff ;
1771-
1772- /*
1773- * Finding the best possible split would require checking all the possible
1774- * split points, because of the high-key and left-key special cases.
1775- * That's probably more work than it's worth; instead, stop as soon as we
1776- * find a "good-enough" split, where good-enough is defined as an
1777- * imbalance in free space of no more than pagesize/16 (arbitrary...) This
1778- * should let us stop near the middle on most pages, instead of plowing to
1779- * the end.
1780- */
1781- goodenough = leftspace / 16 ;
1782-
1783- /*
1784- * Scan through the data items and calculate space usage for a split at
1785- * each possible position.
1786- */
1787- olddataitemstoleft = 0 ;
1788- goodenoughfound = false;
1789- maxoff = PageGetMaxOffsetNumber (page );
1790-
1791- for (offnum = P_FIRSTDATAKEY (opaque );
1792- offnum <= maxoff ;
1793- offnum = OffsetNumberNext (offnum ))
1794- {
1795- Size itemsz ;
1796-
1797- itemid = PageGetItemId (page , offnum );
1798- itemsz = MAXALIGN (ItemIdGetLength (itemid )) + sizeof (ItemIdData );
1799-
1800- /*
1801- * Will the new item go to left or right of split?
1802- */
1803- if (offnum > newitemoff )
1804- _bt_checksplitloc (& state , offnum , true,
1805- olddataitemstoleft , itemsz );
1806-
1807- else if (offnum < newitemoff )
1808- _bt_checksplitloc (& state , offnum , false,
1809- olddataitemstoleft , itemsz );
1810- else
1811- {
1812- /* need to try it both ways! */
1813- _bt_checksplitloc (& state , offnum , true,
1814- olddataitemstoleft , itemsz );
1815-
1816- _bt_checksplitloc (& state , offnum , false,
1817- olddataitemstoleft , itemsz );
1818- }
1819-
1820- /* Abort scan once we find a good-enough choice */
1821- if (state .have_split && state .best_delta <= goodenough )
1822- {
1823- goodenoughfound = true;
1824- break ;
1825- }
1826-
1827- olddataitemstoleft += itemsz ;
1828- }
1829-
1830- /*
1831- * If the new item goes as the last item, check for splitting so that all
1832- * the old items go to the left page and the new item goes to the right
1833- * page.
1834- */
1835- if (newitemoff > maxoff && !goodenoughfound )
1836- _bt_checksplitloc (& state , newitemoff , false, olddataitemstotal , 0 );
1837-
1838- /*
1839- * I believe it is not possible to fail to find a feasible split, but just
1840- * in case ...
1841- */
1842- if (!state .have_split )
1843- elog (ERROR , "could not find a feasible split point for index \"%s\"" ,
1844- RelationGetRelationName (rel ));
1845-
1846- * newitemonleft = state .newitemonleft ;
1847- return state .firstright ;
1848- }
1849-
1850- /*
1851- * Subroutine to analyze a particular possible split choice (ie, firstright
1852- * and newitemonleft settings), and record the best split so far in *state.
1853- *
1854- * firstoldonright is the offset of the first item on the original page
1855- * that goes to the right page, and firstoldonrightsz is the size of that
1856- * tuple. firstoldonright can be > max offset, which means that all the old
1857- * items go to the left page and only the new item goes to the right page.
1858- * In that case, firstoldonrightsz is not used.
1859- *
1860- * olddataitemstoleft is the total size of all old items to the left of
1861- * firstoldonright.
1862- */
1863- static void
1864- _bt_checksplitloc (FindSplitData * state ,
1865- OffsetNumber firstoldonright ,
1866- bool newitemonleft ,
1867- int olddataitemstoleft ,
1868- Size firstoldonrightsz )
1869- {
1870- int leftfree ,
1871- rightfree ;
1872- Size firstrightitemsz ;
1873- bool newitemisfirstonright ;
1874-
1875- /* Is the new item going to be the first item on the right page? */
1876- newitemisfirstonright = (firstoldonright == state -> newitemoff
1877- && !newitemonleft );
1878-
1879- if (newitemisfirstonright )
1880- firstrightitemsz = state -> newitemsz ;
1881- else
1882- firstrightitemsz = firstoldonrightsz ;
1883-
1884- /* Account for all the old tuples */
1885- leftfree = state -> leftspace - olddataitemstoleft ;
1886- rightfree = state -> rightspace -
1887- (state -> olddataitemstotal - olddataitemstoleft );
1888-
1889- /*
1890- * The first item on the right page becomes the high key of the left page;
1891- * therefore it counts against left space as well as right space. When
1892- * index has included attributes, then those attributes of left page high
1893- * key will be truncated leaving that page with slightly more free space.
1894- * However, that shouldn't affect our ability to find valid split
1895- * location, because anyway split location should exists even without high
1896- * key truncation.
1897- */
1898- leftfree -= firstrightitemsz ;
1899-
1900- /* account for the new item */
1901- if (newitemonleft )
1902- leftfree -= (int ) state -> newitemsz ;
1903- else
1904- rightfree -= (int ) state -> newitemsz ;
1905-
1906- /*
1907- * If we are not on the leaf level, we will be able to discard the key
1908- * data from the first item that winds up on the right page.
1909- */
1910- if (!state -> is_leaf )
1911- rightfree += (int ) firstrightitemsz -
1912- (int ) (MAXALIGN (sizeof (IndexTupleData )) + sizeof (ItemIdData ));
1913-
1914- /*
1915- * If feasible split point, remember best delta.
1916- */
1917- if (leftfree >= 0 && rightfree >= 0 )
1918- {
1919- int delta ;
1920-
1921- if (state -> is_rightmost )
1922- {
1923- /*
1924- * If splitting a rightmost page, try to put (100-fillfactor)% of
1925- * free space on left page. See comments for _bt_findsplitloc.
1926- */
1927- delta = (state -> fillfactor * leftfree )
1928- - ((100 - state -> fillfactor ) * rightfree );
1929- }
1930- else
1931- {
1932- /* Otherwise, aim for equal free space on both sides */
1933- delta = leftfree - rightfree ;
1934- }
1935-
1936- if (delta < 0 )
1937- delta = - delta ;
1938- if (!state -> have_split || delta < state -> best_delta )
1939- {
1940- state -> have_split = true;
1941- state -> newitemonleft = newitemonleft ;
1942- state -> firstright = firstoldonright ;
1943- state -> best_delta = delta ;
1944- }
1945- }
1946- }
1947-
19481663/*
19491664 * _bt_insert_parent() -- Insert downlink into parent after a page split.
19501665 *
0 commit comments