PostGIS  3.0.6dev-r@@SVN_REVISION@@

◆ 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 char * box2df_to_string(const BOX2DF *a)
static void fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
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: