PostGIS  3.3.9dev-r@@SVN_REVISION@@

◆ compute_gserialized_stats_mode()

static void compute_gserialized_stats_mode ( VacAttrStats *  stats,
AnalyzeAttrFetchFunc  fetchfunc,
int  sample_rows,
double  total_rows,
int  mode 
)
static

The gserialized_analyze_nd sets this function as a callback on the stats object when called by the ANALYZE command.

ANALYZE then gathers the requisite number of sample rows and then calls this function.

We could also pass stats->extra_data in from gserialized_analyze_nd (things like the column type or other stuff from the system catalogs) but so far we don't use that capability.

Our job is to build some statistics on the sample data for use by operator estimators.

We will populate an n-d histogram using the provided sample rows. The selectivity estimators (sel and joinsel) can then use the histogram

Definition at line 1377 of file gserialized_estimate.c.

1379 {
1380  MemoryContext old_context;
1381  int d, i; /* Counters */
1382  int notnull_cnt = 0; /* # not null rows in the sample */
1383  int null_cnt = 0; /* # null rows in the sample */
1384  int histogram_features = 0; /* # rows that actually got counted in the histogram */
1385 
1386  ND_STATS *nd_stats; /* Our histogram */
1387  size_t nd_stats_size; /* Size to allocate */
1388 
1389  double total_width = 0; /* # of bytes used by sample */
1390  double total_cell_count = 0; /* # of cells in histogram affected by sample */
1391 
1392  ND_BOX sum; /* Sum of extents of sample boxes */
1393  ND_BOX avg; /* Avg of extents of sample boxes */
1394  ND_BOX stddev; /* StdDev of extents of sample boxes */
1395 
1396  const ND_BOX **sample_boxes; /* ND_BOXes for each of the sample features */
1397  ND_BOX sample_extent; /* Extent of the raw sample */
1398  int histo_size[ND_DIMS]; /* histogram nrows, ncols, etc */
1399  ND_BOX histo_extent; /* Spatial extent of the histogram */
1400  ND_BOX histo_extent_new; /* Temporary variable */
1401  int histo_cells_target; /* Number of cells we will shoot for, given the stats target */
1402  int histo_cells; /* Number of cells in the histogram */
1403  int histo_cells_new = 1; /* Temporary variable */
1404 
1405  int ndims = 2; /* Dimensionality of the sample */
1406  int histo_ndims = 0; /* Dimensionality of the histogram */
1407  double sample_distribution[ND_DIMS]; /* How homogeneous is distribution of sample in each axis? */
1408  double total_distribution; /* Total of sample_distribution */
1409 
1410  int stats_slot; /* What slot is this data going into? (2D vs ND) */
1411  int stats_kind; /* And this is what? (2D vs ND) */
1412 
1413  /* Initialize sum and stddev */
1414  nd_box_init(&sum);
1415  nd_box_init(&stddev);
1416  nd_box_init(&avg);
1417  nd_box_init(&histo_extent);
1418  nd_box_init(&histo_extent_new);
1419 
1420  /*
1421  * This is where gserialized_analyze_nd
1422  * should put its' custom parameters.
1423  */
1424  /* void *mystats = stats->extra_data; */
1425 
1426  POSTGIS_DEBUG(2, "compute_gserialized_stats called");
1427  POSTGIS_DEBUGF(3, " # sample_rows: %d", sample_rows);
1428  POSTGIS_DEBUGF(3, " estimate of total_rows: %.6g", total_rows);
1429 
1430  /*
1431  * We might need less space, but don't think
1432  * its worth saving...
1433  */
1434  sample_boxes = palloc(sizeof(ND_BOX*) * sample_rows);
1435 
1436  /*
1437  * First scan:
1438  * o read boxes
1439  * o find dimensionality of the sample
1440  * o find extent of the sample
1441  * o count null-infinite/not-null values
1442  * o compute total_width
1443  * o compute total features's box area (for avgFeatureArea)
1444  * o sum features box coordinates (for standard deviation)
1445  */
1446  for ( i = 0; i < sample_rows; i++ )
1447  {
1448  Datum datum;
1449  GBOX gbox = {0};
1450  ND_BOX *nd_box;
1451  bool is_null;
1452 
1453  datum = fetchfunc(stats, i, &is_null);
1454 
1455  /* Skip all NULLs. */
1456  if ( is_null )
1457  {
1458  POSTGIS_DEBUGF(4, " skipped null geometry %d", i);
1459  null_cnt++;
1460  continue;
1461  }
1462 
1463  /* Read the bounds from the gserialized. */
1464  if (LW_FAILURE == gserialized_datum_get_gbox_p(datum, &gbox))
1465  {
1466  /* Skip empties too. */
1467  POSTGIS_DEBUGF(3, " skipped empty geometry %d", i);
1468  continue;
1469  }
1470 
1471  /* If we're in 2D mode, zero out the higher dimensions for "safety" */
1472  if ( mode == 2 )
1473  gbox.zmin = gbox.zmax = gbox.mmin = gbox.mmax = 0.0;
1474 
1475  /* Check bounds for validity (finite and not NaN) */
1476  if ( ! gbox_is_valid(&gbox) )
1477  {
1478  POSTGIS_DEBUGF(3, " skipped infinite/nan geometry %d", i);
1479  continue;
1480  }
1481 
1482  /*
1483  * In N-D mode, set the ndims to the maximum dimensionality found
1484  * in the sample. Otherwise, leave at ndims == 2.
1485  */
1486  if ( mode != 2 )
1487  ndims = Max(gbox_ndims(&gbox), ndims);
1488 
1489  /* Convert gbox to n-d box */
1490  nd_box = palloc(sizeof(ND_BOX));
1491  nd_box_from_gbox(&gbox, nd_box);
1492 
1493  /* Cache n-d bounding box */
1494  sample_boxes[notnull_cnt] = nd_box;
1495 
1496  /* Initialize sample extent before merging first entry */
1497  if ( ! notnull_cnt )
1498  nd_box_init_bounds(&sample_extent);
1499 
1500  /* Add current sample to overall sample extent */
1501  nd_box_merge(nd_box, &sample_extent);
1502 
1503  /* How many bytes does this sample use? */
1504  total_width += toast_raw_datum_size(datum);
1505 
1506  /* Add bounds coordinates to sums for stddev calculation */
1507  for ( d = 0; d < ndims; d++ )
1508  {
1509  sum.min[d] += nd_box->min[d];
1510  sum.max[d] += nd_box->max[d];
1511  }
1512 
1513  /* Increment our "good feature" count */
1514  notnull_cnt++;
1515 
1516  /* Give backend a chance of interrupting us */
1517  vacuum_delay_point();
1518  }
1519 
1520  /*
1521  * We'll build a histogram having stats->attr->attstattarget cells
1522  * on each side, within reason... we'll use ndims*10000 as the
1523  * maximum number of cells.
1524  * Also, if we're sampling a relatively small table, we'll try to ensure that
1525  * we have an average of 5 features for each cell so the histogram isn't
1526  * so sparse.
1527  */
1528 #if POSTGIS_PGSQL_VERSION >= 170
1529  histo_cells_target = (int)pow((double)(stats->attstattarget), (double)ndims);
1530 #else
1531  histo_cells_target = (int)pow((double)(stats->attr->attstattarget), (double)ndims);
1532 #endif
1533  histo_cells_target = Min(histo_cells_target, ndims * 10000);
1534  histo_cells_target = Min(histo_cells_target, (int)(total_rows/5));
1535  POSTGIS_DEBUGF(3, " stats->attr->attstattarget: %d", stats->attr->attstattarget);
1536  POSTGIS_DEBUGF(3, " target # of histogram cells: %d", histo_cells_target);
1537 
1538  /* If there's no useful features, we can't work out stats */
1539  if ( ! notnull_cnt )
1540  {
1541  stats->stats_valid = false;
1542  return;
1543  }
1544 
1545  POSTGIS_DEBUGF(3, " sample_extent: %s", nd_box_to_json(&sample_extent, ndims));
1546 
1547  /*
1548  * Second scan:
1549  * o compute standard deviation
1550  */
1551  for ( d = 0; d < ndims; d++ )
1552  {
1553  /* Calculate average bounds values */
1554  avg.min[d] = sum.min[d] / notnull_cnt;
1555  avg.max[d] = sum.max[d] / notnull_cnt;
1556 
1557  /* Calculate standard deviation for this dimension bounds */
1558  for ( i = 0; i < notnull_cnt; i++ )
1559  {
1560  const ND_BOX *ndb = sample_boxes[i];
1561  stddev.min[d] += (ndb->min[d] - avg.min[d]) * (ndb->min[d] - avg.min[d]);
1562  stddev.max[d] += (ndb->max[d] - avg.max[d]) * (ndb->max[d] - avg.max[d]);
1563  }
1564  stddev.min[d] = sqrt(stddev.min[d] / notnull_cnt);
1565  stddev.max[d] = sqrt(stddev.max[d] / notnull_cnt);
1566 
1567  /* Histogram bounds for this dimension bounds is avg +/- SDFACTOR * stdev */
1568  histo_extent.min[d] = Max(avg.min[d] - SDFACTOR * stddev.min[d], sample_extent.min[d]);
1569  histo_extent.max[d] = Min(avg.max[d] + SDFACTOR * stddev.max[d], sample_extent.max[d]);
1570  }
1571 
1572  /*
1573  * Third scan:
1574  * o skip hard deviants
1575  * o compute new histogram box
1576  */
1577  nd_box_init_bounds(&histo_extent_new);
1578  for ( i = 0; i < notnull_cnt; i++ )
1579  {
1580  const ND_BOX *ndb = sample_boxes[i];
1581  /* Skip any hard deviants (boxes entirely outside our histo_extent */
1582  if ( ! nd_box_intersects(&histo_extent, ndb, ndims) )
1583  {
1584  POSTGIS_DEBUGF(4, " feature %d is a hard deviant, skipped", i);
1585  sample_boxes[i] = NULL;
1586  continue;
1587  }
1588  /* Expand our new box to fit all the other features. */
1589  nd_box_merge(ndb, &histo_extent_new);
1590  }
1591  /*
1592  * Expand the box slightly (1%) to avoid edge effects
1593  * with objects that are on the boundary
1594  */
1595  nd_box_expand(&histo_extent_new, 0.01);
1596  histo_extent = histo_extent_new;
1597 
1598  /*
1599  * How should we allocate our histogram cells to the
1600  * different dimensions? We can't do it by raw dimensional width,
1601  * because in x/y/z space, the z can have different units
1602  * from the x/y. Similarly for x/y/t space.
1603  * So, we instead calculate how much features overlap
1604  * each other in their dimension to figure out which
1605  * dimensions have useful selectivity characteristics (more
1606  * variability in density) and therefor would find
1607  * more cells useful (to distinguish between dense places and
1608  * homogeneous places).
1609  */
1610  nd_box_array_distribution(sample_boxes, notnull_cnt, &histo_extent, ndims,
1611  sample_distribution);
1612 
1613  /*
1614  * The sample_distribution array now tells us how spread out the
1615  * data is in each dimension, so we use that data to allocate
1616  * the histogram cells we have available.
1617  * At this point, histo_cells_target is the approximate target number
1618  * of cells.
1619  */
1620 
1621  /*
1622  * Some dimensions have basically a uniform distribution, we want
1623  * to allocate no cells to those dimensions, only to dimensions
1624  * that have some interesting differences in data distribution.
1625  * Here we count up the number of interesting dimensions
1626  */
1627  for ( d = 0; d < ndims; d++ )
1628  {
1629  if ( sample_distribution[d] > 0 )
1630  histo_ndims++;
1631  }
1632 
1633  if ( histo_ndims == 0 )
1634  {
1635  /* Special case: all our dimensions had low variability! */
1636  /* We just divide the cells up evenly */
1637  POSTGIS_DEBUG(3, " special case: no axes have variability");
1638  histo_cells_new = 1;
1639  for ( d = 0; d < ndims; d++ )
1640  {
1641  histo_size[d] = (int)pow((double)histo_cells_target, 1/(double)ndims);
1642  if ( ! histo_size[d] )
1643  histo_size[d] = 1;
1644  POSTGIS_DEBUGF(3, " histo_size[d]: %d", histo_size[d]);
1645  histo_cells_new *= histo_size[d];
1646  }
1647  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1648  }
1649  else
1650  {
1651  /*
1652  * We're going to express the amount of variability in each dimension
1653  * as a proportion of the total variability and allocate cells in that
1654  * dimension relative to that proportion.
1655  */
1656  POSTGIS_DEBUG(3, " allocating histogram axes based on axis variability");
1657  total_distribution = total_double(sample_distribution, ndims); /* First get the total */
1658  POSTGIS_DEBUGF(3, " total_distribution: %.8g", total_distribution);
1659  histo_cells_new = 1; /* For the number of cells in the final histogram */
1660  for ( d = 0; d < ndims; d++ )
1661  {
1662  if ( sample_distribution[d] == 0 ) /* Uninteresting dimensions don't get any room */
1663  {
1664  histo_size[d] = 1;
1665  }
1666  else /* Interesting dimension */
1667  {
1668  /* How does this dims variability compare to the total? */
1669  float edge_ratio = (float)sample_distribution[d] / (float)total_distribution;
1670  /*
1671  * Scale the target cells number by the # of dims and ratio,
1672  * then take the appropriate root to get the estimated number of cells
1673  * on this axis (eg, pow(0.5) for 2d, pow(0.333) for 3d, pow(0.25) for 4d)
1674  */
1675  histo_size[d] = (int)pow(histo_cells_target * histo_ndims * edge_ratio, 1/(double)histo_ndims);
1676  /* If something goes awry, just give this dim one slot */
1677  if ( ! histo_size[d] )
1678  histo_size[d] = 1;
1679  }
1680  histo_cells_new *= histo_size[d];
1681  }
1682  POSTGIS_DEBUGF(3, " histo_cells_new: %d", histo_cells_new);
1683  }
1684 
1685  /* Update histo_cells to the actual number of cells we need to allocate */
1686  histo_cells = histo_cells_new;
1687  POSTGIS_DEBUGF(3, " histo_cells: %d", histo_cells);
1688 
1689  /*
1690  * Create the histogram (ND_STATS) in the stats memory context
1691  */
1692  old_context = MemoryContextSwitchTo(stats->anl_context);
1693  nd_stats_size = sizeof(ND_STATS) + ((histo_cells - 1) * sizeof(float4));
1694  nd_stats = palloc(nd_stats_size);
1695  memset(nd_stats, 0, nd_stats_size); /* Initialize all values to 0 */
1696  MemoryContextSwitchTo(old_context);
1697 
1698  /* Initialize the #ND_STATS objects */
1699  nd_stats->ndims = ndims;
1700  nd_stats->extent = histo_extent;
1701  nd_stats->sample_features = sample_rows;
1702  nd_stats->table_features = total_rows;
1703  nd_stats->not_null_features = notnull_cnt;
1704  /* Copy in the histogram dimensions */
1705  for ( d = 0; d < ndims; d++ )
1706  nd_stats->size[d] = histo_size[d];
1707 
1708  /*
1709  * Fourth scan:
1710  * o fill histogram values with the proportion of
1711  * features' bbox overlaps: a feature's bvol
1712  * can fully overlap (1) or partially overlap
1713  * (fraction of 1) an histogram cell.
1714  *
1715  * Note that we are filling each cell with the "portion of
1716  * the feature's box that overlaps the cell". So, if we sum
1717  * up the values in the histogram, we could get the
1718  * histogram feature count.
1719  *
1720  */
1721  for ( i = 0; i < notnull_cnt; i++ )
1722  {
1723  const ND_BOX *nd_box;
1724  ND_IBOX nd_ibox;
1725  int at[ND_DIMS];
1726  double num_cells = 0;
1727  double min[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1728  double max[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1729  double cellsize[ND_DIMS] = {0.0, 0.0, 0.0, 0.0};
1730 
1731  nd_box = sample_boxes[i];
1732  if ( ! nd_box ) continue; /* Skip Null'ed out hard deviants */
1733 
1734  /* Give backend a chance of interrupting us */
1735  vacuum_delay_point();
1736 
1737  /* Find the cells that overlap with this box and put them into the ND_IBOX */
1738  nd_box_overlap(nd_stats, nd_box, &nd_ibox);
1739  memset(at, 0, sizeof(int)*ND_DIMS);
1740 
1741  POSTGIS_DEBUGF(3, " feature %d: ibox (%d, %d, %d, %d) (%d, %d, %d, %d)", i,
1742  nd_ibox.min[0], nd_ibox.min[1], nd_ibox.min[2], nd_ibox.min[3],
1743  nd_ibox.max[0], nd_ibox.max[1], nd_ibox.max[2], nd_ibox.max[3]);
1744 
1745  for ( d = 0; d < nd_stats->ndims; d++ )
1746  {
1747  /* Initialize the starting values */
1748  at[d] = nd_ibox.min[d];
1749  min[d] = nd_stats->extent.min[d];
1750  max[d] = nd_stats->extent.max[d];
1751  cellsize[d] = (max[d] - min[d])/(nd_stats->size[d]);
1752  }
1753 
1754  /*
1755  * Move through all the overlaped histogram cells values and
1756  * add the box overlap proportion to them.
1757  */
1758  do
1759  {
1760  ND_BOX nd_cell = { {0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0} };
1761  double ratio;
1762  /* Create a box for this histogram cell */
1763  for ( d = 0; d < nd_stats->ndims; d++ )
1764  {
1765  nd_cell.min[d] = min[d] + (at[d]+0) * cellsize[d];
1766  nd_cell.max[d] = min[d] + (at[d]+1) * cellsize[d];
1767  }
1768 
1769  /*
1770  * If a feature box is completely inside one cell the ratio will be
1771  * 1.0. If a feature box is 50% in two cells, each cell will get
1772  * 0.5 added on.
1773  */
1774  ratio = nd_box_ratio(&nd_cell, nd_box, nd_stats->ndims);
1775  nd_stats->value[nd_stats_value_index(nd_stats, at)] += ratio;
1776  num_cells += ratio;
1777  POSTGIS_DEBUGF(3, " ratio (%.8g) num_cells (%.8g)", ratio, num_cells);
1778  POSTGIS_DEBUGF(3, " at (%d, %d, %d, %d)", at[0], at[1], at[2], at[3]);
1779  }
1780  while ( nd_increment(&nd_ibox, nd_stats->ndims, at) );
1781 
1782  /* Keep track of overall number of overlaps counted */
1783  total_cell_count += num_cells;
1784  /* How many features have we added to this histogram? */
1785  histogram_features++;
1786  }
1787 
1788  POSTGIS_DEBUGF(3, " histogram_features: %d", histogram_features);
1789  POSTGIS_DEBUGF(3, " sample_rows: %d", sample_rows);
1790  POSTGIS_DEBUGF(3, " table_rows: %.6g", total_rows);
1791 
1792  /* Error out if we got no sample information */
1793  if ( ! histogram_features )
1794  {
1795  POSTGIS_DEBUG(3, " no stats have been gathered");
1796  elog(NOTICE, " no features lie in the stats histogram, invalid stats");
1797  stats->stats_valid = false;
1798  return;
1799  }
1800 
1801  nd_stats->histogram_features = histogram_features;
1802  nd_stats->histogram_cells = histo_cells;
1803  nd_stats->cells_covered = total_cell_count;
1804 
1805  /* Put this histogram data into the right slot/kind */
1806  if ( mode == 2 )
1807  {
1808  stats_slot = STATISTIC_SLOT_2D;
1809  stats_kind = STATISTIC_KIND_2D;
1810  }
1811  else
1812  {
1813  stats_slot = STATISTIC_SLOT_ND;
1814  stats_kind = STATISTIC_KIND_ND;
1815  }
1816 
1817  /* Write the statistics data */
1818  stats->stakind[stats_slot] = stats_kind;
1819  stats->staop[stats_slot] = InvalidOid;
1820  stats->stanumbers[stats_slot] = (float4*)nd_stats;
1821  stats->numnumbers[stats_slot] = nd_stats_size/sizeof(float4);
1822  stats->stanullfrac = (float4)null_cnt/sample_rows;
1823  stats->stawidth = total_width/notnull_cnt;
1824  stats->stadistinct = -1.0;
1825  stats->stats_valid = true;
1826 
1827  POSTGIS_DEBUGF(3, " out: slot 0: kind %d (STATISTIC_KIND_ND)", stats->stakind[0]);
1828  POSTGIS_DEBUGF(3, " out: slot 0: op %d (InvalidOid)", stats->staop[0]);
1829  POSTGIS_DEBUGF(3, " out: slot 0: numnumbers %d", stats->numnumbers[0]);
1830  POSTGIS_DEBUGF(3, " out: null fraction: %f=%d/%d", stats->stanullfrac, null_cnt, sample_rows);
1831  POSTGIS_DEBUGF(3, " out: average width: %d bytes", stats->stawidth);
1832  POSTGIS_DEBUG (3, " out: distinct values: all (no check done)");
1833  POSTGIS_DEBUGF(3, " out: %s", nd_stats_to_json(nd_stats));
1834  /*
1835  POSTGIS_DEBUGF(3, " out histogram:\n%s", nd_stats_to_grid(nd_stats));
1836  */
1837 
1838  return;
1839 }
int gbox_is_valid(const GBOX *gbox)
Return false if any of the dimensions is NaN or infinite.
Definition: gbox.c:197
struct ND_STATS_T ND_STATS
N-dimensional statistics structure.
static char * nd_stats_to_json(const ND_STATS *nd_stats)
Convert an ND_STATS to a JSON representation for external use.
static int nd_box_intersects(const ND_BOX *a, const ND_BOX *b, int ndims)
Return true if ND_BOX a overlaps b, false otherwise.
static int nd_box_init_bounds(ND_BOX *a)
Prepare an ND_BOX for bounds calculation: set the maxes to the smallest thing possible and the mins t...
static int nd_increment(ND_IBOX *ibox, int ndims, int *counter)
Given an n-d index array (counter), and a domain to increment it in (ibox) increment it by one,...
#define STATISTIC_SLOT_ND
static int gbox_ndims(const GBOX *gbox)
Given that geodetic boxes are X/Y/Z regardless of the underlying geometry dimensionality and other bo...
#define ND_DIMS
The maximum number of dimensions our code can handle.
#define STATISTIC_KIND_2D
static int nd_box_merge(const ND_BOX *source, ND_BOX *target)
Create a printable view of the ND_STATS histogram.
#define STATISTIC_KIND_ND
static double total_double(const double *vals, int nvals)
Given double array, return sum of values.
#define SDFACTOR
static void nd_box_from_gbox(const GBOX *gbox, ND_BOX *nd_box)
Set the values of an ND_BOX from a GBOX.
static int nd_box_init(ND_BOX *a)
Zero out an ND_BOX.
static int nd_box_expand(ND_BOX *nd_box, double expansion_factor)
Expand an ND_BOX ever so slightly.
static int nd_box_overlap(const ND_STATS *nd_stats, const ND_BOX *nd_box, ND_IBOX *nd_ibox)
What stats cells overlap with this ND_BOX? Put the lowest cell addresses in ND_IBOX->min and the high...
static double nd_box_ratio(const ND_BOX *b1, const ND_BOX *b2, int ndims)
Returns the proportion of b2 that is covered by b1.
static int nd_stats_value_index(const ND_STATS *stats, int *indexes)
Given a position in the n-d histogram (i,j,k) return the position in the 1-d values array.
static char * nd_box_to_json(const ND_BOX *nd_box, int ndims)
Convert an ND_BOX to a JSON string for printing.
#define STATISTIC_SLOT_2D
static int nd_box_array_distribution(const ND_BOX **nd_boxes, int num_boxes, const ND_BOX *extent, int ndims, double *distribution)
Calculate how much a set of boxes is homogenously distributed or contentrated within one dimension,...
int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
Given a GSERIALIZED datum, as quickly as possible (peaking into the top of the memory) return the gbo...
#define LW_FAILURE
Definition: liblwgeom.h:111
double zmax
Definition: liblwgeom.h:374
double zmin
Definition: liblwgeom.h:373
double mmax
Definition: liblwgeom.h:376
double mmin
Definition: liblwgeom.h:375
float4 max[ND_DIMS]
float4 min[ND_DIMS]
N-dimensional box type for calculations, to avoid doing explicit axis conversions from GBOX in all ca...
int max[ND_DIMS]
int min[ND_DIMS]
N-dimensional box index type.
float4 size[ND_DIMS]
N-dimensional statistics structure.

References ND_STATS_T::cells_covered, ND_STATS_T::extent, gbox_is_valid(), gbox_ndims(), gserialized_datum_get_gbox_p(), ND_STATS_T::histogram_cells, ND_STATS_T::histogram_features, LW_FAILURE, ND_BOX_T::max, ND_IBOX_T::max, ND_BOX_T::min, ND_IBOX_T::min, GBOX::mmax, GBOX::mmin, nd_box_array_distribution(), nd_box_expand(), nd_box_from_gbox(), nd_box_init(), nd_box_init_bounds(), nd_box_intersects(), nd_box_merge(), nd_box_overlap(), nd_box_ratio(), nd_box_to_json(), ND_DIMS, nd_increment(), nd_stats_to_json(), nd_stats_value_index(), ND_STATS_T::ndims, ND_STATS_T::not_null_features, ND_STATS_T::sample_features, SDFACTOR, ND_STATS_T::size, STATISTIC_KIND_2D, STATISTIC_KIND_ND, STATISTIC_SLOT_2D, STATISTIC_SLOT_ND, ND_STATS_T::table_features, total_double(), ND_STATS_T::value, GBOX::zmax, and GBOX::zmin.

Referenced by compute_gserialized_stats().

Here is the call graph for this function:
Here is the caller graph for this function: