28
28
/* Minimum tree height for application of fastpath optimization */
29
29
#define BTREE_FASTPATH_MIN_LEVEL 2
30
30
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
-
51
31
52
32
static Buffer _bt_newroot (Relation rel , Buffer lbuf , Buffer rbuf );
53
33
@@ -73,13 +53,6 @@ static Buffer _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf,
73
53
Size newitemsz , IndexTuple newitem , bool newitemonleft );
74
54
static void _bt_insert_parent (Relation rel , Buffer buf , Buffer rbuf ,
75
55
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 );
83
56
static bool _bt_pgaddtup (Page page , Size itemsize , IndexTuple itup ,
84
57
OffsetNumber itup_off );
85
58
static bool _bt_isequal (TupleDesc itupdesc , BTScanInsert itup_key ,
@@ -1003,7 +976,7 @@ _bt_insertonpg(Relation rel,
1003
976
1004
977
/* Choose the split point */
1005
978
firstright = _bt_findsplitloc (rel , page ,
1006
- newitemoff , itemsz ,
979
+ newitemoff , itemsz , itup ,
1007
980
& newitemonleft );
1008
981
1009
982
/* split the buffer into left and right halves */
@@ -1687,264 +1660,6 @@ _bt_split(Relation rel, BTScanInsert itup_key, Buffer buf, Buffer cbuf,
1687
1660
return rbuf ;
1688
1661
}
1689
1662
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
-
1948
1663
/*
1949
1664
* _bt_insert_parent() -- Insert downlink into parent after a page split.
1950
1665
*
0 commit comments