PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ gserialized_gist_picksplit_2d()

Datum gserialized_gist_picksplit_2d ( PG_FUNCTION_ARGS  )

Definition at line 1613 of file gserialized_gist_2d.c.

1614{
1615 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1616 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
1617 OffsetNumber i,
1618 maxoff;
1619 ConsiderSplitContext context;
1620 BOX2DF *box,
1621 *leftBox,
1622 *rightBox;
1623 int dim,
1624 commonEntriesCount;
1625 SplitInterval *intervalsLower,
1626 *intervalsUpper;
1627 CommonEntry *commonEntries;
1628 int nentries;
1629
1630 POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
1631
1632 memset(&context, 0, sizeof(ConsiderSplitContext));
1633
1634 maxoff = entryvec->n - 1;
1635 nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
1636
1637 /* Allocate arrays for intervals along axes */
1638 intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1639 intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1640
1641 /*
1642 * Calculate the overall minimum bounding box over all the entries.
1643 */
1644 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1645 {
1646 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1647 if (i == FirstOffsetNumber)
1648 context.boundingBox = *box;
1649 else
1650 adjustBox(&context.boundingBox, box);
1651 }
1652
1653 POSTGIS_DEBUGF(4, "boundingBox is %s", box2df_to_string(
1654 &context.boundingBox));
1655
1656 /*
1657 * Iterate over axes for optimal split searching.
1658 */
1659 context.first = true; /* nothing selected yet */
1660 for (dim = 0; dim < 2; dim++)
1661 {
1662 float leftUpper,
1663 rightLower;
1664 int i1,
1665 i2;
1666
1667 /* Project each entry as an interval on the selected axis. */
1668 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1669 {
1670 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1671 if (dim == 0)
1672 {
1673 intervalsLower[i - FirstOffsetNumber].lower = box->xmin;
1674 intervalsLower[i - FirstOffsetNumber].upper = box->xmax;
1675 }
1676 else
1677 {
1678 intervalsLower[i - FirstOffsetNumber].lower = box->ymin;
1679 intervalsLower[i - FirstOffsetNumber].upper = box->ymax;
1680 }
1681 }
1682
1683 /*
1684 * Make two arrays of intervals: one sorted by lower bound and another
1685 * sorted by upper bound.
1686 */
1687 memcpy(intervalsUpper, intervalsLower,
1688 sizeof(SplitInterval) * nentries);
1689 qsort(intervalsLower, nentries, sizeof(SplitInterval),
1691 qsort(intervalsUpper, nentries, sizeof(SplitInterval),
1693
1694 /*----
1695 * The goal is to form a left and right interval, so that every entry
1696 * interval is contained by either left or right interval (or both).
1697 *
1698 * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
1699 *
1700 * 0 1 2 3 4
1701 * +-+
1702 * +---+
1703 * +-+
1704 * +---+
1705 *
1706 * The left and right intervals are of the form (0,a) and (b,4).
1707 * We first consider splits where b is the lower bound of an entry.
1708 * We iterate through all entries, and for each b, calculate the
1709 * smallest possible a. Then we consider splits where a is the
1710 * upper bound of an entry, and for each a, calculate the greatest
1711 * possible b.
1712 *
1713 * In the above example, the first loop would consider splits:
1714 * b=0: (0,1)-(0,4)
1715 * b=1: (0,1)-(1,4)
1716 * b=2: (0,3)-(2,4)
1717 *
1718 * And the second loop:
1719 * a=1: (0,1)-(1,4)
1720 * a=3: (0,3)-(2,4)
1721 * a=4: (0,4)-(2,4)
1722 */
1723
1724 /*
1725 * Iterate over lower bound of right group, finding smallest possible
1726 * upper bound of left group.
1727 */
1728 i1 = 0;
1729 i2 = 0;
1730 rightLower = intervalsLower[i1].lower;
1731 leftUpper = intervalsUpper[i2].lower;
1732 while (true)
1733 {
1734 /*
1735 * Find next lower bound of right group.
1736 */
1737 while (i1 < nentries && (rightLower == intervalsLower[i1].lower ||
1738 isnan(intervalsLower[i1].lower)))
1739 {
1740 leftUpper = Max(leftUpper, intervalsLower[i1].upper);
1741 i1++;
1742 }
1743 if (i1 >= nentries)
1744 break;
1745 rightLower = intervalsLower[i1].lower;
1746
1747 /*
1748 * Find count of intervals which anyway should be placed to the
1749 * left group.
1750 */
1751 while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
1752 i2++;
1753
1754 /*
1755 * Consider found split.
1756 */
1757 g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
1758 }
1759
1760 /*
1761 * Iterate over upper bound of left group finding greatest possible
1762 * lower bound of right group.
1763 */
1764 i1 = nentries - 1;
1765 i2 = nentries - 1;
1766 rightLower = intervalsLower[i1].upper;
1767 leftUpper = intervalsUpper[i2].upper;
1768 while (true)
1769 {
1770 /*
1771 * Find next upper bound of left group.
1772 */
1773 while (i2 >= 0 && (leftUpper == intervalsUpper[i2].upper ||
1774 isnan(intervalsUpper[i2].upper)))
1775 {
1776 rightLower = Min(rightLower, intervalsUpper[i2].lower);
1777 i2--;
1778 }
1779 if (i2 < 0)
1780 break;
1781 leftUpper = intervalsUpper[i2].upper;
1782
1783 /*
1784 * Find count of intervals which anyway should be placed to the
1785 * right group.
1786 */
1787 while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
1788 i1--;
1789
1790 /*
1791 * Consider found split.
1792 */
1793 g_box_consider_split(&context, dim,
1794 rightLower, i1 + 1, leftUpper, i2 + 1);
1795 }
1796 }
1797
1798 /*
1799 * If we failed to find any acceptable splits, use trivial split.
1800 */
1801 if (context.first)
1802 {
1803 POSTGIS_DEBUG(4, "no acceptable splits, trivial split");
1804 fallbackSplit(entryvec, v);
1805 PG_RETURN_POINTER(v);
1806 }
1807
1808 /*
1809 * Ok, we have now selected the split across one axis.
1810 *
1811 * While considering the splits, we already determined that there will be
1812 * enough entries in both groups to reach the desired ratio, but we did
1813 * not memorize which entries go to which group. So determine that now.
1814 */
1815
1816 POSTGIS_DEBUGF(4, "split direction: %d", context.dim);
1817
1818 /* Allocate vectors for results */
1819 v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1820 v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1821 v->spl_nleft = 0;
1822 v->spl_nright = 0;
1823
1824 /* Allocate bounding boxes of left and right groups */
1825 leftBox = palloc0(sizeof(BOX2DF));
1826 rightBox = palloc0(sizeof(BOX2DF));
1827
1828 /*
1829 * Allocate an array for "common entries" - entries which can be placed to
1830 * either group without affecting overlap along selected axis.
1831 */
1832 commonEntriesCount = 0;
1833 commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
1834
1835 /* Helper macros to place an entry in the left or right group */
1836#define PLACE_LEFT(box, off) \
1837 do { \
1838 if (v->spl_nleft > 0) \
1839 adjustBox(leftBox, box); \
1840 else \
1841 *leftBox = *(box); \
1842 v->spl_left[v->spl_nleft++] = off; \
1843 } while(0)
1844
1845#define PLACE_RIGHT(box, off) \
1846 do { \
1847 if (v->spl_nright > 0) \
1848 adjustBox(rightBox, box); \
1849 else \
1850 *rightBox = *(box); \
1851 v->spl_right[v->spl_nright++] = off; \
1852 } while(0)
1853
1854 /*
1855 * Distribute entries which can be distributed unambiguously, and collect
1856 * common entries.
1857 */
1858 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1859 {
1860 float lower,
1861 upper;
1862
1863 /*
1864 * Get upper and lower bounds along selected axis.
1865 */
1866 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1867 if (context.dim == 0)
1868 {
1869 lower = box->xmin;
1870 upper = box->xmax;
1871 }
1872 else
1873 {
1874 lower = box->ymin;
1875 upper = box->ymax;
1876 }
1877
1878 if (upper <= context.leftUpper || isnan(upper))
1879 {
1880 /* Fits to the left group */
1881 if (lower >= context.rightLower || isnan(lower))
1882 {
1883 /* Fits also to the right group, so "common entry" */
1884 commonEntries[commonEntriesCount++].index = i;
1885 }
1886 else
1887 {
1888 /* Doesn't fit to the right group, so join to the left group */
1889 PLACE_LEFT(box, i);
1890 }
1891 }
1892 else
1893 {
1894 /*
1895 * Each entry should fit on either left or right group. Since this
1896 * entry didn't fit on the left group, it better fit in the right
1897 * group.
1898 */
1899 Assert(lower >= context.rightLower);
1900
1901 /* Doesn't fit to the left group, so join to the right group */
1902 PLACE_RIGHT(box, i);
1903 }
1904 }
1905
1906 POSTGIS_DEBUGF(4, "leftBox is %s", box2df_to_string(leftBox));
1907 POSTGIS_DEBUGF(4, "rightBox is %s", box2df_to_string(rightBox));
1908
1909 /*
1910 * Distribute "common entries", if any.
1911 */
1912 if (commonEntriesCount > 0)
1913 {
1914 /*
1915 * Calculate minimum number of entries that must be placed in both
1916 * groups, to reach LIMIT_RATIO.
1917 */
1918 int m = ceil(LIMIT_RATIO * (double) nentries);
1919
1920 /*
1921 * Calculate delta between penalties of join "common entries" to
1922 * different groups.
1923 */
1924 for (i = 0; i < commonEntriesCount; i++)
1925 {
1926 box = (BOX2DF *) DatumGetPointer(entryvec->vector[
1927 commonEntries[i].index].key);
1928 commonEntries[i].delta = Abs(box2df_penalty(leftBox, box) - box2df_penalty(rightBox, box));
1929 }
1930
1931 /*
1932 * Sort "common entries" by calculated deltas in order to distribute
1933 * the most ambiguous entries first.
1934 */
1935 qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
1936
1937 /*
1938 * Distribute "common entries" between groups.
1939 */
1940 for (i = 0; i < commonEntriesCount; i++)
1941 {
1942 box = (BOX2DF *) DatumGetPointer(entryvec->vector[
1943 commonEntries[i].index].key);
1944
1945 /*
1946 * Check if we have to place this entry in either group to achieve
1947 * LIMIT_RATIO.
1948 */
1949 if (v->spl_nleft + (commonEntriesCount - i) <= m)
1950 PLACE_LEFT(box, commonEntries[i].index);
1951 else if (v->spl_nright + (commonEntriesCount - i) <= m)
1952 PLACE_RIGHT(box, commonEntries[i].index);
1953 else
1954 {
1955 /* Otherwise select the group by minimal penalty */
1956 if (box2df_penalty(leftBox, box) < box2df_penalty(rightBox, box))
1957 PLACE_LEFT(box, commonEntries[i].index);
1958 else
1959 PLACE_RIGHT(box, commonEntries[i].index);
1960 }
1961 }
1962 }
1963 v->spl_ldatum = PointerGetDatum(leftBox);
1964 v->spl_rdatum = PointerGetDatum(rightBox);
1965
1966 POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
1967
1968 PG_RETURN_POINTER(v);
1969}
static void fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
static char * box2df_to_string(const BOX2DF *a)
static int interval_cmp_upper(const void *i1, const void *i2)
#define LIMIT_RATIO
#define PLACE_RIGHT(box, off)
static void g_box_consider_split(ConsiderSplitContext *context, int dimNum, float rightLower, int minLeftCount, float leftUpper, int maxLeftCount)
static int interval_cmp_lower(const void *i1, const void *i2)
static int common_entry_cmp(const void *i1, const void *i2)
#define PLACE_LEFT(box, off)
static float box2df_penalty(const BOX2DF *b1, const BOX2DF *b2)
static void adjustBox(BOX2DF *b, BOX2DF *addon)

References adjustBox(), ConsiderSplitContext::boundingBox, box2df_penalty(), box2df_to_string(), common_entry_cmp(), CommonEntry::delta, ConsiderSplitContext::dim, ConsiderSplitContext::entriesCount, fallbackSplit(), ConsiderSplitContext::first, g_box_consider_split(), CommonEntry::index, interval_cmp_lower(), interval_cmp_upper(), ConsiderSplitContext::leftUpper, LIMIT_RATIO, SplitInterval::lower, PLACE_LEFT, PLACE_RIGHT, ConsiderSplitContext::rightLower, and SplitInterval::upper.

Here is the call graph for this function: