PostgreSQL Source Code  git master
nbtutils.c File Reference
#include "postgres.h"
#include <time.h>
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
#include "commands/progress.h"
#include "lib/qunique.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for nbtutils.c:

Go to the source code of this file.

Data Structures

struct  BTSortArrayContext
 
struct  BTScanKeyPreproc
 
struct  BTOneVacInfo
 
struct  BTVacInfo
 

Macros

#define LOOK_AHEAD_REQUIRED_RECHECKS   3
 
#define LOOK_AHEAD_DEFAULT_DISTANCE   5
 

Typedefs

typedef struct BTSortArrayContext BTSortArrayContext
 
typedef struct BTScanKeyPreproc BTScanKeyPreproc
 
typedef struct BTOneVacInfo BTOneVacInfo
 
typedef struct BTVacInfo BTVacInfo
 

Functions

static void _bt_setup_array_cmp (IndexScanDesc scan, ScanKey skey, Oid elemtype, FmgrInfo *orderproc, FmgrInfo **sortprocp)
 
static Datum _bt_find_extreme_element (IndexScanDesc scan, ScanKey skey, Oid elemtype, StrategyNumber strat, Datum *elems, int nelems)
 
static int _bt_sort_array_elements (ScanKey skey, FmgrInfo *sortproc, bool reverse, Datum *elems, int nelems)
 
static bool _bt_merge_arrays (IndexScanDesc scan, ScanKey skey, FmgrInfo *sortproc, bool reverse, Oid origelemtype, Oid nextelemtype, Datum *elems_orig, int *nelems_orig, Datum *elems_next, int nelems_next)
 
static bool _bt_compare_array_scankey_args (IndexScanDesc scan, ScanKey arraysk, ScanKey skey, FmgrInfo *orderproc, BTArrayKeyInfo *array, bool *qual_ok)
 
static ScanKey _bt_preprocess_array_keys (IndexScanDesc scan)
 
static void _bt_preprocess_array_keys_final (IndexScanDesc scan, int *keyDataMap)
 
static int _bt_compare_array_elements (const void *a, const void *b, void *arg)
 
static int32 _bt_compare_array_skey (FmgrInfo *orderproc, Datum tupdatum, bool tupnull, Datum arrdatum, ScanKey cur)
 
static int _bt_binsrch_array_skey (FmgrInfo *orderproc, bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
 
static bool _bt_advance_array_keys_increment (IndexScanDesc scan, ScanDirection dir)
 
static void _bt_rewind_nonrequired_arrays (IndexScanDesc scan, ScanDirection dir)
 
static bool _bt_tuple_before_array_skeys (IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, TupleDesc tupdesc, int tupnatts, bool readpagetup, int sktrig, bool *scanBehind)
 
static bool _bt_advance_array_keys (IndexScanDesc scan, BTReadPageState *pstate, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, int sktrig, bool sktrig_required)
 
static bool _bt_compare_scankey_args (IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, BTArrayKeyInfo *array, FmgrInfo *orderproc, bool *result)
 
static bool _bt_fix_scankey_strategy (ScanKey skey, int16 *indoption)
 
static void _bt_mark_scankey_required (ScanKey skey)
 
static bool _bt_check_compare (IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, bool advancenonrequired, bool prechecked, bool firstmatch, bool *continuescan, int *ikey)
 
static bool _bt_check_rowcompare (ScanKey skey, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, ScanDirection dir, bool *continuescan)
 
static void _bt_checkkeys_look_ahead (IndexScanDesc scan, BTReadPageState *pstate, int tupnatts, TupleDesc tupdesc)
 
static int _bt_keep_natts (Relation rel, IndexTuple lastleft, IndexTuple firstright, BTScanInsert itup_key)
 
BTScanInsert _bt_mkscankey (Relation rel, IndexTuple itup)
 
void _bt_freestack (BTStack stack)
 
void _bt_start_array_keys (IndexScanDesc scan, ScanDirection dir)
 
bool _bt_start_prim_scan (IndexScanDesc scan, ScanDirection dir)
 
void _bt_preprocess_keys (IndexScanDesc scan)
 
bool _bt_checkkeys (IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys, IndexTuple tuple, int tupnatts)
 
void _bt_killitems (IndexScanDesc scan)
 
BTCycleId _bt_vacuum_cycleid (Relation rel)
 
BTCycleId _bt_start_vacuum (Relation rel)
 
void _bt_end_vacuum (Relation rel)
 
void _bt_end_vacuum_callback (int code, Datum arg)
 
Size BTreeShmemSize (void)
 
void BTreeShmemInit (void)
 
byteabtoptions (Datum reloptions, bool validate)
 
bool btproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 
char * btbuildphasename (int64 phasenum)
 
IndexTuple _bt_truncate (Relation rel, IndexTuple lastleft, IndexTuple firstright, BTScanInsert itup_key)
 
int _bt_keep_natts_fast (Relation rel, IndexTuple lastleft, IndexTuple firstright)
 
bool _bt_check_natts (Relation rel, bool heapkeyspace, Page page, OffsetNumber offnum)
 
void _bt_check_third_page (Relation rel, Relation heap, bool needheaptidspace, Page page, IndexTuple newtup)
 
bool _bt_allequalimage (Relation rel, bool debugmessage)
 

Variables

static BTVacInfobtvacinfo
 

Macro Definition Documentation

◆ LOOK_AHEAD_DEFAULT_DISTANCE

#define LOOK_AHEAD_DEFAULT_DISTANCE   5

Definition at line 33 of file nbtutils.c.

◆ LOOK_AHEAD_REQUIRED_RECHECKS

#define LOOK_AHEAD_REQUIRED_RECHECKS   3

Definition at line 32 of file nbtutils.c.

Typedef Documentation

◆ BTOneVacInfo

typedef struct BTOneVacInfo BTOneVacInfo

◆ BTScanKeyPreproc

◆ BTSortArrayContext

◆ BTVacInfo

typedef struct BTVacInfo BTVacInfo

Function Documentation

◆ _bt_advance_array_keys()

static bool _bt_advance_array_keys ( IndexScanDesc  scan,
BTReadPageState pstate,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
int  sktrig,
bool  sktrig_required 
)
static

Definition at line 1789 of file nbtutils.c.

1792 {
1793  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1794  Relation rel = scan->indexRelation;
1795  ScanDirection dir = pstate ? pstate->dir : ForwardScanDirection;
1796  int arrayidx = 0;
1797  bool beyond_end_advance = false,
1798  has_required_opposite_direction_only = false,
1799  oppodir_inequality_sktrig = false,
1800  all_required_satisfied = true,
1801  all_satisfied = true;
1802 
1803  if (sktrig_required)
1804  {
1805  /*
1806  * Precondition array state assertion
1807  */
1808  Assert(!_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc,
1809  tupnatts, false, 0, NULL));
1810 
1811  so->scanBehind = false; /* reset */
1812 
1813  /*
1814  * Required scan key wasn't satisfied, so required arrays will have to
1815  * advance. Invalidate page-level state that tracks whether the
1816  * scan's required-in-opposite-direction-only keys are known to be
1817  * satisfied by page's remaining tuples.
1818  */
1819  pstate->firstmatch = false;
1820 
1821  /* Shouldn't have to invalidate 'prechecked', though */
1822  Assert(!pstate->prechecked);
1823 
1824  /*
1825  * Once we return we'll have a new set of required array keys, so
1826  * reset state used by "look ahead" optimization
1827  */
1828  pstate->rechecks = 0;
1829  pstate->targetdistance = 0;
1830  }
1831 
1832  Assert(_bt_verify_keys_with_arraykeys(scan));
1833 
1834  for (int ikey = 0; ikey < so->numberOfKeys; ikey++)
1835  {
1836  ScanKey cur = so->keyData + ikey;
1837  BTArrayKeyInfo *array = NULL;
1838  Datum tupdatum;
1839  bool required = false,
1840  required_opposite_direction_only = false,
1841  tupnull;
1842  int32 result;
1843  int set_elem = 0;
1844 
1845  if (cur->sk_strategy == BTEqualStrategyNumber)
1846  {
1847  /* Manage array state */
1848  if (cur->sk_flags & SK_SEARCHARRAY)
1849  {
1850  array = &so->arrayKeys[arrayidx++];
1851  Assert(array->scan_key == ikey);
1852  }
1853  }
1854  else
1855  {
1856  /*
1857  * Are any inequalities required in the opposite direction only
1858  * present here?
1859  */
1860  if (((ScanDirectionIsForward(dir) &&
1861  (cur->sk_flags & (SK_BT_REQBKWD))) ||
1862  (ScanDirectionIsBackward(dir) &&
1863  (cur->sk_flags & (SK_BT_REQFWD)))))
1864  has_required_opposite_direction_only =
1865  required_opposite_direction_only = true;
1866  }
1867 
1868  /* Optimization: skip over known-satisfied scan keys */
1869  if (ikey < sktrig)
1870  continue;
1871 
1872  if (cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))
1873  {
1874  Assert(sktrig_required);
1875 
1876  required = true;
1877 
1878  if (cur->sk_attno > tupnatts)
1879  {
1880  /* Set this just like _bt_tuple_before_array_skeys */
1881  Assert(sktrig < ikey);
1882  so->scanBehind = true;
1883  }
1884  }
1885 
1886  /*
1887  * Handle a required non-array scan key that the initial call to
1888  * _bt_check_compare indicated triggered array advancement, if any.
1889  *
1890  * The non-array scan key's strategy will be <, <=, or = during a
1891  * forwards scan (or any one of =, >=, or > during a backwards scan).
1892  * It follows that the corresponding tuple attribute's value must now
1893  * be either > or >= the scan key value (for backwards scans it must
1894  * be either < or <= that value).
1895  *
1896  * If this is a required equality strategy scan key, this is just an
1897  * optimization; _bt_tuple_before_array_skeys already confirmed that
1898  * this scan key places us ahead of caller's tuple. There's no need
1899  * to repeat that work now. (The same underlying principle also gets
1900  * applied by the cur_elem_trig optimization used to speed up searches
1901  * for the next array element.)
1902  *
1903  * If this is a required inequality strategy scan key, we _must_ rely
1904  * on _bt_check_compare like this; we aren't capable of directly
1905  * evaluating required inequality strategy scan keys here, on our own.
1906  */
1907  if (ikey == sktrig && !array)
1908  {
1909  Assert(sktrig_required && required && all_required_satisfied);
1910 
1911  /* Use "beyond end" advancement. See below for an explanation. */
1912  beyond_end_advance = true;
1913  all_satisfied = all_required_satisfied = false;
1914 
1915  /*
1916  * Set a flag that remembers that this was an inequality required
1917  * in the opposite scan direction only, that nevertheless
1918  * triggered the call here.
1919  *
1920  * This only happens when an inequality operator (which must be
1921  * strict) encounters a group of NULLs that indicate the end of
1922  * non-NULL values for tuples in the current scan direction.
1923  */
1924  if (unlikely(required_opposite_direction_only))
1925  oppodir_inequality_sktrig = true;
1926 
1927  continue;
1928  }
1929 
1930  /*
1931  * Nothing more for us to do with an inequality strategy scan key that
1932  * wasn't the one that _bt_check_compare stopped on, though.
1933  *
1934  * Note: if our later call to _bt_check_compare (to recheck caller's
1935  * tuple) sets continuescan=false due to finding this same inequality
1936  * unsatisfied (possible when it's required in the scan direction),
1937  * we'll deal with it via a recursive "second pass" call.
1938  */
1939  else if (cur->sk_strategy != BTEqualStrategyNumber)
1940  continue;
1941 
1942  /*
1943  * Nothing for us to do with an equality strategy scan key that isn't
1944  * marked required, either -- unless it's a non-required array
1945  */
1946  else if (!required && !array)
1947  continue;
1948 
1949  /*
1950  * Here we perform steps for all array scan keys after a required
1951  * array scan key whose binary search triggered "beyond end of array
1952  * element" array advancement due to encountering a tuple attribute
1953  * value > the closest matching array key (or < for backwards scans).
1954  */
1955  if (beyond_end_advance)
1956  {
1957  int final_elem_dir;
1958 
1959  if (ScanDirectionIsBackward(dir) || !array)
1960  final_elem_dir = 0;
1961  else
1962  final_elem_dir = array->num_elems - 1;
1963 
1964  if (array && array->cur_elem != final_elem_dir)
1965  {
1966  array->cur_elem = final_elem_dir;
1967  cur->sk_argument = array->elem_values[final_elem_dir];
1968  }
1969 
1970  continue;
1971  }
1972 
1973  /*
1974  * Here we perform steps for all array scan keys after a required
1975  * array scan key whose tuple attribute was < the closest matching
1976  * array key when we dealt with it (or > for backwards scans).
1977  *
1978  * This earlier required array key already puts us ahead of caller's
1979  * tuple in the key space (for the current scan direction). We must
1980  * make sure that subsequent lower-order array keys do not put us too
1981  * far ahead (ahead of tuples that have yet to be seen by our caller).
1982  * For example, when a tuple "(a, b) = (42, 5)" advances the array
1983  * keys on "a" from 40 to 45, we must also set "b" to whatever the
1984  * first array element for "b" is. It would be wrong to allow "b" to
1985  * be set based on the tuple value.
1986  *
1987  * Perform the same steps with truncated high key attributes. You can
1988  * think of this as a "binary search" for the element closest to the
1989  * value -inf. Again, the arrays must never get ahead of the scan.
1990  */
1991  if (!all_required_satisfied || cur->sk_attno > tupnatts)
1992  {
1993  int first_elem_dir;
1994 
1995  if (ScanDirectionIsForward(dir) || !array)
1996  first_elem_dir = 0;
1997  else
1998  first_elem_dir = array->num_elems - 1;
1999 
2000  if (array && array->cur_elem != first_elem_dir)
2001  {
2002  array->cur_elem = first_elem_dir;
2003  cur->sk_argument = array->elem_values[first_elem_dir];
2004  }
2005 
2006  continue;
2007  }
2008 
2009  /*
2010  * Search in scankey's array for the corresponding tuple attribute
2011  * value from caller's tuple
2012  */
2013  tupdatum = index_getattr(tuple, cur->sk_attno, tupdesc, &tupnull);
2014 
2015  if (array)
2016  {
2017  bool cur_elem_trig = (sktrig_required && ikey == sktrig);
2018 
2019  /*
2020  * Binary search for closest match that's available from the array
2021  */
2022  set_elem = _bt_binsrch_array_skey(&so->orderProcs[ikey],
2023  cur_elem_trig, dir,
2024  tupdatum, tupnull, array, cur,
2025  &result);
2026 
2027  Assert(set_elem >= 0 && set_elem < array->num_elems);
2028  }
2029  else
2030  {
2031  Assert(sktrig_required && required);
2032 
2033  /*
2034  * This is a required non-array equality strategy scan key, which
2035  * we'll treat as a degenerate single element array.
2036  *
2037  * This scan key's imaginary "array" can't really advance, but it
2038  * can still roll over like any other array. (Actually, this is
2039  * no different to real single value arrays, which never advance
2040  * without rolling over -- they can never truly advance, either.)
2041  */
2042  result = _bt_compare_array_skey(&so->orderProcs[ikey],
2043  tupdatum, tupnull,
2044  cur->sk_argument, cur);
2045  }
2046 
2047  /*
2048  * Consider "beyond end of array element" array advancement.
2049  *
2050  * When the tuple attribute value is > the closest matching array key
2051  * (or < in the backwards scan case), we need to ratchet this array
2052  * forward (backward) by one increment, so that caller's tuple ends up
2053  * being < final array value instead (or > final array value instead).
2054  * This process has to work for all of the arrays, not just this one:
2055  * it must "carry" to higher-order arrays when the set_elem that we
2056  * just found happens to be the final one for the scan's direction.
2057  * Incrementing (decrementing) set_elem itself isn't good enough.
2058  *
2059  * Our approach is to provisionally use set_elem as if it was an exact
2060  * match now, then set each later/less significant array to whatever
2061  * its final element is. Once outside the loop we'll then "increment
2062  * this array's set_elem" by calling _bt_advance_array_keys_increment.
2063  * That way the process rolls over to higher order arrays as needed.
2064  *
2065  * Under this scheme any required arrays only ever ratchet forwards
2066  * (or backwards), and always do so to the maximum possible extent
2067  * that we can know will be safe without seeing the scan's next tuple.
2068  * We don't need any special handling for required scan keys that lack
2069  * a real array to advance, nor for redundant scan keys that couldn't
2070  * be eliminated by _bt_preprocess_keys. It won't matter if some of
2071  * our "true" array scan keys (or even all of them) are non-required.
2072  */
2073  if (required &&
2074  ((ScanDirectionIsForward(dir) && result > 0) ||
2075  (ScanDirectionIsBackward(dir) && result < 0)))
2076  beyond_end_advance = true;
2077 
2078  Assert(all_required_satisfied && all_satisfied);
2079  if (result != 0)
2080  {
2081  /*
2082  * Track whether caller's tuple satisfies our new post-advancement
2083  * qual, for required scan keys, as well as for the entire set of
2084  * interesting scan keys (all required scan keys plus non-required
2085  * array scan keys are considered interesting.)
2086  */
2087  all_satisfied = false;
2088  if (required)
2089  all_required_satisfied = false;
2090  else
2091  {
2092  /*
2093  * There's no need to advance the arrays using the best
2094  * available match for a non-required array. Give up now.
2095  * (Though note that sktrig_required calls still have to do
2096  * all the usual post-advancement steps, including the recheck
2097  * call to _bt_check_compare.)
2098  */
2099  break;
2100  }
2101  }
2102 
2103  /* Advance array keys, even when set_elem isn't an exact match */
2104  if (array && array->cur_elem != set_elem)
2105  {
2106  array->cur_elem = set_elem;
2107  cur->sk_argument = array->elem_values[set_elem];
2108  }
2109  }
2110 
2111  /*
2112  * Advance the array keys incrementally whenever "beyond end of array
2113  * element" array advancement happens, so that advancement will carry to
2114  * higher-order arrays (might exhaust all the scan's arrays instead, which
2115  * ends the top-level scan).
2116  */
2117  if (beyond_end_advance && !_bt_advance_array_keys_increment(scan, dir))
2118  goto end_toplevel_scan;
2119 
2120  Assert(_bt_verify_keys_with_arraykeys(scan));
2121 
2122  /*
2123  * Does tuple now satisfy our new qual? Recheck with _bt_check_compare.
2124  *
2125  * Calls triggered by an unsatisfied required scan key, whose tuple now
2126  * satisfies all required scan keys, but not all nonrequired array keys,
2127  * will still require a recheck call to _bt_check_compare. They'll still
2128  * need its "second pass" handling of required inequality scan keys.
2129  * (Might have missed a still-unsatisfied required inequality scan key
2130  * that caller didn't detect as the sktrig scan key during its initial
2131  * _bt_check_compare call that used the old/original qual.)
2132  *
2133  * Calls triggered by an unsatisfied nonrequired array scan key never need
2134  * "second pass" handling of required inequalities (nor any other handling
2135  * of any required scan key). All that matters is whether caller's tuple
2136  * satisfies the new qual, so it's safe to just skip the _bt_check_compare
2137  * recheck when we've already determined that it can only return 'false'.
2138  */
2139  if ((sktrig_required && all_required_satisfied) ||
2140  (!sktrig_required && all_satisfied))
2141  {
2142  int nsktrig = sktrig + 1;
2143  bool continuescan;
2144 
2145  Assert(all_required_satisfied);
2146 
2147  /* Recheck _bt_check_compare on behalf of caller */
2148  if (_bt_check_compare(scan, dir, tuple, tupnatts, tupdesc,
2149  false, false, false,
2150  &continuescan, &nsktrig) &&
2151  !so->scanBehind)
2152  {
2153  /* This tuple satisfies the new qual */
2154  Assert(all_satisfied && continuescan);
2155 
2156  if (pstate)
2157  pstate->continuescan = true;
2158 
2159  return true;
2160  }
2161 
2162  /*
2163  * Consider "second pass" handling of required inequalities.
2164  *
2165  * It's possible that our _bt_check_compare call indicated that the
2166  * scan should end due to some unsatisfied inequality that wasn't
2167  * initially recognized as such by us. Handle this by calling
2168  * ourselves recursively, this time indicating that the trigger is the
2169  * inequality that we missed first time around (and using a set of
2170  * required array/equality keys that are now exact matches for tuple).
2171  *
2172  * We make a strong, general guarantee that every _bt_checkkeys call
2173  * here will advance the array keys to the maximum possible extent
2174  * that we can know to be safe based on caller's tuple alone. If we
2175  * didn't perform this step, then that guarantee wouldn't quite hold.
2176  */
2177  if (unlikely(!continuescan))
2178  {
2179  bool satisfied PG_USED_FOR_ASSERTS_ONLY;
2180 
2181  Assert(sktrig_required);
2183 
2184  /*
2185  * The tuple must use "beyond end" advancement during the
2186  * recursive call, so we cannot possibly end up back here when
2187  * recursing. We'll consume a small, fixed amount of stack space.
2188  */
2189  Assert(!beyond_end_advance);
2190 
2191  /* Advance the array keys a second time using same tuple */
2192  satisfied = _bt_advance_array_keys(scan, pstate, tuple, tupnatts,
2193  tupdesc, nsktrig, true);
2194 
2195  /* This tuple doesn't satisfy the inequality */
2196  Assert(!satisfied);
2197  return false;
2198  }
2199 
2200  /*
2201  * Some non-required scan key (from new qual) still not satisfied.
2202  *
2203  * All scan keys required in the current scan direction must still be
2204  * satisfied, though, so we can trust all_required_satisfied below.
2205  */
2206  }
2207 
2208  /*
2209  * When we were called just to deal with "advancing" non-required arrays,
2210  * this is as far as we can go (cannot stop the scan for these callers)
2211  */
2212  if (!sktrig_required)
2213  {
2214  /* Caller's tuple doesn't match any qual */
2215  return false;
2216  }
2217 
2218  /*
2219  * Postcondition array state assertion (for still-unsatisfied tuples).
2220  *
2221  * By here we have established that the scan's required arrays (scan must
2222  * have at least one required array) advanced, without becoming exhausted.
2223  *
2224  * Caller's tuple is now < the newly advanced array keys (or > when this
2225  * is a backwards scan), except in the case where we only got this far due
2226  * to an unsatisfied non-required scan key. Verify that with an assert.
2227  *
2228  * Note: we don't just quit at this point when all required scan keys were
2229  * found to be satisfied because we need to consider edge-cases involving
2230  * scan keys required in the opposite direction only; those aren't tracked
2231  * by all_required_satisfied. (Actually, oppodir_inequality_sktrig trigger
2232  * scan keys are tracked by all_required_satisfied, since it's convenient
2233  * for _bt_check_compare to behave as if they are required in the current
2234  * scan direction to deal with NULLs. We'll account for that separately.)
2235  */
2236  Assert(_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc, tupnatts,
2237  false, 0, NULL) ==
2238  !all_required_satisfied);
2239 
2240  /*
2241  * We generally permit primitive index scans to continue onto the next
2242  * sibling page when the page's finaltup satisfies all required scan keys
2243  * at the point where we're between pages.
2244  *
2245  * If caller's tuple is also the page's finaltup, and we see that required
2246  * scan keys still aren't satisfied, start a new primitive index scan.
2247  */
2248  if (!all_required_satisfied && pstate->finaltup == tuple)
2249  goto new_prim_scan;
2250 
2251  /*
2252  * Proactively check finaltup (don't wait until finaltup is reached by the
2253  * scan) when it might well turn out to not be satisfied later on.
2254  *
2255  * Note: if so->scanBehind hasn't already been set for finaltup by us,
2256  * it'll be set during this call to _bt_tuple_before_array_skeys. Either
2257  * way, it'll be set correctly (for the whole page) after this point.
2258  */
2259  if (!all_required_satisfied && pstate->finaltup &&
2260  _bt_tuple_before_array_skeys(scan, dir, pstate->finaltup, tupdesc,
2261  BTreeTupleGetNAtts(pstate->finaltup, rel),
2262  false, 0, &so->scanBehind))
2263  goto new_prim_scan;
2264 
2265  /*
2266  * When we encounter a truncated finaltup high key attribute, we're
2267  * optimistic about the chances of its corresponding required scan key
2268  * being satisfied when we go on to check it against tuples from this
2269  * page's right sibling leaf page. We consider truncated attributes to be
2270  * satisfied by required scan keys, which allows the primitive index scan
2271  * to continue to the next leaf page. We must set so->scanBehind to true
2272  * to remember that the last page's finaltup had "satisfied" required scan
2273  * keys for one or more truncated attribute values (scan keys required in
2274  * _either_ scan direction).
2275  *
2276  * There is a chance that _bt_checkkeys (which checks so->scanBehind) will
2277  * find that even the sibling leaf page's finaltup is < the new array
2278  * keys. When that happens, our optimistic policy will have incurred a
2279  * single extra leaf page access that could have been avoided.
2280  *
2281  * A pessimistic policy would give backward scans a gratuitous advantage
2282  * over forward scans. We'd punish forward scans for applying more
2283  * accurate information from the high key, rather than just using the
2284  * final non-pivot tuple as finaltup, in the style of backward scans.
2285  * Being pessimistic would also give some scans with non-required arrays a
2286  * perverse advantage over similar scans that use required arrays instead.
2287  *
2288  * You can think of this as a speculative bet on what the scan is likely
2289  * to find on the next page. It's not much of a gamble, though, since the
2290  * untruncated prefix of attributes must strictly satisfy the new qual
2291  * (though it's okay if any non-required scan keys fail to be satisfied).
2292  */
2293  if (so->scanBehind && has_required_opposite_direction_only)
2294  {
2295  /*
2296  * However, we avoid this behavior whenever the scan involves a scan
2297  * key required in the opposite direction to the scan only, along with
2298  * a finaltup with at least one truncated attribute that's associated
2299  * with a scan key marked required (required in either direction).
2300  *
2301  * _bt_check_compare simply won't stop the scan for a scan key that's
2302  * marked required in the opposite scan direction only. That leaves
2303  * us without any reliable way of reconsidering any opposite-direction
2304  * inequalities if it turns out that starting a new primitive index
2305  * scan will allow _bt_first to skip ahead by a great many leaf pages
2306  * (see next section for details of how that works).
2307  */
2308  goto new_prim_scan;
2309  }
2310 
2311  /*
2312  * Handle inequalities marked required in the opposite scan direction.
2313  * They can also signal that we should start a new primitive index scan.
2314  *
2315  * It's possible that the scan is now positioned where "matching" tuples
2316  * begin, and that caller's tuple satisfies all scan keys required in the
2317  * current scan direction. But if caller's tuple still doesn't satisfy
2318  * other scan keys that are required in the opposite scan direction only
2319  * (e.g., a required >= strategy scan key when scan direction is forward),
2320  * it's still possible that there are many leaf pages before the page that
2321  * _bt_first could skip straight to. Groveling through all those pages
2322  * will always give correct answers, but it can be very inefficient. We
2323  * must avoid needlessly scanning extra pages.
2324  *
2325  * Separately, it's possible that _bt_check_compare set continuescan=false
2326  * for a scan key that's required in the opposite direction only. This is
2327  * a special case, that happens only when _bt_check_compare sees that the
2328  * inequality encountered a NULL value. This signals the end of non-NULL
2329  * values in the current scan direction, which is reason enough to end the
2330  * (primitive) scan. If this happens at the start of a large group of
2331  * NULL values, then we shouldn't expect to be called again until after
2332  * the scan has already read indefinitely-many leaf pages full of tuples
2333  * with NULL suffix values. We need a separate test for this case so that
2334  * we don't miss our only opportunity to skip over such a group of pages.
2335  * (_bt_first is expected to skip over the group of NULLs by applying a
2336  * similar "deduce NOT NULL" rule, where it finishes its insertion scan
2337  * key by consing up an explicit SK_SEARCHNOTNULL key.)
2338  *
2339  * Apply a test against finaltup to detect and recover from these problem:
2340  * if even finaltup doesn't satisfy such an inequality, we just skip by
2341  * starting a new primitive index scan. When we skip, we know for sure
2342  * that all of the tuples on the current page following caller's tuple are
2343  * also before the _bt_first-wise start of tuples for our new qual. That
2344  * at least suggests many more skippable pages beyond the current page.
2345  */
2346  if (has_required_opposite_direction_only && pstate->finaltup &&
2347  (all_required_satisfied || oppodir_inequality_sktrig))
2348  {
2349  int nfinaltupatts = BTreeTupleGetNAtts(pstate->finaltup, rel);
2350  ScanDirection flipped;
2351  bool continuescanflip;
2352  int opsktrig;
2353 
2354  /*
2355  * We're checking finaltup (which is usually not caller's tuple), so
2356  * cannot reuse work from caller's earlier _bt_check_compare call.
2357  *
2358  * Flip the scan direction when calling _bt_check_compare this time,
2359  * so that it will set continuescanflip=false when it encounters an
2360  * inequality required in the opposite scan direction.
2361  */
2362  Assert(!so->scanBehind);
2363  opsktrig = 0;
2364  flipped = -dir;
2365  _bt_check_compare(scan, flipped,
2366  pstate->finaltup, nfinaltupatts, tupdesc,
2367  false, false, false,
2368  &continuescanflip, &opsktrig);
2369 
2370  /*
2371  * Only start a new primitive index scan when finaltup has a required
2372  * unsatisfied inequality (unsatisfied in the opposite direction)
2373  */
2374  Assert(all_required_satisfied != oppodir_inequality_sktrig);
2375  if (unlikely(!continuescanflip &&
2376  so->keyData[opsktrig].sk_strategy != BTEqualStrategyNumber))
2377  {
2378  /*
2379  * It's possible for the same inequality to be unsatisfied by both
2380  * caller's tuple (in scan's direction) and finaltup (in the
2381  * opposite direction) due to _bt_check_compare's behavior with
2382  * NULLs
2383  */
2384  Assert(opsktrig >= sktrig); /* not opsktrig > sktrig due to NULLs */
2385 
2386  /*
2387  * Make sure that any non-required arrays are set to the first
2388  * array element for the current scan direction
2389  */
2390  _bt_rewind_nonrequired_arrays(scan, dir);
2391 
2392  goto new_prim_scan;
2393  }
2394  }
2395 
2396  /*
2397  * Stick with the ongoing primitive index scan for now.
2398  *
2399  * It's possible that later tuples will also turn out to have values that
2400  * are still < the now-current array keys (or > the current array keys).
2401  * Our caller will handle this by performing what amounts to a linear
2402  * search of the page, implemented by calling _bt_check_compare and then
2403  * _bt_tuple_before_array_skeys for each tuple.
2404  *
2405  * This approach has various advantages over a binary search of the page.
2406  * Repeated binary searches of the page (one binary search for every array
2407  * advancement) won't outperform a continuous linear search. While there
2408  * are workloads that a naive linear search won't handle well, our caller
2409  * has a "look ahead" fallback mechanism to deal with that problem.
2410  */
2411  pstate->continuescan = true; /* Override _bt_check_compare */
2412  so->needPrimScan = false; /* _bt_readpage has more tuples to check */
2413 
2414  if (so->scanBehind)
2415  {
2416  /* Optimization: skip by setting "look ahead" mechanism's offnum */
2418  pstate->skip = pstate->maxoff + 1;
2419  }
2420 
2421  /* Caller's tuple doesn't match the new qual */
2422  return false;
2423 
2424 new_prim_scan:
2425 
2426  /*
2427  * End this primitive index scan, but schedule another.
2428  *
2429  * Note: If the scan direction happens to change, this scheduled primitive
2430  * index scan won't go ahead after all.
2431  */
2432  pstate->continuescan = false; /* Tell _bt_readpage we're done... */
2433  so->needPrimScan = true; /* ...but call _bt_first again */
2434 
2435  if (scan->parallel_scan)
2437 
2438  /* Caller's tuple doesn't match the new qual */
2439  return false;
2440 
2441 end_toplevel_scan:
2442 
2443  /*
2444  * End the current primitive index scan, but don't schedule another.
2445  *
2446  * This ends the entire top-level scan in the current scan direction.
2447  *
2448  * Note: The scan's arrays (including any non-required arrays) are now in
2449  * their final positions for the current scan direction. If the scan
2450  * direction happens to change, then the arrays will already be in their
2451  * first positions for what will then be the current scan direction.
2452  */
2453  pstate->continuescan = false; /* Tell _bt_readpage we're done... */
2454  so->needPrimScan = false; /* ...don't call _bt_first again, though */
2455 
2456  /* Caller's tuple doesn't match any qual */
2457  return false;
2458 }
signed int int32
Definition: c.h:494
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:182
#define Assert(condition)
Definition: c.h:858
#define unlikely(x)
Definition: c.h:311
struct cursor * cur
Definition: ecpg.c:28
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: itup.h:117
void _bt_parallel_primscan_schedule(IndexScanDesc scan, BlockNumber prev_scan_page)
Definition: nbtree.c:771
#define SK_BT_REQBKWD
Definition: nbtree.h:1125
#define SK_BT_REQFWD
Definition: nbtree.h:1124
#define BTreeTupleGetNAtts(itup, rel)
Definition: nbtree.h:577
BTScanOpaqueData * BTScanOpaque
Definition: nbtree.h:1081
static void _bt_rewind_nonrequired_arrays(IndexScanDesc scan, ScanDirection dir)
Definition: nbtutils.c:1467
static bool _bt_check_compare(IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, bool advancenonrequired, bool prechecked, bool firstmatch, bool *continuescan, int *ikey)
Definition: nbtutils.c:3676
static int _bt_binsrch_array_skey(FmgrInfo *orderproc, bool cur_elem_trig, ScanDirection dir, Datum tupdatum, bool tupnull, BTArrayKeyInfo *array, ScanKey cur, int32 *set_elem_result)
Definition: nbtutils.c:1201
static bool _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, int sktrig, bool sktrig_required)
Definition: nbtutils.c:1789
static bool _bt_tuple_before_array_skeys(IndexScanDesc scan, ScanDirection dir, IndexTuple tuple, TupleDesc tupdesc, int tupnatts, bool readpagetup, int sktrig, bool *scanBehind)
Definition: nbtutils.c:1544
static bool _bt_advance_array_keys_increment(IndexScanDesc scan, ScanDirection dir)
Definition: nbtutils.c:1381
static int32 _bt_compare_array_skey(FmgrInfo *orderproc, Datum tupdatum, bool tupnull, Datum arrdatum, ScanKey cur)
Definition: nbtutils.c:1131
uintptr_t Datum
Definition: postgres.h:64
#define ScanDirectionIsForward(direction)
Definition: sdir.h:64
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
ScanDirection
Definition: sdir.h:25
@ ForwardScanDirection
Definition: sdir.h:28
#define SK_SEARCHARRAY
Definition: skey.h:120
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Datum * elem_values
Definition: nbtree.h:1037
bool firstmatch
Definition: nbtree.h:1108
BlockNumber prev_scan_page
Definition: nbtree.h:1093
bool continuescan
Definition: nbtree.h:1101
IndexTuple finaltup
Definition: nbtree.h:1092
bool prechecked
Definition: nbtree.h:1107
ScanDirection dir
Definition: nbtree.h:1089
int16 targetdistance
Definition: nbtree.h:1115
int16 rechecks
Definition: nbtree.h:1114
OffsetNumber skip
Definition: nbtree.h:1100
OffsetNumber maxoff
Definition: nbtree.h:1091
bool needPrimScan
Definition: nbtree.h:1049
BTArrayKeyInfo * arrayKeys
Definition: nbtree.h:1051
FmgrInfo * orderProcs
Definition: nbtree.h:1052
ScanKey keyData
Definition: nbtree.h:1045
struct ParallelIndexScanDescData * parallel_scan
Definition: relscan.h:166
Relation indexRelation
Definition: relscan.h:118
StrategyNumber sk_strategy
Definition: skey.h:68

References _bt_advance_array_keys_increment(), _bt_binsrch_array_skey(), _bt_check_compare(), _bt_compare_array_skey(), _bt_parallel_primscan_schedule(), _bt_rewind_nonrequired_arrays(), _bt_tuple_before_array_skeys(), BTScanOpaqueData::arrayKeys, Assert, BTEqualStrategyNumber, BTreeTupleGetNAtts, BTReadPageState::continuescan, cur, BTArrayKeyInfo::cur_elem, BTReadPageState::dir, BTArrayKeyInfo::elem_values, BTReadPageState::finaltup, BTReadPageState::firstmatch, ForwardScanDirection, if(), index_getattr(), IndexScanDescData::indexRelation, BTScanOpaqueData::keyData, BTReadPageState::maxoff, BTScanOpaqueData::needPrimScan, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, IndexScanDescData::parallel_scan, PG_USED_FOR_ASSERTS_ONLY, BTReadPageState::prechecked, BTReadPageState::prev_scan_page, BTReadPageState::rechecks, generate_unaccent_rules::required, BTArrayKeyInfo::scan_key, BTScanOpaqueData::scanBehind, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_REQBKWD, SK_BT_REQFWD, SK_SEARCHARRAY, ScanKeyData::sk_strategy, BTReadPageState::skip, BTReadPageState::targetdistance, and unlikely.

Referenced by _bt_check_compare(), and _bt_checkkeys().

◆ _bt_advance_array_keys_increment()

static bool _bt_advance_array_keys_increment ( IndexScanDesc  scan,
ScanDirection  dir 
)
static

Definition at line 1381 of file nbtutils.c.

1382 {
1383  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1384 
1385  /*
1386  * We must advance the last array key most quickly, since it will
1387  * correspond to the lowest-order index column among the available
1388  * qualifications
1389  */
1390  for (int i = so->numArrayKeys - 1; i >= 0; i--)
1391  {
1392  BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
1393  ScanKey skey = &so->keyData[curArrayKey->scan_key];
1394  int cur_elem = curArrayKey->cur_elem;
1395  int num_elems = curArrayKey->num_elems;
1396  bool rolled = false;
1397 
1398  if (ScanDirectionIsForward(dir) && ++cur_elem >= num_elems)
1399  {
1400  cur_elem = 0;
1401  rolled = true;
1402  }
1403  else if (ScanDirectionIsBackward(dir) && --cur_elem < 0)
1404  {
1405  cur_elem = num_elems - 1;
1406  rolled = true;
1407  }
1408 
1409  curArrayKey->cur_elem = cur_elem;
1410  skey->sk_argument = curArrayKey->elem_values[cur_elem];
1411  if (!rolled)
1412  return true;
1413 
1414  /* Need to advance next array key, if any */
1415  }
1416 
1417  /*
1418  * The array keys are now exhausted. (There isn't actually a distinct
1419  * state that represents array exhaustion, since index scans don't always
1420  * end after btgettuple returns "false".)
1421  *
1422  * Restore the array keys to the state they were in immediately before we
1423  * were called. This ensures that the arrays only ever ratchet in the
1424  * current scan direction. Without this, scans would overlook matching
1425  * tuples if and when the scan's direction was subsequently reversed.
1426  */
1427  _bt_start_array_keys(scan, -dir);
1428 
1429  return false;
1430 }
for(;;)
int i
Definition: isn.c:73
void _bt_start_array_keys(IndexScanDesc scan, ScanDirection dir)
Definition: nbtutils.c:1343
Datum sk_argument
Definition: skey.h:72

References _bt_start_array_keys(), BTScanOpaqueData::arrayKeys, BTArrayKeyInfo::cur_elem, BTArrayKeyInfo::elem_values, for(), i, BTScanOpaqueData::keyData, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, BTArrayKeyInfo::scan_key, ScanDirectionIsBackward, ScanDirectionIsForward, and ScanKeyData::sk_argument.

Referenced by _bt_advance_array_keys().

◆ _bt_allequalimage()

bool _bt_allequalimage ( Relation  rel,
bool  debugmessage 
)

Definition at line 5129 of file nbtutils.c.

5130 {
5131  bool allequalimage = true;
5132 
5133  /* INCLUDE indexes can never support deduplication */
5136  return false;
5137 
5138  for (int i = 0; i < IndexRelationGetNumberOfKeyAttributes(rel); i++)
5139  {
5140  Oid opfamily = rel->rd_opfamily[i];
5141  Oid opcintype = rel->rd_opcintype[i];
5142  Oid collation = rel->rd_indcollation[i];
5143  Oid equalimageproc;
5144 
5145  equalimageproc = get_opfamily_proc(opfamily, opcintype, opcintype,
5147 
5148  /*
5149  * If there is no BTEQUALIMAGE_PROC then deduplication is assumed to
5150  * be unsafe. Otherwise, actually call proc and see what it says.
5151  */
5152  if (!OidIsValid(equalimageproc) ||
5153  !DatumGetBool(OidFunctionCall1Coll(equalimageproc, collation,
5154  ObjectIdGetDatum(opcintype))))
5155  {
5156  allequalimage = false;
5157  break;
5158  }
5159  }
5160 
5161  if (debugmessage)
5162  {
5163  if (allequalimage)
5164  elog(DEBUG1, "index \"%s\" can safely use deduplication",
5166  else
5167  elog(DEBUG1, "index \"%s\" cannot use deduplication",
5169  }
5170 
5171  return allequalimage;
5172 }
#define OidIsValid(objectId)
Definition: c.h:775
#define DEBUG1
Definition: elog.h:30
#define elog(elevel,...)
Definition: elog.h:224
Datum OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
Definition: fmgr.c:1411
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:796
#define BTEQUALIMAGE_PROC
Definition: nbtree.h:710
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:517
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
Oid * rd_opcintype
Definition: rel.h:208
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217

References BTEQUALIMAGE_PROC, DatumGetBool(), DEBUG1, elog, get_opfamily_proc(), i, IndexRelationGetNumberOfAttributes, IndexRelationGetNumberOfKeyAttributes, ObjectIdGetDatum(), OidFunctionCall1Coll(), OidIsValid, RelationData::rd_indcollation, RelationData::rd_opcintype, RelationData::rd_opfamily, and RelationGetRelationName.

Referenced by _bt_leafbuild(), bt_index_check_internal(), and btbuildempty().

◆ _bt_binsrch_array_skey()

static int _bt_binsrch_array_skey ( FmgrInfo orderproc,
bool  cur_elem_trig,
ScanDirection  dir,
Datum  tupdatum,
bool  tupnull,
BTArrayKeyInfo array,
ScanKey  cur,
int32 set_elem_result 
)
static

Definition at line 1201 of file nbtutils.c.

1206 {
1207  int low_elem = 0,
1208  mid_elem = -1,
1209  high_elem = array->num_elems - 1,
1210  result = 0;
1211  Datum arrdatum;
1212 
1213  Assert(cur->sk_flags & SK_SEARCHARRAY);
1214  Assert(cur->sk_strategy == BTEqualStrategyNumber);
1215 
1216  if (cur_elem_trig)
1217  {
1219  Assert(cur->sk_flags & SK_BT_REQFWD);
1220 
1221  /*
1222  * When the scan key that triggered array advancement is a required
1223  * array scan key, it is now certain that the current array element
1224  * (plus all prior elements relative to the current scan direction)
1225  * cannot possibly be at or ahead of the corresponding tuple value.
1226  * (_bt_checkkeys must have called _bt_tuple_before_array_skeys, which
1227  * makes sure this is true as a condition of advancing the arrays.)
1228  *
1229  * This makes it safe to exclude array elements up to and including
1230  * the former-current array element from our search.
1231  *
1232  * Separately, when array advancement was triggered by a required scan
1233  * key, the array element immediately after the former-current element
1234  * is often either an exact tupdatum match, or a "close by" near-match
1235  * (a near-match tupdatum is one whose key space falls _between_ the
1236  * former-current and new-current array elements). We'll detect both
1237  * cases via an optimistic comparison of the new search lower bound
1238  * (or new search upper bound in the case of backwards scans).
1239  */
1240  if (ScanDirectionIsForward(dir))
1241  {
1242  low_elem = array->cur_elem + 1; /* old cur_elem exhausted */
1243 
1244  /* Compare prospective new cur_elem (also the new lower bound) */
1245  if (high_elem >= low_elem)
1246  {
1247  arrdatum = array->elem_values[low_elem];
1248  result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
1249  arrdatum, cur);
1250 
1251  if (result <= 0)
1252  {
1253  /* Optimistic comparison optimization worked out */
1254  *set_elem_result = result;
1255  return low_elem;
1256  }
1257  mid_elem = low_elem;
1258  low_elem++; /* this cur_elem exhausted, too */
1259  }
1260 
1261  if (high_elem < low_elem)
1262  {
1263  /* Caller needs to perform "beyond end" array advancement */
1264  *set_elem_result = 1;
1265  return high_elem;
1266  }
1267  }
1268  else
1269  {
1270  high_elem = array->cur_elem - 1; /* old cur_elem exhausted */
1271 
1272  /* Compare prospective new cur_elem (also the new upper bound) */
1273  if (high_elem >= low_elem)
1274  {
1275  arrdatum = array->elem_values[high_elem];
1276  result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
1277  arrdatum, cur);
1278 
1279  if (result >= 0)
1280  {
1281  /* Optimistic comparison optimization worked out */
1282  *set_elem_result = result;
1283  return high_elem;
1284  }
1285  mid_elem = high_elem;
1286  high_elem--; /* this cur_elem exhausted, too */
1287  }
1288 
1289  if (high_elem < low_elem)
1290  {
1291  /* Caller needs to perform "beyond end" array advancement */
1292  *set_elem_result = -1;
1293  return low_elem;
1294  }
1295  }
1296  }
1297 
1298  while (high_elem > low_elem)
1299  {
1300  mid_elem = low_elem + ((high_elem - low_elem) / 2);
1301  arrdatum = array->elem_values[mid_elem];
1302 
1303  result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
1304  arrdatum, cur);
1305 
1306  if (result == 0)
1307  {
1308  /*
1309  * It's safe to quit as soon as we see an equal array element.
1310  * This often saves an extra comparison or two...
1311  */
1312  low_elem = mid_elem;
1313  break;
1314  }
1315 
1316  if (result > 0)
1317  low_elem = mid_elem + 1;
1318  else
1319  high_elem = mid_elem;
1320  }
1321 
1322  /*
1323  * ...but our caller also cares about how its searched-for tuple datum
1324  * compares to the low_elem datum. Must always set *set_elem_result with
1325  * the result of that comparison specifically.
1326  */
1327  if (low_elem != mid_elem)
1328  result = _bt_compare_array_skey(orderproc, tupdatum, tupnull,
1329  array->elem_values[low_elem], cur);
1330 
1331  *set_elem_result = result;
1332 
1333  return low_elem;
1334 }
#define ScanDirectionIsNoMovement(direction)
Definition: sdir.h:57

References _bt_compare_array_skey(), Assert, BTEqualStrategyNumber, cur, BTArrayKeyInfo::cur_elem, BTArrayKeyInfo::elem_values, BTArrayKeyInfo::num_elems, ScanDirectionIsForward, ScanDirectionIsNoMovement, SK_BT_REQFWD, and SK_SEARCHARRAY.

Referenced by _bt_advance_array_keys(), and _bt_compare_array_scankey_args().

◆ _bt_check_compare()

static bool _bt_check_compare ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
bool  advancenonrequired,
bool  prechecked,
bool  firstmatch,
bool continuescan,
int *  ikey 
)
static

Definition at line 3676 of file nbtutils.c.

3680 {
3681  BTScanOpaque so = (BTScanOpaque) scan->opaque;
3682 
3683  *continuescan = true; /* default assumption */
3684 
3685  for (; *ikey < so->numberOfKeys; (*ikey)++)
3686  {
3687  ScanKey key = so->keyData + *ikey;
3688  Datum datum;
3689  bool isNull;
3690  bool requiredSameDir = false,
3691  requiredOppositeDirOnly = false;
3692 
3693  /*
3694  * Check if the key is required in the current scan direction, in the
3695  * opposite scan direction _only_, or in neither direction
3696  */
3697  if (((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsForward(dir)) ||
3698  ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsBackward(dir)))
3699  requiredSameDir = true;
3700  else if (((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsBackward(dir)) ||
3701  ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsForward(dir)))
3702  requiredOppositeDirOnly = true;
3703 
3704  /*
3705  * If the caller told us the *continuescan flag is known to be true
3706  * for the last item on the page, then we know the keys required for
3707  * the current direction scan should be matched. Otherwise, the
3708  * *continuescan flag would be set for the current item and
3709  * subsequently the last item on the page accordingly.
3710  *
3711  * If the key is required for the opposite direction scan, we can skip
3712  * the check if the caller tells us there was already at least one
3713  * matching item on the page. Also, we require the *continuescan flag
3714  * to be true for the last item on the page to know there are no
3715  * NULLs.
3716  *
3717  * Both cases above work except for the row keys, where NULLs could be
3718  * found in the middle of matching values.
3719  */
3720  if (prechecked &&
3721  (requiredSameDir || (requiredOppositeDirOnly && firstmatch)) &&
3722  !(key->sk_flags & SK_ROW_HEADER))
3723  continue;
3724 
3725  if (key->sk_attno > tupnatts)
3726  {
3727  /*
3728  * This attribute is truncated (must be high key). The value for
3729  * this attribute in the first non-pivot tuple on the page to the
3730  * right could be any possible value. Assume that truncated
3731  * attribute passes the qual.
3732  */
3733  Assert(BTreeTupleIsPivot(tuple));
3734  continue;
3735  }
3736 
3737  /* row-comparison keys need special processing */
3738  if (key->sk_flags & SK_ROW_HEADER)
3739  {
3740  if (_bt_check_rowcompare(key, tuple, tupnatts, tupdesc, dir,
3741  continuescan))
3742  continue;
3743  return false;
3744  }
3745 
3746  datum = index_getattr(tuple,
3747  key->sk_attno,
3748  tupdesc,
3749  &isNull);
3750 
3751  if (key->sk_flags & SK_ISNULL)
3752  {
3753  /* Handle IS NULL/NOT NULL tests */
3754  if (key->sk_flags & SK_SEARCHNULL)
3755  {
3756  if (isNull)
3757  continue; /* tuple satisfies this qual */
3758  }
3759  else
3760  {
3761  Assert(key->sk_flags & SK_SEARCHNOTNULL);
3762  if (!isNull)
3763  continue; /* tuple satisfies this qual */
3764  }
3765 
3766  /*
3767  * Tuple fails this qual. If it's a required qual for the current
3768  * scan direction, then we can conclude no further tuples will
3769  * pass, either.
3770  */
3771  if (requiredSameDir)
3772  *continuescan = false;
3773 
3774  /*
3775  * In any case, this indextuple doesn't match the qual.
3776  */
3777  return false;
3778  }
3779 
3780  if (isNull)
3781  {
3782  if (key->sk_flags & SK_BT_NULLS_FIRST)
3783  {
3784  /*
3785  * Since NULLs are sorted before non-NULLs, we know we have
3786  * reached the lower limit of the range of values for this
3787  * index attr. On a backward scan, we can stop if this qual
3788  * is one of the "must match" subset. We can stop regardless
3789  * of whether the qual is > or <, so long as it's required,
3790  * because it's not possible for any future tuples to pass. On
3791  * a forward scan, however, we must keep going, because we may
3792  * have initially positioned to the start of the index.
3793  * (_bt_advance_array_keys also relies on this behavior during
3794  * forward scans.)
3795  */
3796  if ((key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
3798  *continuescan = false;
3799  }
3800  else
3801  {
3802  /*
3803  * Since NULLs are sorted after non-NULLs, we know we have
3804  * reached the upper limit of the range of values for this
3805  * index attr. On a forward scan, we can stop if this qual is
3806  * one of the "must match" subset. We can stop regardless of
3807  * whether the qual is > or <, so long as it's required,
3808  * because it's not possible for any future tuples to pass. On
3809  * a backward scan, however, we must keep going, because we
3810  * may have initially positioned to the end of the index.
3811  * (_bt_advance_array_keys also relies on this behavior during
3812  * backward scans.)
3813  */
3814  if ((key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
3816  *continuescan = false;
3817  }
3818 
3819  /*
3820  * In any case, this indextuple doesn't match the qual.
3821  */
3822  return false;
3823  }
3824 
3825  /*
3826  * Apply the key-checking function, though only if we must.
3827  *
3828  * When a key is required in the opposite-of-scan direction _only_,
3829  * then it must already be satisfied if firstmatch=true indicates that
3830  * an earlier tuple from this same page satisfied it earlier on.
3831  */
3832  if (!(requiredOppositeDirOnly && firstmatch) &&
3833  !DatumGetBool(FunctionCall2Coll(&key->sk_func, key->sk_collation,
3834  datum, key->sk_argument)))
3835  {
3836  /*
3837  * Tuple fails this qual. If it's a required qual for the current
3838  * scan direction, then we can conclude no further tuples will
3839  * pass, either.
3840  *
3841  * Note: because we stop the scan as soon as any required equality
3842  * qual fails, it is critical that equality quals be used for the
3843  * initial positioning in _bt_first() when they are available. See
3844  * comments in _bt_first().
3845  */
3846  if (requiredSameDir)
3847  *continuescan = false;
3848 
3849  /*
3850  * If this is a non-required equality-type array key, the tuple
3851  * needs to be checked against every possible array key. Handle
3852  * this by "advancing" the scan key's array to a matching value
3853  * (if we're successful then the tuple might match the qual).
3854  */
3855  else if (advancenonrequired &&
3856  key->sk_strategy == BTEqualStrategyNumber &&
3857  (key->sk_flags & SK_SEARCHARRAY))
3858  return _bt_advance_array_keys(scan, NULL, tuple, tupnatts,
3859  tupdesc, *ikey, false);
3860 
3861  /*
3862  * This indextuple doesn't match the qual.
3863  */
3864  return false;
3865  }
3866  }
3867 
3868  /* If we get here, the tuple passes all index quals. */
3869  return true;
3870 }
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
static bool BTreeTupleIsPivot(IndexTuple itup)
Definition: nbtree.h:480
#define SK_BT_NULLS_FIRST
Definition: nbtree.h:1128
static bool _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, int tupnatts, TupleDesc tupdesc, ScanDirection dir, bool *continuescan)
Definition: nbtutils.c:3882
#define SK_ROW_HEADER
Definition: skey.h:117
#define SK_SEARCHNOTNULL
Definition: skey.h:122
#define SK_SEARCHNULL
Definition: skey.h:121
#define SK_ISNULL
Definition: skey.h:115

References _bt_advance_array_keys(), _bt_check_rowcompare(), Assert, BTEqualStrategyNumber, BTreeTupleIsPivot(), DatumGetBool(), for(), FunctionCall2Coll(), index_getattr(), sort-test::key, BTScanOpaqueData::keyData, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_NULLS_FIRST, SK_BT_REQBKWD, SK_BT_REQFWD, SK_ISNULL, SK_ROW_HEADER, SK_SEARCHARRAY, SK_SEARCHNOTNULL, and SK_SEARCHNULL.

Referenced by _bt_advance_array_keys(), and _bt_checkkeys().

◆ _bt_check_natts()

bool _bt_check_natts ( Relation  rel,
bool  heapkeyspace,
Page  page,
OffsetNumber  offnum 
)

Definition at line 4911 of file nbtutils.c.

4912 {
4915  BTPageOpaque opaque = BTPageGetOpaque(page);
4916  IndexTuple itup;
4917  int tupnatts;
4918 
4919  /*
4920  * We cannot reliably test a deleted or half-dead page, since they have
4921  * dummy high keys
4922  */
4923  if (P_IGNORE(opaque))
4924  return true;
4925 
4926  Assert(offnum >= FirstOffsetNumber &&
4927  offnum <= PageGetMaxOffsetNumber(page));
4928 
4929  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
4930  tupnatts = BTreeTupleGetNAtts(itup, rel);
4931 
4932  /* !heapkeyspace indexes do not support deduplication */
4933  if (!heapkeyspace && BTreeTupleIsPosting(itup))
4934  return false;
4935 
4936  /* Posting list tuples should never have "pivot heap TID" bit set */
4937  if (BTreeTupleIsPosting(itup) &&
4939  BT_PIVOT_HEAP_TID_ATTR) != 0)
4940  return false;
4941 
4942  /* INCLUDE indexes do not support deduplication */
4943  if (natts != nkeyatts && BTreeTupleIsPosting(itup))
4944  return false;
4945 
4946  if (P_ISLEAF(opaque))
4947  {
4948  if (offnum >= P_FIRSTDATAKEY(opaque))
4949  {
4950  /*
4951  * Non-pivot tuple should never be explicitly marked as a pivot
4952  * tuple
4953  */
4954  if (BTreeTupleIsPivot(itup))
4955  return false;
4956 
4957  /*
4958  * Leaf tuples that are not the page high key (non-pivot tuples)
4959  * should never be truncated. (Note that tupnatts must have been
4960  * inferred, even with a posting list tuple, because only pivot
4961  * tuples store tupnatts directly.)
4962  */
4963  return tupnatts == natts;
4964  }
4965  else
4966  {
4967  /*
4968  * Rightmost page doesn't contain a page high key, so tuple was
4969  * checked above as ordinary leaf tuple
4970  */
4971  Assert(!P_RIGHTMOST(opaque));
4972 
4973  /*
4974  * !heapkeyspace high key tuple contains only key attributes. Note
4975  * that tupnatts will only have been explicitly represented in
4976  * !heapkeyspace indexes that happen to have non-key attributes.
4977  */
4978  if (!heapkeyspace)
4979  return tupnatts == nkeyatts;
4980 
4981  /* Use generic heapkeyspace pivot tuple handling */
4982  }
4983  }
4984  else /* !P_ISLEAF(opaque) */
4985  {
4986  if (offnum == P_FIRSTDATAKEY(opaque))
4987  {
4988  /*
4989  * The first tuple on any internal page (possibly the first after
4990  * its high key) is its negative infinity tuple. Negative
4991  * infinity tuples are always truncated to zero attributes. They
4992  * are a particular kind of pivot tuple.
4993  */
4994  if (heapkeyspace)
4995  return tupnatts == 0;
4996 
4997  /*
4998  * The number of attributes won't be explicitly represented if the
4999  * negative infinity tuple was generated during a page split that
5000  * occurred with a version of Postgres before v11. There must be
5001  * a problem when there is an explicit representation that is
5002  * non-zero, or when there is no explicit representation and the
5003  * tuple is evidently not a pre-pg_upgrade tuple.
5004  *
5005  * Prior to v11, downlinks always had P_HIKEY as their offset.
5006  * Accept that as an alternative indication of a valid
5007  * !heapkeyspace negative infinity tuple.
5008  */
5009  return tupnatts == 0 ||
5011  }
5012  else
5013  {
5014  /*
5015  * !heapkeyspace downlink tuple with separator key contains only
5016  * key attributes. Note that tupnatts will only have been
5017  * explicitly represented in !heapkeyspace indexes that happen to
5018  * have non-key attributes.
5019  */
5020  if (!heapkeyspace)
5021  return tupnatts == nkeyatts;
5022 
5023  /* Use generic heapkeyspace pivot tuple handling */
5024  }
5025  }
5026 
5027  /* Handle heapkeyspace pivot tuples (excluding minus infinity items) */
5028  Assert(heapkeyspace);
5029 
5030  /*
5031  * Explicit representation of the number of attributes is mandatory with
5032  * heapkeyspace index pivot tuples, regardless of whether or not there are
5033  * non-key attributes.
5034  */
5035  if (!BTreeTupleIsPivot(itup))
5036  return false;
5037 
5038  /* Pivot tuple should not use posting list representation (redundant) */
5039  if (BTreeTupleIsPosting(itup))
5040  return false;
5041 
5042  /*
5043  * Heap TID is a tiebreaker key attribute, so it cannot be untruncated
5044  * when any other key attribute is truncated
5045  */
5046  if (BTreeTupleGetHeapTID(itup) != NULL && tupnatts != nkeyatts)
5047  return false;
5048 
5049  /*
5050  * Pivot tuple must have at least one untruncated key attribute (minus
5051  * infinity pivot tuples are the only exception). Pivot tuples can never
5052  * represent that there is a value present for a key attribute that
5053  * exceeds pg_index.indnkeyatts for the index.
5054  */
5055  return tupnatts > 0 && tupnatts <= nkeyatts;
5056 }
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:372
signed short int16
Definition: c.h:493
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static OffsetNumber ItemPointerGetOffsetNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:114
IndexTupleData * IndexTuple
Definition: itup.h:53
#define BT_PIVOT_HEAP_TID_ATTR
Definition: nbtree.h:465
#define P_ISLEAF(opaque)
Definition: nbtree.h:220
#define P_HIKEY
Definition: nbtree.h:367
#define BTPageGetOpaque(page)
Definition: nbtree.h:73
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:369
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:219
#define P_IGNORE(opaque)
Definition: nbtree.h:225
static bool BTreeTupleIsPosting(IndexTuple itup)
Definition: nbtree.h:492
static ItemPointer BTreeTupleGetHeapTID(IndexTuple itup)
Definition: nbtree.h:638
#define FirstOffsetNumber
Definition: off.h:27
ItemPointerData t_tid
Definition: itup.h:37

References Assert, BT_PIVOT_HEAP_TID_ATTR, BTPageGetOpaque, BTreeTupleGetHeapTID(), BTreeTupleGetNAtts, BTreeTupleIsPivot(), BTreeTupleIsPosting(), FirstOffsetNumber, IndexRelationGetNumberOfAttributes, IndexRelationGetNumberOfKeyAttributes, ItemPointerGetOffsetNumber(), ItemPointerGetOffsetNumberNoCheck(), P_FIRSTDATAKEY, P_HIKEY, P_IGNORE, P_ISLEAF, P_RIGHTMOST, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), and IndexTupleData::t_tid.

Referenced by _bt_compare(), and bt_target_page_check().

◆ _bt_check_rowcompare()

static bool _bt_check_rowcompare ( ScanKey  skey,
IndexTuple  tuple,
int  tupnatts,
TupleDesc  tupdesc,
ScanDirection  dir,
bool continuescan 
)
static

Definition at line 3882 of file nbtutils.c.

3884 {
3885  ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
3886  int32 cmpresult = 0;
3887  bool result;
3888 
3889  /* First subkey should be same as the header says */
3890  Assert(subkey->sk_attno == skey->sk_attno);
3891 
3892  /* Loop over columns of the row condition */
3893  for (;;)
3894  {
3895  Datum datum;
3896  bool isNull;
3897 
3898  Assert(subkey->sk_flags & SK_ROW_MEMBER);
3899 
3900  if (subkey->sk_attno > tupnatts)
3901  {
3902  /*
3903  * This attribute is truncated (must be high key). The value for
3904  * this attribute in the first non-pivot tuple on the page to the
3905  * right could be any possible value. Assume that truncated
3906  * attribute passes the qual.
3907  */
3908  Assert(BTreeTupleIsPivot(tuple));
3909  cmpresult = 0;
3910  if (subkey->sk_flags & SK_ROW_END)
3911  break;
3912  subkey++;
3913  continue;
3914  }
3915 
3916  datum = index_getattr(tuple,
3917  subkey->sk_attno,
3918  tupdesc,
3919  &isNull);
3920 
3921  if (isNull)
3922  {
3923  if (subkey->sk_flags & SK_BT_NULLS_FIRST)
3924  {
3925  /*
3926  * Since NULLs are sorted before non-NULLs, we know we have
3927  * reached the lower limit of the range of values for this
3928  * index attr. On a backward scan, we can stop if this qual
3929  * is one of the "must match" subset. We can stop regardless
3930  * of whether the qual is > or <, so long as it's required,
3931  * because it's not possible for any future tuples to pass. On
3932  * a forward scan, however, we must keep going, because we may
3933  * have initially positioned to the start of the index.
3934  * (_bt_advance_array_keys also relies on this behavior during
3935  * forward scans.)
3936  */
3937  if ((subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
3939  *continuescan = false;
3940  }
3941  else
3942  {
3943  /*
3944  * Since NULLs are sorted after non-NULLs, we know we have
3945  * reached the upper limit of the range of values for this
3946  * index attr. On a forward scan, we can stop if this qual is
3947  * one of the "must match" subset. We can stop regardless of
3948  * whether the qual is > or <, so long as it's required,
3949  * because it's not possible for any future tuples to pass. On
3950  * a backward scan, however, we must keep going, because we
3951  * may have initially positioned to the end of the index.
3952  * (_bt_advance_array_keys also relies on this behavior during
3953  * backward scans.)
3954  */
3955  if ((subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
3957  *continuescan = false;
3958  }
3959 
3960  /*
3961  * In any case, this indextuple doesn't match the qual.
3962  */
3963  return false;
3964  }
3965 
3966  if (subkey->sk_flags & SK_ISNULL)
3967  {
3968  /*
3969  * Unlike the simple-scankey case, this isn't a disallowed case.
3970  * But it can never match. If all the earlier row comparison
3971  * columns are required for the scan direction, we can stop the
3972  * scan, because there can't be another tuple that will succeed.
3973  */
3974  if (subkey != (ScanKey) DatumGetPointer(skey->sk_argument))
3975  subkey--;
3976  if ((subkey->sk_flags & SK_BT_REQFWD) &&
3978  *continuescan = false;
3979  else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
3981  *continuescan = false;
3982  return false;
3983  }
3984 
3985  /* Perform the test --- three-way comparison not bool operator */
3986  cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
3987  subkey->sk_collation,
3988  datum,
3989  subkey->sk_argument));
3990 
3991  if (subkey->sk_flags & SK_BT_DESC)
3992  INVERT_COMPARE_RESULT(cmpresult);
3993 
3994  /* Done comparing if unequal, else advance to next column */
3995  if (cmpresult != 0)
3996  break;
3997 
3998  if (subkey->sk_flags & SK_ROW_END)
3999  break;
4000  subkey++;
4001  }
4002 
4003  /*
4004  * At this point cmpresult indicates the overall result of the row
4005  * comparison, and subkey points to the deciding column (or the last
4006  * column if the result is "=").
4007  */
4008  switch (subkey->sk_strategy)
4009  {
4010  /* EQ and NE cases aren't allowed here */
4011  case BTLessStrategyNumber:
4012  result = (cmpresult < 0);
4013  break;
4015  result = (cmpresult <= 0);
4016  break;
4018  result = (cmpresult >= 0);
4019  break;
4021  result = (cmpresult > 0);
4022  break;
4023  default:
4024  elog(ERROR, "unrecognized RowCompareType: %d",
4025  (int) subkey->sk_strategy);
4026  result = 0; /* keep compiler quiet */
4027  break;
4028  }
4029 
4030  if (!result)
4031  {
4032  /*
4033  * Tuple fails this qual. If it's a required qual for the current
4034  * scan direction, then we can conclude no further tuples will pass,
4035  * either. Note we have to look at the deciding column, not
4036  * necessarily the first or last column of the row condition.
4037  */
4038  if ((subkey->sk_flags & SK_BT_REQFWD) &&
4040  *continuescan = false;
4041  else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
4043  *continuescan = false;
4044  }
4045 
4046  return result;
4047 }
#define INVERT_COMPARE_RESULT(var)
Definition: c.h:1106
#define ERROR
Definition: elog.h:39
#define SK_BT_DESC
Definition: nbtree.h:1127
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:202
#define SK_ROW_MEMBER
Definition: skey.h:118
#define SK_ROW_END
Definition: skey.h:119
ScanKeyData * ScanKey
Definition: skey.h:75
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
int sk_flags
Definition: skey.h:66
FmgrInfo sk_func
Definition: skey.h:71
Oid sk_collation
Definition: skey.h:70
AttrNumber sk_attno
Definition: skey.h:67

References Assert, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTreeTupleIsPivot(), DatumGetInt32(), DatumGetPointer(), elog, ERROR, FunctionCall2Coll(), index_getattr(), INVERT_COMPARE_RESULT, ScanDirectionIsBackward, ScanDirectionIsForward, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_DESC, SK_BT_NULLS_FIRST, SK_BT_REQBKWD, SK_BT_REQFWD, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_ROW_END, SK_ROW_MEMBER, and ScanKeyData::sk_strategy.

Referenced by _bt_check_compare().

◆ _bt_check_third_page()

void _bt_check_third_page ( Relation  rel,
Relation  heap,
bool  needheaptidspace,
Page  page,
IndexTuple  newtup 
)

Definition at line 5071 of file nbtutils.c.

5073 {
5074  Size itemsz;
5075  BTPageOpaque opaque;
5076 
5077  itemsz = MAXALIGN(IndexTupleSize(newtup));
5078 
5079  /* Double check item size against limit */
5080  if (itemsz <= BTMaxItemSize(page))
5081  return;
5082 
5083  /*
5084  * Tuple is probably too large to fit on page, but it's possible that the
5085  * index uses version 2 or version 3, or that page is an internal page, in
5086  * which case a slightly higher limit applies.
5087  */
5088  if (!needheaptidspace && itemsz <= BTMaxItemSizeNoHeapTid(page))
5089  return;
5090 
5091  /*
5092  * Internal page insertions cannot fail here, because that would mean that
5093  * an earlier leaf level insertion that should have failed didn't
5094  */
5095  opaque = BTPageGetOpaque(page);
5096  if (!P_ISLEAF(opaque))
5097  elog(ERROR, "cannot insert oversized tuple of size %zu on internal page of index \"%s\"",
5098  itemsz, RelationGetRelationName(rel));
5099 
5100  ereport(ERROR,
5101  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5102  errmsg("index row size %zu exceeds btree version %u maximum %zu for index \"%s\"",
5103  itemsz,
5104  needheaptidspace ? BTREE_VERSION : BTREE_NOVAC_VERSION,
5105  needheaptidspace ? BTMaxItemSize(page) :
5106  BTMaxItemSizeNoHeapTid(page),
5108  errdetail("Index row references tuple (%u,%u) in relation \"%s\".",
5111  RelationGetRelationName(heap)),
5112  errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
5113  "Consider a function index of an MD5 hash of the value, "
5114  "or use full text indexing."),
5116 }
#define MAXALIGN(LEN)
Definition: c.h:811
size_t Size
Definition: c.h:605
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
#define IndexTupleSize(itup)
Definition: itup.h:70
#define BTMaxItemSizeNoHeapTid(page)
Definition: nbtree.h:169
#define BTREE_VERSION
Definition: nbtree.h:150
#define BTMaxItemSize(page)
Definition: nbtree.h:164
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:152
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6006

References BTMaxItemSize, BTMaxItemSizeNoHeapTid, BTPageGetOpaque, BTREE_NOVAC_VERSION, BTREE_VERSION, BTreeTupleGetHeapTID(), elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, errtableconstraint(), IndexTupleSize, ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), MAXALIGN, P_ISLEAF, and RelationGetRelationName.

Referenced by _bt_buildadd(), and _bt_findinsertloc().

◆ _bt_checkkeys()

bool _bt_checkkeys ( IndexScanDesc  scan,
BTReadPageState pstate,
bool  arrayKeys,
IndexTuple  tuple,
int  tupnatts 
)

Definition at line 3502 of file nbtutils.c.

3504 {
3505  TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
3506  BTScanOpaque so = (BTScanOpaque) scan->opaque;
3507  ScanDirection dir = pstate->dir;
3508  int ikey = 0;
3509  bool res;
3510 
3511  Assert(BTreeTupleGetNAtts(tuple, scan->indexRelation) == tupnatts);
3512 
3513  res = _bt_check_compare(scan, dir, tuple, tupnatts, tupdesc,
3514  arrayKeys, pstate->prechecked, pstate->firstmatch,
3515  &pstate->continuescan, &ikey);
3516 
3517 #ifdef USE_ASSERT_CHECKING
3518  if (!arrayKeys && so->numArrayKeys)
3519  {
3520  /*
3521  * This is a continuescan precheck call for a scan with array keys.
3522  *
3523  * Assert that the scan isn't in danger of becoming confused.
3524  */
3525  Assert(!so->scanBehind && !pstate->prechecked && !pstate->firstmatch);
3526  Assert(!_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc,
3527  tupnatts, false, 0, NULL));
3528  }
3529  if (pstate->prechecked || pstate->firstmatch)
3530  {
3531  bool dcontinuescan;
3532  int dikey = 0;
3533 
3534  /*
3535  * Call relied on continuescan/firstmatch prechecks -- assert that we
3536  * get the same answer without those optimizations
3537  */
3538  Assert(res == _bt_check_compare(scan, dir, tuple, tupnatts, tupdesc,
3539  false, false, false,
3540  &dcontinuescan, &dikey));
3541  Assert(pstate->continuescan == dcontinuescan);
3542  }
3543 #endif
3544 
3545  /*
3546  * Only one _bt_check_compare call is required in the common case where
3547  * there are no equality strategy array scan keys. Otherwise we can only
3548  * accept _bt_check_compare's answer unreservedly when it didn't set
3549  * pstate.continuescan=false.
3550  */
3551  if (!arrayKeys || pstate->continuescan)
3552  return res;
3553 
3554  /*
3555  * _bt_check_compare call set continuescan=false in the presence of
3556  * equality type array keys. This could mean that the tuple is just past
3557  * the end of matches for the current array keys.
3558  *
3559  * It's also possible that the scan is still _before_ the _start_ of
3560  * tuples matching the current set of array keys. Check for that first.
3561  */
3562  if (_bt_tuple_before_array_skeys(scan, dir, tuple, tupdesc, tupnatts, true,
3563  ikey, NULL))
3564  {
3565  /*
3566  * Tuple is still before the start of matches according to the scan's
3567  * required array keys (according to _all_ of its required equality
3568  * strategy keys, actually).
3569  *
3570  * _bt_advance_array_keys occasionally sets so->scanBehind to signal
3571  * that the scan's current position/tuples might be significantly
3572  * behind (multiple pages behind) its current array keys. When this
3573  * happens, we need to be prepared to recover by starting a new
3574  * primitive index scan here, on our own.
3575  */
3576  Assert(!so->scanBehind ||
3578  if (unlikely(so->scanBehind) && pstate->finaltup &&
3579  _bt_tuple_before_array_skeys(scan, dir, pstate->finaltup, tupdesc,
3580  BTreeTupleGetNAtts(pstate->finaltup,
3581  scan->indexRelation),
3582  false, 0, NULL))
3583  {
3584  /* Cut our losses -- start a new primitive index scan now */
3585  pstate->continuescan = false;
3586  so->needPrimScan = true;
3587  }
3588  else
3589  {
3590  /* Override _bt_check_compare, continue primitive scan */
3591  pstate->continuescan = true;
3592 
3593  /*
3594  * We will end up here repeatedly given a group of tuples > the
3595  * previous array keys and < the now-current keys (for a backwards
3596  * scan it's just the same, though the operators swap positions).
3597  *
3598  * We must avoid allowing this linear search process to scan very
3599  * many tuples from well before the start of tuples matching the
3600  * current array keys (or from well before the point where we'll
3601  * once again have to advance the scan's array keys).
3602  *
3603  * We keep the overhead under control by speculatively "looking
3604  * ahead" to later still-unscanned items from this same leaf page.
3605  * We'll only attempt this once the number of tuples that the
3606  * linear search process has examined starts to get out of hand.
3607  */
3608  pstate->rechecks++;
3609  if (pstate->rechecks >= LOOK_AHEAD_REQUIRED_RECHECKS)
3610  {
3611  /* See if we should skip ahead within the current leaf page */
3612  _bt_checkkeys_look_ahead(scan, pstate, tupnatts, tupdesc);
3613 
3614  /*
3615  * Might have set pstate.skip to a later page offset. When
3616  * that happens then _bt_readpage caller will inexpensively
3617  * skip ahead to a later tuple from the same page (the one
3618  * just after the tuple we successfully "looked ahead" to).
3619  */
3620  }
3621  }
3622 
3623  /* This indextuple doesn't match the current qual, in any case */
3624  return false;
3625  }
3626 
3627  /*
3628  * Caller's tuple is >= the current set of array keys and other equality
3629  * constraint scan keys (or <= if this is a backwards scan). It's now
3630  * clear that we _must_ advance any required array keys in lockstep with
3631  * the scan.
3632  */
3633  return _bt_advance_array_keys(scan, pstate, tuple, tupnatts, tupdesc,
3634  ikey, true);
3635 }
#define LOOK_AHEAD_REQUIRED_RECHECKS
Definition: nbtutils.c:32
static void _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate, int tupnatts, TupleDesc tupdesc)
Definition: nbtutils.c:4066
#define RelationGetDescr(relation)
Definition: rel.h:531

References _bt_advance_array_keys(), _bt_check_compare(), _bt_checkkeys_look_ahead(), _bt_tuple_before_array_skeys(), Assert, BTEqualStrategyNumber, BTreeTupleGetNAtts, BTReadPageState::continuescan, BTReadPageState::dir, BTReadPageState::finaltup, BTReadPageState::firstmatch, IndexScanDescData::indexRelation, BTScanOpaqueData::keyData, LOOK_AHEAD_REQUIRED_RECHECKS, BTScanOpaqueData::needPrimScan, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, BTReadPageState::prechecked, BTReadPageState::rechecks, RelationGetDescr, res, BTScanOpaqueData::scanBehind, ScanKeyData::sk_strategy, and unlikely.

Referenced by _bt_readpage().

◆ _bt_checkkeys_look_ahead()

static void _bt_checkkeys_look_ahead ( IndexScanDesc  scan,
BTReadPageState pstate,
int  tupnatts,
TupleDesc  tupdesc 
)
static

Definition at line 4066 of file nbtutils.c.

4068 {
4069  ScanDirection dir = pstate->dir;
4070  OffsetNumber aheadoffnum;
4071  IndexTuple ahead;
4072 
4073  /* Avoid looking ahead when comparing the page high key */
4074  if (pstate->offnum < pstate->minoff)
4075  return;
4076 
4077  /*
4078  * Don't look ahead when there aren't enough tuples remaining on the page
4079  * (in the current scan direction) for it to be worth our while
4080  */
4081  if (ScanDirectionIsForward(dir) &&
4082  pstate->offnum >= pstate->maxoff - LOOK_AHEAD_DEFAULT_DISTANCE)
4083  return;
4084  else if (ScanDirectionIsBackward(dir) &&
4085  pstate->offnum <= pstate->minoff + LOOK_AHEAD_DEFAULT_DISTANCE)
4086  return;
4087 
4088  /*
4089  * The look ahead distance starts small, and ramps up as each call here
4090  * allows _bt_readpage to skip over more tuples
4091  */
4092  if (!pstate->targetdistance)
4094  else
4095  pstate->targetdistance *= 2;
4096 
4097  /* Don't read past the end (or before the start) of the page, though */
4098  if (ScanDirectionIsForward(dir))
4099  aheadoffnum = Min((int) pstate->maxoff,
4100  (int) pstate->offnum + pstate->targetdistance);
4101  else
4102  aheadoffnum = Max((int) pstate->minoff,
4103  (int) pstate->offnum - pstate->targetdistance);
4104 
4105  ahead = (IndexTuple) PageGetItem(pstate->page,
4106  PageGetItemId(pstate->page, aheadoffnum));
4107  if (_bt_tuple_before_array_skeys(scan, dir, ahead, tupdesc, tupnatts,
4108  false, 0, NULL))
4109  {
4110  /*
4111  * Success -- instruct _bt_readpage to skip ahead to very next tuple
4112  * after the one we determined was still before the current array keys
4113  */
4114  if (ScanDirectionIsForward(dir))
4115  pstate->skip = aheadoffnum + 1;
4116  else
4117  pstate->skip = aheadoffnum - 1;
4118  }
4119  else
4120  {
4121  /*
4122  * Failure -- "ahead" tuple is too far ahead (we were too aggressive).
4123  *
4124  * Reset the number of rechecks, and aggressively reduce the target
4125  * distance (we're much more aggressive here than we were when the
4126  * distance was initially ramped up).
4127  */
4128  pstate->rechecks = 0;
4129  pstate->targetdistance = Max(pstate->targetdistance / 8, 1);
4130  }
4131 }
#define Min(x, y)
Definition: c.h:1004
#define Max(x, y)
Definition: c.h:998
#define LOOK_AHEAD_DEFAULT_DISTANCE
Definition: nbtutils.c:33
uint16 OffsetNumber
Definition: off.h:24
OffsetNumber minoff
Definition: nbtree.h:1090
OffsetNumber offnum
Definition: nbtree.h:1097

References _bt_tuple_before_array_skeys(), BTReadPageState::dir, LOOK_AHEAD_DEFAULT_DISTANCE, Max, BTReadPageState::maxoff, Min, BTReadPageState::minoff, BTReadPageState::offnum, BTReadPageState::page, PageGetItem(), PageGetItemId(), BTReadPageState::rechecks, ScanDirectionIsBackward, ScanDirectionIsForward, BTReadPageState::skip, and BTReadPageState::targetdistance.

Referenced by _bt_checkkeys().

◆ _bt_compare_array_elements()

static int _bt_compare_array_elements ( const void *  a,
const void *  b,
void *  arg 
)
static

Definition at line 1099 of file nbtutils.c.

1100 {
1101  Datum da = *((const Datum *) a);
1102  Datum db = *((const Datum *) b);
1104  int32 compare;
1105 
1107  cxt->collation,
1108  da, db));
1109  if (cxt->reverse)
1111  return compare;
1112 }
static int compare(const void *arg1, const void *arg2)
Definition: geqo_pool.c:145
int b
Definition: isn.c:70
int a
Definition: isn.c:69
void * arg
FmgrInfo * sortproc
Definition: nbtutils.c:37

References a, arg, b, BTSortArrayContext::collation, compare(), DatumGetInt32(), FunctionCall2Coll(), INVERT_COMPARE_RESULT, BTSortArrayContext::reverse, and BTSortArrayContext::sortproc.

Referenced by _bt_merge_arrays(), and _bt_sort_array_elements().

◆ _bt_compare_array_scankey_args()

static bool _bt_compare_array_scankey_args ( IndexScanDesc  scan,
ScanKey  arraysk,
ScanKey  skey,
FmgrInfo orderproc,
BTArrayKeyInfo array,
bool qual_ok 
)
static

Definition at line 976 of file nbtutils.c.

979 {
980  Relation rel = scan->indexRelation;
981  Oid opcintype = rel->rd_opcintype[arraysk->sk_attno - 1];
982  int cmpresult = 0,
983  cmpexact = 0,
984  matchelem,
985  new_nelems = 0;
986  FmgrInfo crosstypeproc;
987  FmgrInfo *orderprocp = orderproc;
988 
989  Assert(arraysk->sk_attno == skey->sk_attno);
990  Assert(array->num_elems > 0);
991  Assert(!(arraysk->sk_flags & (SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
992  Assert((arraysk->sk_flags & SK_SEARCHARRAY) &&
993  arraysk->sk_strategy == BTEqualStrategyNumber);
995  Assert(!(skey->sk_flags & SK_SEARCHARRAY) ||
997 
998  /*
999  * _bt_binsrch_array_skey searches an array for the entry best matching a
1000  * datum of opclass input type for the index's attribute (on-disk type).
1001  * We can reuse the array's ORDER proc whenever the non-array scan key's
1002  * type is a match for the corresponding attribute's input opclass type.
1003  * Otherwise, we have to do another ORDER proc lookup so that our call to
1004  * _bt_binsrch_array_skey applies the correct comparator.
1005  *
1006  * Note: we have to support the convention that sk_subtype == InvalidOid
1007  * means the opclass input type; this is a hack to simplify life for
1008  * ScanKeyInit().
1009  */
1010  if (skey->sk_subtype != opcintype && skey->sk_subtype != InvalidOid)
1011  {
1012  RegProcedure cmp_proc;
1013  Oid arraysk_elemtype;
1014 
1015  /*
1016  * Need an ORDER proc lookup to detect redundancy/contradictoriness
1017  * with this pair of scankeys.
1018  *
1019  * Scalar scan key's argument will be passed to _bt_compare_array_skey
1020  * as its tupdatum/lefthand argument (rhs arg is for array elements).
1021  */
1022  arraysk_elemtype = arraysk->sk_subtype;
1023  if (arraysk_elemtype == InvalidOid)
1024  arraysk_elemtype = rel->rd_opcintype[arraysk->sk_attno - 1];
1025  cmp_proc = get_opfamily_proc(rel->rd_opfamily[arraysk->sk_attno - 1],
1026  skey->sk_subtype, arraysk_elemtype,
1027  BTORDER_PROC);
1028  if (!RegProcedureIsValid(cmp_proc))
1029  {
1030  /* Can't make the comparison */
1031  *qual_ok = false; /* suppress compiler warnings */
1032  return false;
1033  }
1034 
1035  /* We have all we need to determine redundancy/contradictoriness */
1036  orderprocp = &crosstypeproc;
1037  fmgr_info(cmp_proc, orderprocp);
1038  }
1039 
1040  matchelem = _bt_binsrch_array_skey(orderprocp, false,
1042  skey->sk_argument, false, array,
1043  arraysk, &cmpresult);
1044 
1045  switch (skey->sk_strategy)
1046  {
1047  case BTLessStrategyNumber:
1048  cmpexact = 1; /* exclude exact match, if any */
1049  /* FALL THRU */
1051  if (cmpresult >= cmpexact)
1052  matchelem++;
1053  /* Resize, keeping elements from the start of the array */
1054  new_nelems = matchelem;
1055  break;
1056  case BTEqualStrategyNumber:
1057  if (cmpresult != 0)
1058  {
1059  /* qual is unsatisfiable */
1060  new_nelems = 0;
1061  }
1062  else
1063  {
1064  /* Shift matching element to the start of the array, resize */
1065  array->elem_values[0] = array->elem_values[matchelem];
1066  new_nelems = 1;
1067  }
1068  break;
1070  cmpexact = 1; /* include exact match, if any */
1071  /* FALL THRU */
1073  if (cmpresult >= cmpexact)
1074  matchelem++;
1075  /* Shift matching elements to the start of the array, resize */
1076  new_nelems = array->num_elems - matchelem;
1077  memmove(array->elem_values, array->elem_values + matchelem,
1078  sizeof(Datum) * new_nelems);
1079  break;
1080  default:
1081  elog(ERROR, "unrecognized StrategyNumber: %d",
1082  (int) skey->sk_strategy);
1083  break;
1084  }
1085 
1086  Assert(new_nelems >= 0);
1087  Assert(new_nelems <= array->num_elems);
1088 
1089  array->num_elems = new_nelems;
1090  *qual_ok = new_nelems > 0;
1091 
1092  return true;
1093 }
#define RegProcedureIsValid(p)
Definition: c.h:777
regproc RegProcedure
Definition: c.h:650
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
#define BTORDER_PROC
Definition: nbtree.h:707
#define InvalidOid
Definition: postgres_ext.h:36
@ NoMovementScanDirection
Definition: sdir.h:27
Definition: fmgr.h:57
Oid sk_subtype
Definition: skey.h:69

References _bt_binsrch_array_skey(), Assert, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTORDER_PROC, BTArrayKeyInfo::elem_values, elog, ERROR, fmgr_info(), get_opfamily_proc(), IndexScanDescData::indexRelation, InvalidOid, NoMovementScanDirection, BTArrayKeyInfo::num_elems, RelationData::rd_opcintype, RelationData::rd_opfamily, RegProcedureIsValid, ScanKeyData::sk_argument, ScanKeyData::sk_attno, ScanKeyData::sk_flags, SK_ISNULL, SK_ROW_HEADER, SK_ROW_MEMBER, SK_SEARCHARRAY, ScanKeyData::sk_strategy, and ScanKeyData::sk_subtype.

Referenced by _bt_compare_scankey_args().

◆ _bt_compare_array_skey()

static int32 _bt_compare_array_skey ( FmgrInfo orderproc,
Datum  tupdatum,
bool  tupnull,
Datum  arrdatum,
ScanKey  cur 
)
inlinestatic

Definition at line 1131 of file nbtutils.c.

1134 {
1135  int32 result = 0;
1136 
1137  Assert(cur->sk_strategy == BTEqualStrategyNumber);
1138 
1139  if (tupnull) /* NULL tupdatum */
1140  {
1141  if (cur->sk_flags & SK_ISNULL)
1142  result = 0; /* NULL "=" NULL */
1143  else if (cur->sk_flags & SK_BT_NULLS_FIRST)
1144  result = -1; /* NULL "<" NOT_NULL */
1145  else
1146  result = 1; /* NULL ">" NOT_NULL */
1147  }
1148  else if (cur->sk_flags & SK_ISNULL) /* NOT_NULL tupdatum, NULL arrdatum */
1149  {
1150  if (cur->sk_flags & SK_BT_NULLS_FIRST)
1151  result = 1; /* NOT_NULL ">" NULL */
1152  else
1153  result = -1; /* NOT_NULL "<" NULL */
1154  }
1155  else
1156  {
1157  /*
1158  * Like _bt_compare, we need to be careful of cross-type comparisons,
1159  * so the left value has to be the value that came from an index tuple
1160  */
1161  result = DatumGetInt32(FunctionCall2Coll(orderproc, cur->sk_collation,
1162  tupdatum, arrdatum));
1163 
1164  /*
1165  * We flip the sign by following the obvious rule: flip whenever the
1166  * column is a DESC column.
1167  *
1168  * _bt_compare does it the wrong way around (flip when *ASC*) in order
1169  * to compensate for passing its orderproc arguments backwards. We
1170  * don't need to play these games because we find it natural to pass
1171  * tupdatum as the left value (and arrdatum as the right value).
1172  */
1173  if (cur->sk_flags & SK_BT_DESC)
1174  INVERT_COMPARE_RESULT(result);
1175  }
1176 
1177  return result;
1178 }

References Assert, BTEqualStrategyNumber, cur, DatumGetInt32(), FunctionCall2Coll(), INVERT_COMPARE_RESULT, SK_BT_DESC, SK_BT_NULLS_FIRST, and SK_ISNULL.

Referenced by _bt_advance_array_keys(), _bt_binsrch_array_skey(), and _bt_tuple_before_array_skeys().

◆ _bt_compare_scankey_args()

static bool _bt_compare_scankey_args ( IndexScanDesc  scan,
ScanKey  op,
ScanKey  leftarg,
ScanKey  rightarg,
BTArrayKeyInfo array,
FmgrInfo orderproc,
bool result 
)
static

Definition at line 3116 of file nbtutils.c.

3120 {
3121  Relation rel = scan->indexRelation;
3122  Oid lefttype,
3123  righttype,
3124  optype,
3125  opcintype,
3126  cmp_op;
3127  StrategyNumber strat;
3128 
3129  /*
3130  * First, deal with cases where one or both args are NULL. This should
3131  * only happen when the scankeys represent IS NULL/NOT NULL conditions.
3132  */
3133  if ((leftarg->sk_flags | rightarg->sk_flags) & SK_ISNULL)
3134  {
3135  bool leftnull,
3136  rightnull;
3137 
3138  if (leftarg->sk_flags & SK_ISNULL)
3139  {
3141  leftnull = true;
3142  }
3143  else
3144  leftnull = false;
3145  if (rightarg->sk_flags & SK_ISNULL)
3146  {
3147  Assert(rightarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
3148  rightnull = true;
3149  }
3150  else
3151  rightnull = false;
3152 
3153  /*
3154  * We treat NULL as either greater than or less than all other values.
3155  * Since true > false, the tests below work correctly for NULLS LAST
3156  * logic. If the index is NULLS FIRST, we need to flip the strategy.
3157  */
3158  strat = op->sk_strategy;
3159  if (op->sk_flags & SK_BT_NULLS_FIRST)
3160  strat = BTCommuteStrategyNumber(strat);
3161 
3162  switch (strat)
3163  {
3164  case BTLessStrategyNumber:
3165  *result = (leftnull < rightnull);
3166  break;
3168  *result = (leftnull <= rightnull);
3169  break;
3170  case BTEqualStrategyNumber:
3171  *result = (leftnull == rightnull);
3172  break;
3174  *result = (leftnull >= rightnull);
3175  break;
3177  *result = (leftnull > rightnull);
3178  break;
3179  default:
3180  elog(ERROR, "unrecognized StrategyNumber: %d", (int) strat);
3181  *result = false; /* keep compiler quiet */
3182  break;
3183  }
3184  return true;
3185  }
3186 
3187  /*
3188  * If either leftarg or rightarg are equality-type array scankeys, we need
3189  * specialized handling (since by now we know that IS NULL wasn't used)
3190  */
3191  if (array)
3192  {
3193  bool leftarray,
3194  rightarray;
3195 
3196  leftarray = ((leftarg->sk_flags & SK_SEARCHARRAY) &&
3197  leftarg->sk_strategy == BTEqualStrategyNumber);
3198  rightarray = ((rightarg->sk_flags & SK_SEARCHARRAY) &&
3199  rightarg->sk_strategy == BTEqualStrategyNumber);
3200 
3201  /*
3202  * _bt_preprocess_array_keys is responsible for merging together array
3203  * scan keys, and will do so whenever the opfamily has the required
3204  * cross-type support. If it failed to do that, we handle it just
3205  * like the case where we can't make the comparison ourselves.
3206  */
3207  if (leftarray && rightarray)
3208  {
3209  /* Can't make the comparison */
3210  *result = false; /* suppress compiler warnings */
3211  return false;
3212  }
3213 
3214  /*
3215  * Otherwise we need to determine if either one of leftarg or rightarg
3216  * uses an array, then pass this through to a dedicated helper
3217  * function.
3218  */
3219  if (leftarray)
3220  return _bt_compare_array_scankey_args(scan, leftarg, rightarg,
3221  orderproc, array, result);
3222  else if (rightarray)
3223  return _bt_compare_array_scankey_args(scan, rightarg, leftarg,
3224  orderproc, array, result);
3225 
3226  /* FALL THRU */
3227  }
3228 
3229  /*
3230  * The opfamily we need to worry about is identified by the index column.
3231  */
3232  Assert(leftarg->sk_attno == rightarg->sk_attno);
3233 
3234  opcintype = rel->rd_opcintype[leftarg->sk_attno - 1];
3235 
3236  /*
3237  * Determine the actual datatypes of the ScanKey arguments. We have to
3238  * support the convention that sk_subtype == InvalidOid means the opclass
3239  * input type; this is a hack to simplify life for ScanKeyInit().
3240  */
3241  lefttype = leftarg->sk_subtype;
3242  if (lefttype == InvalidOid)
3243  lefttype = opcintype;
3244  righttype = rightarg->sk_subtype;
3245  if (righttype == InvalidOid)
3246  righttype = opcintype;
3247  optype = op->sk_subtype;
3248  if (optype == InvalidOid)
3249  optype = opcintype;
3250 
3251  /*
3252  * If leftarg and rightarg match the types expected for the "op" scankey,
3253  * we can use its already-looked-up comparison function.
3254  */
3255  if (lefttype == opcintype && righttype == optype)
3256  {
3257  *result = DatumGetBool(FunctionCall2Coll(&op->sk_func,
3258  op->sk_collation,
3259  leftarg->sk_argument,
3260  rightarg->sk_argument));
3261  return true;
3262  }
3263 
3264  /*
3265  * Otherwise, we need to go to the syscache to find the appropriate
3266  * operator. (This cannot result in infinite recursion, since no
3267  * indexscan initiated by syscache lookup will use cross-data-type
3268  * operators.)
3269  *
3270  * If the sk_strategy was flipped by _bt_fix_scankey_strategy, we have to
3271  * un-flip it to get the correct opfamily member.
3272  */
3273  strat = op->sk_strategy;
3274  if (op->sk_flags & SK_BT_DESC)
3275  strat = BTCommuteStrategyNumber(strat);
3276 
3277  cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1],
3278  lefttype,
3279  righttype,
3280  strat);
3281  if (OidIsValid(cmp_op))
3282  {
3283  RegProcedure cmp_proc = get_opcode(cmp_op);
3284 
3285  if (RegProcedureIsValid(cmp_proc))
3286  {
3287  *result = DatumGetBool(OidFunctionCall2Coll(cmp_proc,
3288  op->sk_collation,
3289  leftarg->sk_argument,
3290  rightarg->sk_argument));
3291  return true;
3292  }
3293  }
3294 
3295  /* Can't make the comparison */
3296  *result = false; /* suppress compiler warnings */
3297  return false;
3298 }
Datum OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1421
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1285
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
#define BTCommuteStrategyNumber(strat)
Definition: nbtree.h:685
static bool _bt_compare_array_scankey_args(IndexScanDesc scan, ScanKey arraysk, ScanKey skey, FmgrInfo *orderproc, BTArrayKeyInfo *array, bool *qual_ok)
Definition: nbtutils.c:976
uint16 StrategyNumber
Definition: stratnum.h:22

References _bt_compare_array_scankey_args(), Assert, BTCommuteStrategyNumber, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, DatumGetBool(), elog, ERROR, FunctionCall2Coll(), get_opcode(), get_opfamily_member(), IndexScanDescData::indexRelation, InvalidOid, OidFunctionCall2Coll(), OidIsValid, RelationData::rd_opcintype, RelationData::rd_opfamily, RegProcedureIsValid, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_DESC, SK_BT_NULLS_FIRST, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, SK_ISNULL, SK_SEARCHARRAY, SK_SEARCHNOTNULL, SK_SEARCHNULL, ScanKeyData::sk_strategy, and ScanKeyData::sk_subtype.

Referenced by _bt_preprocess_keys().

◆ _bt_end_vacuum()

void _bt_end_vacuum ( Relation  rel)

Definition at line 4473 of file nbtutils.c.

4474 {
4475  int i;
4476 
4477  LWLockAcquire(BtreeVacuumLock, LW_EXCLUSIVE);
4478 
4479  /* Find the array entry */
4480  for (i = 0; i < btvacinfo->num_vacuums; i++)
4481  {
4482  BTOneVacInfo *vac = &btvacinfo->vacuums[i];
4483 
4484  if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
4485  vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
4486  {
4487  /* Remove it by shifting down the last entry */
4488  *vac = btvacinfo->vacuums[btvacinfo->num_vacuums - 1];
4490  break;
4491  }
4492  }
4493 
4494  LWLockRelease(BtreeVacuumLock);
4495 }
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1168
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1781
@ LW_EXCLUSIVE
Definition: lwlock.h:114
static BTVacInfo * btvacinfo
Definition: nbtutils.c:4369
LockRelId relid
Definition: nbtutils.c:4357
int num_vacuums
Definition: nbtutils.c:4364
BTOneVacInfo vacuums[FLEXIBLE_ARRAY_MEMBER]
Definition: nbtutils.c:4366
LockRelId lockRelId
Definition: rel.h:46
Oid relId
Definition: rel.h:40
Oid dbId
Definition: rel.h:41
LockInfoData rd_lockInfo
Definition: rel.h:114

References btvacinfo, LockRelId::dbId, i, LockInfoData::lockRelId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), BTVacInfo::num_vacuums, RelationData::rd_lockInfo, BTOneVacInfo::relid, LockRelId::relId, and BTVacInfo::vacuums.

Referenced by _bt_end_vacuum_callback(), and btbulkdelete().

◆ _bt_end_vacuum_callback()

void _bt_end_vacuum_callback ( int  code,
Datum  arg 
)

Definition at line 4501 of file nbtutils.c.

4502 {
4504 }
void _bt_end_vacuum(Relation rel)
Definition: nbtutils.c:4473

References _bt_end_vacuum(), arg, and DatumGetPointer().

Referenced by btbulkdelete().

◆ _bt_find_extreme_element()

static Datum _bt_find_extreme_element ( IndexScanDesc  scan,
ScanKey  skey,
Oid  elemtype,
StrategyNumber  strat,
Datum elems,
int  nelems 
)
static

Definition at line 789 of file nbtutils.c.

792 {
793  Relation rel = scan->indexRelation;
794  Oid cmp_op;
795  RegProcedure cmp_proc;
796  FmgrInfo flinfo;
797  Datum result;
798  int i;
799 
800  /*
801  * Look up the appropriate comparison operator in the opfamily.
802  *
803  * Note: it's possible that this would fail, if the opfamily is
804  * incomplete, but it seems quite unlikely that an opfamily would omit
805  * non-cross-type comparison operators for any datatype that it supports
806  * at all.
807  */
809  Assert(OidIsValid(elemtype));
810  cmp_op = get_opfamily_member(rel->rd_opfamily[skey->sk_attno - 1],
811  elemtype,
812  elemtype,
813  strat);
814  if (!OidIsValid(cmp_op))
815  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
816  strat, elemtype, elemtype,
817  rel->rd_opfamily[skey->sk_attno - 1]);
818  cmp_proc = get_opcode(cmp_op);
819  if (!RegProcedureIsValid(cmp_proc))
820  elog(ERROR, "missing oprcode for operator %u", cmp_op);
821 
822  fmgr_info(cmp_proc, &flinfo);
823 
824  Assert(nelems > 0);
825  result = elems[0];
826  for (i = 1; i < nelems; i++)
827  {
828  if (DatumGetBool(FunctionCall2Coll(&flinfo,
829  skey->sk_collation,
830  elems[i],
831  result)))
832  result = elems[i];
833  }
834 
835  return result;
836 }

References Assert, BTEqualStrategyNumber, DatumGetBool(), elog, ERROR, fmgr_info(), FunctionCall2Coll(), get_opcode(), get_opfamily_member(), i, IndexScanDescData::indexRelation, OidIsValid, RelationData::rd_opfamily, RegProcedureIsValid, ScanKeyData::sk_attno, ScanKeyData::sk_collation, and ScanKeyData::sk_strategy.

Referenced by _bt_preprocess_array_keys().

◆ _bt_fix_scankey_strategy()

static bool _bt_fix_scankey_strategy ( ScanKey  skey,
int16 indoption 
)
static

Definition at line 3323 of file nbtutils.c.

3324 {
3325  int addflags;
3326 
3327  addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
3328 
3329  /*
3330  * We treat all btree operators as strict (even if they're not so marked
3331  * in pg_proc). This means that it is impossible for an operator condition
3332  * with a NULL comparison constant to succeed, and we can reject it right
3333  * away.
3334  *
3335  * However, we now also support "x IS NULL" clauses as search conditions,
3336  * so in that case keep going. The planner has not filled in any
3337  * particular strategy in this case, so set it to BTEqualStrategyNumber
3338  * --- we can treat IS NULL as an equality operator for purposes of search
3339  * strategy.
3340  *
3341  * Likewise, "x IS NOT NULL" is supported. We treat that as either "less
3342  * than NULL" in a NULLS LAST index, or "greater than NULL" in a NULLS
3343  * FIRST index.
3344  *
3345  * Note: someday we might have to fill in sk_collation from the index
3346  * column's collation. At the moment this is a non-issue because we'll
3347  * never actually call the comparison operator on a NULL.
3348  */
3349  if (skey->sk_flags & SK_ISNULL)
3350  {
3351  /* SK_ISNULL shouldn't be set in a row header scankey */
3352  Assert(!(skey->sk_flags & SK_ROW_HEADER));
3353 
3354  /* Set indoption flags in scankey (might be done already) */
3355  skey->sk_flags |= addflags;
3356 
3357  /* Set correct strategy for IS NULL or NOT NULL search */
3358  if (skey->sk_flags & SK_SEARCHNULL)
3359  {
3361  skey->sk_subtype = InvalidOid;
3362  skey->sk_collation = InvalidOid;
3363  }
3364  else if (skey->sk_flags & SK_SEARCHNOTNULL)
3365  {
3366  if (skey->sk_flags & SK_BT_NULLS_FIRST)
3368  else
3370  skey->sk_subtype = InvalidOid;
3371  skey->sk_collation = InvalidOid;
3372  }
3373  else
3374  {
3375  /* regular qual, so it cannot be satisfied */
3376  return false;
3377  }
3378 
3379  /* Needn't do the rest */
3380  return true;
3381  }
3382 
3383  if (skey->sk_strategy == InvalidStrategy)
3384  {
3385  /* Already-eliminated array scan key; don't need to fix anything */
3386  Assert(skey->sk_flags & SK_SEARCHARRAY);
3387  return true;
3388  }
3389 
3390  /* Adjust strategy for DESC, if we didn't already */
3391  if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC))
3393  skey->sk_flags |= addflags;
3394 
3395  /* If it's a row header, fix row member flags and strategies similarly */
3396  if (skey->sk_flags & SK_ROW_HEADER)
3397  {
3398  ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
3399 
3400  for (;;)
3401  {
3402  Assert(subkey->sk_flags & SK_ROW_MEMBER);
3403  addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
3404  if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
3405  subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
3406  subkey->sk_flags |= addflags;
3407  if (subkey->sk_flags & SK_ROW_END)
3408  break;
3409  subkey++;
3410  }
3411  }
3412 
3413  return true;
3414 }
#define SK_BT_INDOPTION_SHIFT
Definition: nbtree.h:1126
#define InvalidStrategy
Definition: stratnum.h:24

References Assert, BTCommuteStrategyNumber, BTEqualStrategyNumber, BTGreaterStrategyNumber, BTLessStrategyNumber, DatumGetPointer(), InvalidOid, InvalidStrategy, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_DESC, SK_BT_INDOPTION_SHIFT, SK_BT_NULLS_FIRST, ScanKeyData::sk_collation, ScanKeyData::sk_flags, SK_ISNULL, SK_ROW_END, SK_ROW_HEADER, SK_ROW_MEMBER, SK_SEARCHARRAY, SK_SEARCHNOTNULL, SK_SEARCHNULL, ScanKeyData::sk_strategy, and ScanKeyData::sk_subtype.

Referenced by _bt_preprocess_keys().

◆ _bt_freestack()

void _bt_freestack ( BTStack  stack)

Definition at line 221 of file nbtutils.c.

222 {
223  BTStack ostack;
224 
225  while (stack != NULL)
226  {
227  ostack = stack;
228  stack = stack->bts_parent;
229  pfree(ostack);
230  }
231 }
void pfree(void *pointer)
Definition: mcxt.c:1521
struct BTStackData * bts_parent
Definition: nbtree.h:736

References BTStackData::bts_parent, and pfree().

Referenced by _bt_doinsert(), _bt_first(), and bt_rootdescend().

◆ _bt_keep_natts()

static int _bt_keep_natts ( Relation  rel,
IndexTuple  lastleft,
IndexTuple  firstright,
BTScanInsert  itup_key 
)
static

Definition at line 4790 of file nbtutils.c.

4792 {
4793  int nkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
4794  TupleDesc itupdesc = RelationGetDescr(rel);
4795  int keepnatts;
4796  ScanKey scankey;
4797 
4798  /*
4799  * _bt_compare() treats truncated key attributes as having the value minus
4800  * infinity, which would break searches within !heapkeyspace indexes. We
4801  * must still truncate away non-key attribute values, though.
4802  */
4803  if (!itup_key->heapkeyspace)
4804  return nkeyatts;
4805 
4806  scankey = itup_key->scankeys;
4807  keepnatts = 1;
4808  for (int attnum = 1; attnum <= nkeyatts; attnum++, scankey++)
4809  {
4810  Datum datum1,
4811  datum2;
4812  bool isNull1,
4813  isNull2;
4814 
4815  datum1 = index_getattr(lastleft, attnum, itupdesc, &isNull1);
4816  datum2 = index_getattr(firstright, attnum, itupdesc, &isNull2);
4817 
4818  if (isNull1 != isNull2)
4819  break;
4820 
4821  if (!isNull1 &&
4823  scankey->sk_collation,
4824  datum1,
4825  datum2)) != 0)
4826  break;
4827 
4828  keepnatts++;
4829  }
4830 
4831  /*
4832  * Assert that _bt_keep_natts_fast() agrees with us in passing. This is
4833  * expected in an allequalimage index.
4834  */
4835  Assert(!itup_key->allequalimage ||
4836  keepnatts == _bt_keep_natts_fast(rel, lastleft, firstright));
4837 
4838  return keepnatts;
4839 }
int _bt_keep_natts_fast(Relation rel, IndexTuple lastleft, IndexTuple firstright)
Definition: nbtutils.c:4864
int16 attnum
Definition: pg_attribute.h:74
bool allequalimage
Definition: nbtree.h:787
bool heapkeyspace
Definition: nbtree.h:786
ScanKeyData scankeys[INDEX_MAX_KEYS]
Definition: nbtree.h:793

References _bt_keep_natts_fast(), BTScanInsertData::allequalimage, Assert, attnum, DatumGetInt32(), FunctionCall2Coll(), BTScanInsertData::heapkeyspace, index_getattr(), IndexRelationGetNumberOfKeyAttributes, RelationGetDescr, BTScanInsertData::scankeys, ScanKeyData::sk_collation, and ScanKeyData::sk_func.

Referenced by _bt_truncate().

◆ _bt_keep_natts_fast()

int _bt_keep_natts_fast ( Relation  rel,
IndexTuple  lastleft,
IndexTuple  firstright 
)

Definition at line 4864 of file nbtutils.c.

4865 {
4866  TupleDesc itupdesc = RelationGetDescr(rel);
4867  int keysz = IndexRelationGetNumberOfKeyAttributes(rel);
4868  int keepnatts;
4869 
4870  keepnatts = 1;
4871  for (int attnum = 1; attnum <= keysz; attnum++)
4872  {
4873  Datum datum1,
4874  datum2;
4875  bool isNull1,
4876  isNull2;
4877  Form_pg_attribute att;
4878 
4879  datum1 = index_getattr(lastleft, attnum, itupdesc, &isNull1);
4880  datum2 = index_getattr(firstright, attnum, itupdesc, &isNull2);
4881  att = TupleDescAttr(itupdesc, attnum - 1);
4882 
4883  if (isNull1 != isNull2)
4884  break;
4885 
4886  if (!isNull1 &&
4887  !datum_image_eq(datum1, datum2, att->attbyval, att->attlen))
4888  break;
4889 
4890  keepnatts++;
4891  }
4892 
4893  return keepnatts;
4894 }
bool datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
Definition: datum.c:266
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References attnum, datum_image_eq(), index_getattr(), IndexRelationGetNumberOfKeyAttributes, RelationGetDescr, and TupleDescAttr.

Referenced by _bt_afternewitemoff(), _bt_bottomupdel_pass(), _bt_dedup_pass(), _bt_do_singleval(), _bt_keep_natts(), _bt_load(), _bt_split_penalty(), and _bt_strategy().

◆ _bt_killitems()

void _bt_killitems ( IndexScanDesc  scan)

Definition at line 4165 of file nbtutils.c.

4166 {
4167  BTScanOpaque so = (BTScanOpaque) scan->opaque;
4168  Page page;
4169  BTPageOpaque opaque;
4170  OffsetNumber minoff;
4171  OffsetNumber maxoff;
4172  int i;
4173  int numKilled = so->numKilled;
4174  bool killedsomething = false;
4175  bool droppedpin PG_USED_FOR_ASSERTS_ONLY;
4176 
4178 
4179  /*
4180  * Always reset the scan state, so we don't look for same items on other
4181  * pages.
4182  */
4183  so->numKilled = 0;
4184 
4185  if (BTScanPosIsPinned(so->currPos))
4186  {
4187  /*
4188  * We have held the pin on this page since we read the index tuples,
4189  * so all we need to do is lock it. The pin will have prevented
4190  * re-use of any TID on the page, so there is no need to check the
4191  * LSN.
4192  */
4193  droppedpin = false;
4195 
4196  page = BufferGetPage(so->currPos.buf);
4197  }
4198  else
4199  {
4200  Buffer buf;
4201 
4202  droppedpin = true;
4203  /* Attempt to re-read the buffer, getting pin and lock. */
4205 
4206  page = BufferGetPage(buf);
4207  if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
4208  so->currPos.buf = buf;
4209  else
4210  {
4211  /* Modified while not pinned means hinting is not safe. */
4212  _bt_relbuf(scan->indexRelation, buf);
4213  return;
4214  }
4215  }
4216 
4217  opaque = BTPageGetOpaque(page);
4218  minoff = P_FIRSTDATAKEY(opaque);
4219  maxoff = PageGetMaxOffsetNumber(page);
4220 
4221  for (i = 0; i < numKilled; i++)
4222  {
4223  int itemIndex = so->killedItems[i];
4224  BTScanPosItem *kitem = &so->currPos.items[itemIndex];
4225  OffsetNumber offnum = kitem->indexOffset;
4226 
4227  Assert(itemIndex >= so->currPos.firstItem &&
4228  itemIndex <= so->currPos.lastItem);
4229  if (offnum < minoff)
4230  continue; /* pure paranoia */
4231  while (offnum <= maxoff)
4232  {
4233  ItemId iid = PageGetItemId(page, offnum);
4234  IndexTuple ituple = (IndexTuple) PageGetItem(page, iid);
4235  bool killtuple = false;
4236 
4237  if (BTreeTupleIsPosting(ituple))
4238  {
4239  int pi = i + 1;
4240  int nposting = BTreeTupleGetNPosting(ituple);
4241  int j;
4242 
4243  /*
4244  * We rely on the convention that heap TIDs in the scanpos
4245  * items array are stored in ascending heap TID order for a
4246  * group of TIDs that originally came from a posting list
4247  * tuple. This convention even applies during backwards
4248  * scans, where returning the TIDs in descending order might
4249  * seem more natural. This is about effectiveness, not
4250  * correctness.
4251  *
4252  * Note that the page may have been modified in almost any way
4253  * since we first read it (in the !droppedpin case), so it's
4254  * possible that this posting list tuple wasn't a posting list
4255  * tuple when we first encountered its heap TIDs.
4256  */
4257  for (j = 0; j < nposting; j++)
4258  {
4259  ItemPointer item = BTreeTupleGetPostingN(ituple, j);
4260 
4261  if (!ItemPointerEquals(item, &kitem->heapTid))
4262  break; /* out of posting list loop */
4263 
4264  /*
4265  * kitem must have matching offnum when heap TIDs match,
4266  * though only in the common case where the page can't
4267  * have been concurrently modified
4268  */
4269  Assert(kitem->indexOffset == offnum || !droppedpin);
4270 
4271  /*
4272  * Read-ahead to later kitems here.
4273  *
4274  * We rely on the assumption that not advancing kitem here
4275  * will prevent us from considering the posting list tuple
4276  * fully dead by not matching its next heap TID in next
4277  * loop iteration.
4278  *
4279  * If, on the other hand, this is the final heap TID in
4280  * the posting list tuple, then tuple gets killed
4281  * regardless (i.e. we handle the case where the last
4282  * kitem is also the last heap TID in the last index tuple
4283  * correctly -- posting tuple still gets killed).
4284  */
4285  if (pi < numKilled)
4286  kitem = &so->currPos.items[so->killedItems[pi++]];
4287  }
4288 
4289  /*
4290  * Don't bother advancing the outermost loop's int iterator to
4291  * avoid processing killed items that relate to the same
4292  * offnum/posting list tuple. This micro-optimization hardly
4293  * seems worth it. (Further iterations of the outermost loop
4294  * will fail to match on this same posting list's first heap
4295  * TID instead, so we'll advance to the next offnum/index
4296  * tuple pretty quickly.)
4297  */
4298  if (j == nposting)
4299  killtuple = true;
4300  }
4301  else if (ItemPointerEquals(&ituple->t_tid, &kitem->heapTid))
4302  killtuple = true;
4303 
4304  /*
4305  * Mark index item as dead, if it isn't already. Since this
4306  * happens while holding a buffer lock possibly in shared mode,
4307  * it's possible that multiple processes attempt to do this
4308  * simultaneously, leading to multiple full-page images being sent
4309  * to WAL (if wal_log_hints or data checksums are enabled), which
4310  * is undesirable.
4311  */
4312  if (killtuple && !ItemIdIsDead(iid))
4313  {
4314  /* found the item/all posting list items */
4315  ItemIdMarkDead(iid);
4316  killedsomething = true;
4317  break; /* out of inner search loop */
4318  }
4319  offnum = OffsetNumberNext(offnum);
4320  }
4321  }
4322 
4323  /*
4324  * Since this can be redone later if needed, mark as dirty hint.
4325  *
4326  * Whenever we mark anything LP_DEAD, we also set the page's
4327  * BTP_HAS_GARBAGE flag, which is likewise just a hint. (Note that we
4328  * only rely on the page-level flag in !heapkeyspace indexes.)
4329  */
4330  if (killedsomething)
4331  {
4332  opaque->btpo_flags |= BTP_HAS_GARBAGE;
4333  MarkBufferDirtyHint(so->currPos.buf, true);
4334  }
4335 
4336  _bt_unlockbuf(scan->indexRelation, so->currPos.buf);
4337 }
int Buffer
Definition: buf.h:23
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:3997
void MarkBufferDirtyHint(Buffer buffer, bool buffer_std)
Definition: bufmgr.c:5000
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:400
Pointer Page
Definition: bufpage.h:81
int j
Definition: isn.c:74
#define ItemIdMarkDead(itemId)
Definition: itemid.h:179
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1023
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:845
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1070
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1039
#define BTScanPosIsPinned(scanpos)
Definition: nbtree.h:999
static uint16 BTreeTupleGetNPosting(IndexTuple posting)
Definition: nbtree.h:518
#define BTP_HAS_GARBAGE
Definition: nbtree.h:82
#define BTScanPosIsValid(scanpos)
Definition: nbtree.h:1016
static ItemPointer BTreeTupleGetPostingN(IndexTuple posting, int n)
Definition: nbtree.h:544
#define BT_READ
Definition: nbtree.h:719
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
static char * buf
Definition: pg_test_fsync.c:73
BTScanPosData currPos
Definition: nbtree.h:1077
int * killedItems
Definition: nbtree.h:1056
Buffer buf
Definition: nbtree.h:953
BlockNumber currPage
Definition: nbtree.h:956
int firstItem
Definition: nbtree.h:990
BTScanPosItem items[MaxTIDsPerBTreePage]
Definition: nbtree.h:994
XLogRecPtr lsn
Definition: nbtree.h:955
ItemPointerData heapTid
Definition: nbtree.h:946
OffsetNumber indexOffset
Definition: nbtree.h:947

References _bt_getbuf(), _bt_lockbuf(), _bt_relbuf(), _bt_unlockbuf(), Assert, BT_READ, BTP_HAS_GARBAGE, BTPageGetOpaque, BTreeTupleGetNPosting(), BTreeTupleGetPostingN(), BTreeTupleIsPosting(), BTScanPosIsPinned, BTScanPosIsValid, buf, BTScanPosData::buf, BufferGetLSNAtomic(), BufferGetPage(), BTScanPosData::currPage, BTScanOpaqueData::currPos, BTScanPosData::firstItem, BTScanPosItem::heapTid, i, BTScanPosItem::indexOffset, IndexScanDescData::indexRelation, ItemIdIsDead, ItemIdMarkDead, ItemPointerEquals(), BTScanPosData::items, j, BTScanOpaqueData::killedItems, BTScanPosData::lsn, MarkBufferDirtyHint(), BTScanOpaqueData::numKilled, OffsetNumberNext, IndexScanDescData::opaque, P_FIRSTDATAKEY, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PG_USED_FOR_ASSERTS_ONLY, and IndexTupleData::t_tid.

Referenced by _bt_steppage(), btendscan(), btrescan(), and btrestrpos().

◆ _bt_mark_scankey_required()

static void _bt_mark_scankey_required ( ScanKey  skey)
static

Definition at line 3432 of file nbtutils.c.

3433 {
3434  int addflags;
3435 
3436  switch (skey->sk_strategy)
3437  {
3438  case BTLessStrategyNumber:
3440  addflags = SK_BT_REQFWD;
3441  break;
3442  case BTEqualStrategyNumber:
3443  addflags = SK_BT_REQFWD | SK_BT_REQBKWD;
3444  break;
3447  addflags = SK_BT_REQBKWD;
3448  break;
3449  default:
3450  elog(ERROR, "unrecognized StrategyNumber: %d",
3451  (int) skey->sk_strategy);
3452  addflags = 0; /* keep compiler quiet */
3453  break;
3454  }
3455 
3456  skey->sk_flags |= addflags;
3457 
3458  if (skey->sk_flags & SK_ROW_HEADER)
3459  {
3460  ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
3461 
3462  /* First subkey should be same column/operator as the header */
3463  Assert(subkey->sk_flags & SK_ROW_MEMBER);
3464  Assert(subkey->sk_attno == skey->sk_attno);
3465  Assert(subkey->sk_strategy == skey->sk_strategy);
3466  subkey->sk_flags |= addflags;
3467  }
3468 }

References Assert, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, DatumGetPointer(), elog, ERROR, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_REQBKWD, SK_BT_REQFWD, ScanKeyData::sk_flags, SK_ROW_HEADER, SK_ROW_MEMBER, and ScanKeyData::sk_strategy.

Referenced by _bt_preprocess_keys().

◆ _bt_merge_arrays()

static bool _bt_merge_arrays ( IndexScanDesc  scan,
ScanKey  skey,
FmgrInfo sortproc,
bool  reverse,
Oid  origelemtype,
Oid  nextelemtype,
Datum elems_orig,
int *  nelems_orig,
Datum elems_next,
int  nelems_next 
)
static

Definition at line 893 of file nbtutils.c.

897 {
898  Relation rel = scan->indexRelation;
899  BTScanOpaque so = (BTScanOpaque) scan->opaque;
900  BTSortArrayContext cxt;
901  int nelems_orig_start = *nelems_orig,
902  nelems_orig_merged = 0;
903  FmgrInfo *mergeproc = sortproc;
904  FmgrInfo crosstypeproc;
905 
907  Assert(OidIsValid(origelemtype) && OidIsValid(nextelemtype));
908 
909  if (origelemtype != nextelemtype)
910  {
911  RegProcedure cmp_proc;
912 
913  /*
914  * Cross-array-element-type merging is required, so can't just reuse
915  * sortproc when merging
916  */
917  cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
918  origelemtype, nextelemtype, BTORDER_PROC);
919  if (!RegProcedureIsValid(cmp_proc))
920  {
921  /* Can't make the required comparisons */
922  return false;
923  }
924 
925  /* We have all we need to determine redundancy/contradictoriness */
926  mergeproc = &crosstypeproc;
927  fmgr_info_cxt(cmp_proc, mergeproc, so->arrayContext);
928  }
929 
930  cxt.sortproc = mergeproc;
931  cxt.collation = skey->sk_collation;
932  cxt.reverse = reverse;
933 
934  for (int i = 0, j = 0; i < nelems_orig_start && j < nelems_next;)
935  {
936  Datum *oelem = elems_orig + i,
937  *nelem = elems_next + j;
938  int res = _bt_compare_array_elements(oelem, nelem, &cxt);
939 
940  if (res == 0)
941  {
942  elems_orig[nelems_orig_merged++] = *oelem;
943  i++;
944  j++;
945  }
946  else if (res < 0)
947  i++;
948  else /* res > 0 */
949  j++;
950  }
951 
952  *nelems_orig = nelems_orig_merged;
953 
954  return true;
955 }
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
static int _bt_compare_array_elements(const void *a, const void *b, void *arg)
Definition: nbtutils.c:1099
MemoryContext arrayContext
Definition: nbtree.h:1053

References _bt_compare_array_elements(), BTScanOpaqueData::arrayContext, Assert, BTEqualStrategyNumber, BTORDER_PROC, fmgr_info_cxt(), get_opfamily_proc(), i, IndexScanDescData::indexRelation, j, OidIsValid, IndexScanDescData::opaque, RelationData::rd_opfamily, RegProcedureIsValid, res, ScanKeyData::sk_attno, ScanKeyData::sk_collation, and ScanKeyData::sk_strategy.

Referenced by _bt_preprocess_array_keys().

◆ _bt_mkscankey()

BTScanInsert _bt_mkscankey ( Relation  rel,
IndexTuple  itup 
)

Definition at line 129 of file nbtutils.c.

130 {
132  ScanKey skey;
133  TupleDesc itupdesc;
134  int indnkeyatts;
135  int16 *indoption;
136  int tupnatts;
137  int i;
138 
139  itupdesc = RelationGetDescr(rel);
140  indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
141  indoption = rel->rd_indoption;
142  tupnatts = itup ? BTreeTupleGetNAtts(itup, rel) : 0;
143 
144  Assert(tupnatts <= IndexRelationGetNumberOfAttributes(rel));
145 
146  /*
147  * We'll execute search using scan key constructed on key columns.
148  * Truncated attributes and non-key attributes are omitted from the final
149  * scan key.
150  */
151  key = palloc(offsetof(BTScanInsertData, scankeys) +
152  sizeof(ScanKeyData) * indnkeyatts);
153  if (itup)
154  _bt_metaversion(rel, &key->heapkeyspace, &key->allequalimage);
155  else
156  {
157  /* Utility statement callers can set these fields themselves */
158  key->heapkeyspace = true;
159  key->allequalimage = false;
160  }
161  key->anynullkeys = false; /* initial assumption */
162  key->nextkey = false; /* usual case, required by btinsert */
163  key->backward = false; /* usual case, required by btinsert */
164  key->keysz = Min(indnkeyatts, tupnatts);
165  key->scantid = key->heapkeyspace && itup ?
166  BTreeTupleGetHeapTID(itup) : NULL;
167  skey = key->scankeys;
168  for (i = 0; i < indnkeyatts; i++)
169  {
170  FmgrInfo *procinfo;
171  Datum arg;
172  bool null;
173  int flags;
174 
175  /*
176  * We can use the cached (default) support procs since no cross-type
177  * comparison can be needed.
178  */
179  procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
180 
181  /*
182  * Key arguments built from truncated attributes (or when caller
183  * provides no tuple) are defensively represented as NULL values. They
184  * should never be used.
185  */
186  if (i < tupnatts)
187  arg = index_getattr(itup, i + 1, itupdesc, &null);
188  else
189  {
190  arg = (Datum) 0;
191  null = true;
192  }
193  flags = (null ? SK_ISNULL : 0) | (indoption[i] << SK_BT_INDOPTION_SHIFT);
195  flags,
196  (AttrNumber) (i + 1),
198  InvalidOid,
199  rel->rd_indcollation[i],
200  procinfo,
201  arg);
202  /* Record if any key attribute is NULL (or truncated) */
203  if (null)
204  key->anynullkeys = true;
205  }
206 
207  /*
208  * In NULLS NOT DISTINCT mode, we pretend that there are no null keys, so
209  * that full uniqueness check is done.
210  */
211  if (rel->rd_index->indnullsnotdistinct)
212  key->anynullkeys = false;
213 
214  return key;
215 }
int16 AttrNumber
Definition: attnum.h:21
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:860
void * palloc(Size size)
Definition: mcxt.c:1317
void _bt_metaversion(Relation rel, bool *heapkeyspace, bool *allequalimage)
Definition: nbtpage.c:739
void ScanKeyEntryInitializeWithInfo(ScanKey entry, int flags, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, Oid collation, FmgrInfo *finfo, Datum argument)
Definition: scankey.c:101
int16 * rd_indoption
Definition: rel.h:211
Form_pg_index rd_index
Definition: rel.h:192

References _bt_metaversion(), arg, Assert, BTORDER_PROC, BTreeTupleGetHeapTID(), BTreeTupleGetNAtts, i, index_getattr(), index_getprocinfo(), IndexRelationGetNumberOfAttributes, IndexRelationGetNumberOfKeyAttributes, InvalidOid, InvalidStrategy, sort-test::key, Min, palloc(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_indoption, RelationGetDescr, ScanKeyEntryInitializeWithInfo(), SK_BT_INDOPTION_SHIFT, and SK_ISNULL.

Referenced by _bt_doinsert(), _bt_leafbuild(), _bt_pagedel(), bt_mkscankey_pivotsearch(), bt_rootdescend(), tuplesort_begin_cluster(), and tuplesort_begin_index_btree().

◆ _bt_preprocess_array_keys()

static ScanKey _bt_preprocess_array_keys ( IndexScanDesc  scan)
static

Definition at line 269 of file nbtutils.c.

270 {
271  BTScanOpaque so = (BTScanOpaque) scan->opaque;
272  Relation rel = scan->indexRelation;
273  int numberOfKeys = scan->numberOfKeys;
274  int16 *indoption = rel->rd_indoption;
275  int numArrayKeys;
276  int origarrayatt = InvalidAttrNumber,
277  origarraykey = -1;
278  Oid origelemtype = InvalidOid;
279  ScanKey cur;
280  MemoryContext oldContext;
281  ScanKey arrayKeyData; /* modified copy of scan->keyData */
282 
283  Assert(numberOfKeys);
284 
285  /* Quick check to see if there are any array keys */
286  numArrayKeys = 0;
287  for (int i = 0; i < numberOfKeys; i++)
288  {
289  cur = &scan->keyData[i];
290  if (cur->sk_flags & SK_SEARCHARRAY)
291  {
292  numArrayKeys++;
293  Assert(!(cur->sk_flags & (SK_ROW_HEADER | SK_SEARCHNULL | SK_SEARCHNOTNULL)));
294  /* If any arrays are null as a whole, we can quit right now. */
295  if (cur->sk_flags & SK_ISNULL)
296  {
297  so->qual_ok = false;
298  return NULL;
299  }
300  }
301  }
302 
303  /* Quit if nothing to do. */
304  if (numArrayKeys == 0)
305  return NULL;
306 
307  /*
308  * Make a scan-lifespan context to hold array-associated data, or reset it
309  * if we already have one from a previous rescan cycle.
310  */
311  if (so->arrayContext == NULL)
313  "BTree array context",
315  else
317 
318  oldContext = MemoryContextSwitchTo(so->arrayContext);
319 
320  /* Create modifiable copy of scan->keyData in the workspace context */
321  arrayKeyData = (ScanKey) palloc(numberOfKeys * sizeof(ScanKeyData));
322  memcpy(arrayKeyData, scan->keyData, numberOfKeys * sizeof(ScanKeyData));
323 
324  /* Allocate space for per-array data in the workspace context */
325  so->arrayKeys = (BTArrayKeyInfo *) palloc(numArrayKeys * sizeof(BTArrayKeyInfo));
326 
327  /* Allocate space for ORDER procs used to help _bt_checkkeys */
328  so->orderProcs = (FmgrInfo *) palloc(numberOfKeys * sizeof(FmgrInfo));
329 
330  /* Now process each array key */
331  numArrayKeys = 0;
332  for (int i = 0; i < numberOfKeys; i++)
333  {
334  FmgrInfo sortproc;
335  FmgrInfo *sortprocp = &sortproc;
336  Oid elemtype;
337  bool reverse;
338  ArrayType *arrayval;
339  int16 elmlen;
340  bool elmbyval;
341  char elmalign;
342  int num_elems;
343  Datum *elem_values;
344  bool *elem_nulls;
345  int num_nonnulls;
346  int j;
347 
348  cur = &arrayKeyData[i];
349  if (!(cur->sk_flags & SK_SEARCHARRAY))
350  continue;
351 
352  /*
353  * First, deconstruct the array into elements. Anything allocated
354  * here (including a possibly detoasted array value) is in the
355  * workspace context.
356  */
357  arrayval = DatumGetArrayTypeP(cur->sk_argument);
358  /* We could cache this data, but not clear it's worth it */
360  &elmlen, &elmbyval, &elmalign);
361  deconstruct_array(arrayval,
362  ARR_ELEMTYPE(arrayval),
363  elmlen, elmbyval, elmalign,
364  &elem_values, &elem_nulls, &num_elems);
365 
366  /*
367  * Compress out any null elements. We can ignore them since we assume
368  * all btree operators are strict.
369  */
370  num_nonnulls = 0;
371  for (j = 0; j < num_elems; j++)
372  {
373  if (!elem_nulls[j])
374  elem_values[num_nonnulls++] = elem_values[j];
375  }
376 
377  /* We could pfree(elem_nulls) now, but not worth the cycles */
378 
379  /* If there's no non-nulls, the scan qual is unsatisfiable */
380  if (num_nonnulls == 0)
381  {
382  so->qual_ok = false;
383  break;
384  }
385 
386  /*
387  * Determine the nominal datatype of the array elements. We have to
388  * support the convention that sk_subtype == InvalidOid means the
389  * opclass input type; this is a hack to simplify life for
390  * ScanKeyInit().
391  */
392  elemtype = cur->sk_subtype;
393  if (elemtype == InvalidOid)
394  elemtype = rel->rd_opcintype[cur->sk_attno - 1];
395 
396  /*
397  * If the comparison operator is not equality, then the array qual
398  * degenerates to a simple comparison against the smallest or largest
399  * non-null array element, as appropriate.
400  */
401  switch (cur->sk_strategy)
402  {
405  cur->sk_argument =
406  _bt_find_extreme_element(scan, cur, elemtype,
408  elem_values, num_nonnulls);
409  continue;
411  /* proceed with rest of loop */
412  break;
415  cur->sk_argument =
416  _bt_find_extreme_element(scan, cur, elemtype,
418  elem_values, num_nonnulls);
419  continue;
420  default:
421  elog(ERROR, "unrecognized StrategyNumber: %d",
422  (int) cur->sk_strategy);
423  break;
424  }
425 
426  /*
427  * We'll need a 3-way ORDER proc to perform binary searches for the
428  * next matching array element. Set that up now.
429  *
430  * Array scan keys with cross-type equality operators will require a
431  * separate same-type ORDER proc for sorting their array. Otherwise,
432  * sortproc just points to the same proc used during binary searches.
433  */
434  _bt_setup_array_cmp(scan, cur, elemtype,
435  &so->orderProcs[i], &sortprocp);
436 
437  /*
438  * Sort the non-null elements and eliminate any duplicates. We must
439  * sort in the same ordering used by the index column, so that the
440  * arrays can be advanced in lockstep with the scan's progress through
441  * the index's key space.
442  */
443  reverse = (indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0;
444  num_elems = _bt_sort_array_elements(cur, sortprocp, reverse,
445  elem_values, num_nonnulls);
446 
447  if (origarrayatt == cur->sk_attno)
448  {
449  BTArrayKeyInfo *orig = &so->arrayKeys[origarraykey];
450 
451  /*
452  * This array scan key is redundant with a previous equality
453  * operator array scan key. Merge the two arrays together to
454  * eliminate contradictory non-intersecting elements (or try to).
455  *
456  * We merge this next array back into attribute's original array.
457  */
458  Assert(arrayKeyData[orig->scan_key].sk_attno == cur->sk_attno);
459  Assert(arrayKeyData[orig->scan_key].sk_collation ==
460  cur->sk_collation);
461  if (_bt_merge_arrays(scan, cur, sortprocp, reverse,
462  origelemtype, elemtype,
463  orig->elem_values, &orig->num_elems,
464  elem_values, num_elems))
465  {
466  /* Successfully eliminated this array */
467  pfree(elem_values);
468 
469  /*
470  * If no intersecting elements remain in the original array,
471  * the scan qual is unsatisfiable
472  */
473  if (orig->num_elems == 0)
474  {
475  so->qual_ok = false;
476  break;
477  }
478 
479  /*
480  * Indicate to _bt_preprocess_keys caller that it must ignore
481  * this scan key
482  */
483  cur->sk_strategy = InvalidStrategy;
484  continue;
485  }
486 
487  /*
488  * Unable to merge this array with previous array due to a lack of
489  * suitable cross-type opfamily support. Will need to keep both
490  * scan keys/arrays.
491  */
492  }
493  else
494  {
495  /*
496  * This array is the first for current index attribute.
497  *
498  * If it turns out to not be the last array (that is, if the next
499  * array is redundantly applied to this same index attribute),
500  * we'll then treat this array as the attribute's "original" array
501  * when merging.
502  */
503  origarrayatt = cur->sk_attno;
504  origarraykey = numArrayKeys;
505  origelemtype = elemtype;
506  }
507 
508  /*
509  * And set up the BTArrayKeyInfo data.
510  *
511  * Note: _bt_preprocess_array_keys_final will fix-up each array's
512  * scan_key field later on, after so->keyData[] has been finalized.
513  */
514  so->arrayKeys[numArrayKeys].scan_key = i;
515  so->arrayKeys[numArrayKeys].num_elems = num_elems;
516  so->arrayKeys[numArrayKeys].elem_values = elem_values;
517  numArrayKeys++;
518  }
519 
520  so->numArrayKeys = numArrayKeys;
521 
522  MemoryContextSwitchTo(oldContext);
523 
524  return arrayKeyData;
525 }
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3619
#define InvalidAttrNumber
Definition: attnum.h:23
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2271
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
static bool _bt_merge_arrays(IndexScanDesc scan, ScanKey skey, FmgrInfo *sortproc, bool reverse, Oid origelemtype, Oid nextelemtype, Datum *elems_orig, int *nelems_orig, Datum *elems_next, int nelems_next)
Definition: nbtutils.c:893
static Datum _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey, Oid elemtype, StrategyNumber strat, Datum *elems, int nelems)
Definition: nbtutils.c:789
static void _bt_setup_array_cmp(IndexScanDesc scan, ScanKey skey, Oid elemtype, FmgrInfo *orderproc, FmgrInfo **sortprocp)
Definition: nbtutils.c:712
static int _bt_sort_array_elements(ScanKey skey, FmgrInfo *sortproc, bool reverse, Datum *elems, int nelems)
Definition: nbtutils.c:849
MemoryContextSwitchTo(old_ctx)
struct ScanKeyData * keyData
Definition: relscan.h:122

References _bt_find_extreme_element(), _bt_merge_arrays(), _bt_setup_array_cmp(), _bt_sort_array_elements(), ALLOCSET_SMALL_SIZES, AllocSetContextCreate, ARR_ELEMTYPE, BTScanOpaqueData::arrayContext, BTScanOpaqueData::arrayKeys, Assert, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, cur, CurrentMemoryContext, DatumGetArrayTypeP, deconstruct_array(), BTArrayKeyInfo::elem_values, elog, ERROR, get_typlenbyvalalign(), i, IndexScanDescData::indexRelation, InvalidAttrNumber, InvalidOid, InvalidStrategy, j, IndexScanDescData::keyData, MemoryContextReset(), MemoryContextSwitchTo(), BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, palloc(), pfree(), BTScanOpaqueData::qual_ok, BTArrayKeyInfo::scan_key, SK_ISNULL, SK_ROW_HEADER, SK_SEARCHARRAY, SK_SEARCHNOTNULL, and SK_SEARCHNULL.

Referenced by _bt_preprocess_keys().

◆ _bt_preprocess_array_keys_final()

static void _bt_preprocess_array_keys_final ( IndexScanDesc  scan,
int *  keyDataMap 
)
static

Definition at line 551 of file nbtutils.c.

552 {
553  BTScanOpaque so = (BTScanOpaque) scan->opaque;
554  Relation rel = scan->indexRelation;
555  int arrayidx = 0;
556  int last_equal_output_ikey PG_USED_FOR_ASSERTS_ONLY = -1;
557 
558  Assert(so->qual_ok);
559 
560  /*
561  * Nothing for us to do when _bt_preprocess_array_keys only had to deal
562  * with array inequalities
563  */
564  if (so->numArrayKeys == 0)
565  return;
566 
567  for (int output_ikey = 0; output_ikey < so->numberOfKeys; output_ikey++)
568  {
569  ScanKey outkey = so->keyData + output_ikey;
570  int input_ikey;
571  bool found PG_USED_FOR_ASSERTS_ONLY = false;
572 
573  Assert(outkey->sk_strategy != InvalidStrategy);
574 
575  if (outkey->sk_strategy != BTEqualStrategyNumber)
576  continue;
577 
578  input_ikey = keyDataMap[output_ikey];
579 
580  Assert(last_equal_output_ikey < output_ikey);
581  Assert(last_equal_output_ikey < input_ikey);
582  last_equal_output_ikey = output_ikey;
583 
584  /*
585  * We're lazy about looking up ORDER procs for non-array keys, since
586  * not all input keys become output keys. Take care of it now.
587  */
588  if (!(outkey->sk_flags & SK_SEARCHARRAY))
589  {
590  Oid elemtype;
591 
592  /* No need for an ORDER proc given an IS NULL scan key */
593  if (outkey->sk_flags & SK_SEARCHNULL)
594  continue;
595 
596  /*
597  * A non-required scan key doesn't need an ORDER proc, either
598  * (unless it's associated with an array, which this one isn't)
599  */
600  if (!(outkey->sk_flags & SK_BT_REQFWD))
601  continue;
602 
603  elemtype = outkey->sk_subtype;
604  if (elemtype == InvalidOid)
605  elemtype = rel->rd_opcintype[outkey->sk_attno - 1];
606 
607  _bt_setup_array_cmp(scan, outkey, elemtype,
608  &so->orderProcs[output_ikey], NULL);
609  continue;
610  }
611 
612  /*
613  * Reorder existing array scan key so->orderProcs[] entries.
614  *
615  * Doing this in-place is safe because preprocessing is required to
616  * output all equality strategy scan keys in original input order
617  * (among each group of entries against the same index attribute).
618  * This is also the order that the arrays themselves appear in.
619  */
620  so->orderProcs[output_ikey] = so->orderProcs[input_ikey];
621 
622  /* Fix-up array->scan_key references for arrays */
623  for (; arrayidx < so->numArrayKeys; arrayidx++)
624  {
625  BTArrayKeyInfo *array = &so->arrayKeys[arrayidx];
626 
627  Assert(array->num_elems > 0);
628 
629  if (array->scan_key == input_ikey)
630  {
631  /* found it */
632  array->scan_key = output_ikey;
633  found = true;
634 
635  /*
636  * Transform array scan keys that have exactly 1 element
637  * remaining (following all prior preprocessing) into
638  * equivalent non-array scan keys.
639  */
640  if (array->num_elems == 1)
641  {
642  outkey->sk_flags &= ~SK_SEARCHARRAY;
643  outkey->sk_argument = array->elem_values[0];
644  so->numArrayKeys--;
645 
646  /* If we're out of array keys, we can quit right away */
647  if (so->numArrayKeys == 0)
648  return;
649 
650  /* Shift other arrays forward */
651  memmove(array, array + 1,
652  sizeof(BTArrayKeyInfo) *
653  (so->numArrayKeys - arrayidx));
654 
655  /*
656  * Don't increment arrayidx (there was an entry that was
657  * just shifted forward to the offset at arrayidx, which
658  * will still need to be matched)
659  */
660  }
661  else
662  {
663  /* Match found, so done with this array */
664  arrayidx++;
665  }
666 
667  break;
668  }
669  }
670 
671  Assert(found);
672  }
673 
674  /*
675  * Parallel index scans require space in shared memory to store the
676  * current array elements (for arrays kept by preprocessing) to schedule
677  * the next primitive index scan. The underlying structure is protected
678  * using a spinlock, so defensively limit its size. In practice this can
679  * only affect parallel scans that use an incomplete opfamily.
680  */
681  if (scan->parallel_scan && so->numArrayKeys > INDEX_MAX_KEYS)
682  ereport(ERROR,
683  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
684  errmsg_internal("number of array scan keys left by preprocessing (%d) exceeds the maximum allowed by parallel btree index scans (%d)",
686 }
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
#define INDEX_MAX_KEYS

References _bt_setup_array_cmp(), BTScanOpaqueData::arrayKeys, Assert, BTEqualStrategyNumber, BTArrayKeyInfo::elem_values, ereport, errcode(), errmsg_internal(), ERROR, INDEX_MAX_KEYS, IndexScanDescData::indexRelation, InvalidOid, InvalidStrategy, BTScanOpaqueData::keyData, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, IndexScanDescData::parallel_scan, PG_USED_FOR_ASSERTS_ONLY, BTScanOpaqueData::qual_ok, BTArrayKeyInfo::scan_key, ScanKeyData::sk_argument, ScanKeyData::sk_attno, SK_BT_REQFWD, ScanKeyData::sk_flags, SK_SEARCHARRAY, SK_SEARCHNULL, ScanKeyData::sk_strategy, and ScanKeyData::sk_subtype.

Referenced by _bt_preprocess_keys().

◆ _bt_preprocess_keys()

void _bt_preprocess_keys ( IndexScanDesc  scan)

Definition at line 2546 of file nbtutils.c.

2547 {
2548  BTScanOpaque so = (BTScanOpaque) scan->opaque;
2549  int numberOfKeys = scan->numberOfKeys;
2550  int16 *indoption = scan->indexRelation->rd_indoption;
2551  int new_numberOfKeys;
2552  int numberOfEqualCols;
2553  ScanKey inkeys;
2554  ScanKey outkeys;
2555  ScanKey cur;
2557  bool test_result;
2558  int i,
2559  j;
2560  AttrNumber attno;
2561  ScanKey arrayKeyData;
2562  int *keyDataMap = NULL;
2563  int arrayidx = 0;
2564 
2565  if (so->numberOfKeys > 0)
2566  {
2567  /*
2568  * Only need to do preprocessing once per btrescan, at most. All
2569  * calls after the first are handled as no-ops.
2570  *
2571  * If there are array scan keys in so->keyData[], then the now-current
2572  * array elements must already be present in each array's scan key.
2573  * Verify that that happened using an assertion.
2574  */
2575  Assert(_bt_verify_keys_with_arraykeys(scan));
2576  return;
2577  }
2578 
2579  /* initialize result variables */
2580  so->qual_ok = true;
2581  so->numberOfKeys = 0;
2582 
2583  if (numberOfKeys < 1)
2584  return; /* done if qual-less scan */
2585 
2586  /* If any keys are SK_SEARCHARRAY type, set up array-key info */
2587  arrayKeyData = _bt_preprocess_array_keys(scan);
2588  if (!so->qual_ok)
2589  {
2590  /* unmatchable array, so give up */
2591  return;
2592  }
2593 
2594  /*
2595  * Treat arrayKeyData[] (a partially preprocessed copy of scan->keyData[])
2596  * as our input if _bt_preprocess_array_keys just allocated it, else just
2597  * use scan->keyData[]
2598  */
2599  if (arrayKeyData)
2600  {
2601  inkeys = arrayKeyData;
2602 
2603  /* Also maintain keyDataMap for remapping so->orderProc[] later */
2604  keyDataMap = MemoryContextAlloc(so->arrayContext,
2605  numberOfKeys * sizeof(int));
2606  }
2607  else
2608  inkeys = scan->keyData;
2609 
2610  outkeys = so->keyData;
2611  cur = &inkeys[0];
2612  /* we check that input keys are correctly ordered */
2613  if (cur->sk_attno < 1)
2614  elog(ERROR, "btree index keys must be ordered by attribute");
2615 
2616  /* We can short-circuit most of the work if there's just one key */
2617  if (numberOfKeys == 1)
2618  {
2619  /* Apply indoption to scankey (might change sk_strategy!) */
2620  if (!_bt_fix_scankey_strategy(cur, indoption))
2621  so->qual_ok = false;
2622  memcpy(outkeys, cur, sizeof(ScanKeyData));
2623  so->numberOfKeys = 1;
2624  /* We can mark the qual as required if it's for first index col */
2625  if (cur->sk_attno == 1)
2626  _bt_mark_scankey_required(outkeys);
2627  if (arrayKeyData)
2628  {
2629  /*
2630  * Don't call _bt_preprocess_array_keys_final in this fast path
2631  * (we'll miss out on the single value array transformation, but
2632  * that's not nearly as important when there's only one scan key)
2633  */
2634  Assert(cur->sk_flags & SK_SEARCHARRAY);
2635  Assert(cur->sk_strategy != BTEqualStrategyNumber ||
2636  (so->arrayKeys[0].scan_key == 0 &&
2637  OidIsValid(so->orderProcs[0].fn_oid)));
2638  }
2639 
2640  return;
2641  }
2642 
2643  /*
2644  * Otherwise, do the full set of pushups.
2645  */
2646  new_numberOfKeys = 0;
2647  numberOfEqualCols = 0;
2648 
2649  /*
2650  * Initialize for processing of keys for attr 1.
2651  *
2652  * xform[i] points to the currently best scan key of strategy type i+1; it
2653  * is NULL if we haven't yet found such a key for this attr.
2654  */
2655  attno = 1;
2656  memset(xform, 0, sizeof(xform));
2657 
2658  /*
2659  * Loop iterates from 0 to numberOfKeys inclusive; we use the last pass to
2660  * handle after-last-key processing. Actual exit from the loop is at the
2661  * "break" statement below.
2662  */
2663  for (i = 0;; cur++, i++)
2664  {
2665  if (i < numberOfKeys)
2666  {
2667  /* Apply indoption to scankey (might change sk_strategy!) */
2668  if (!_bt_fix_scankey_strategy(cur, indoption))
2669  {
2670  /* NULL can't be matched, so give up */
2671  so->qual_ok = false;
2672  return;
2673  }
2674  }
2675 
2676  /*
2677  * If we are at the end of the keys for a particular attr, finish up
2678  * processing and emit the cleaned-up keys.
2679  */
2680  if (i == numberOfKeys || cur->sk_attno != attno)
2681  {
2682  int priorNumberOfEqualCols = numberOfEqualCols;
2683 
2684  /* check input keys are correctly ordered */
2685  if (i < numberOfKeys && cur->sk_attno < attno)
2686  elog(ERROR, "btree index keys must be ordered by attribute");
2687 
2688  /*
2689  * If = has been specified, all other keys can be eliminated as
2690  * redundant. If we have a case like key = 1 AND key > 2, we can
2691  * set qual_ok to false and abandon further processing.
2692  *
2693  * We also have to deal with the case of "key IS NULL", which is
2694  * unsatisfiable in combination with any other index condition. By
2695  * the time we get here, that's been classified as an equality
2696  * check, and we've rejected any combination of it with a regular
2697  * equality condition; but not with other types of conditions.
2698  */
2699  if (xform[BTEqualStrategyNumber - 1].skey)
2700  {
2701  ScanKey eq = xform[BTEqualStrategyNumber - 1].skey;
2702  BTArrayKeyInfo *array = NULL;
2703  FmgrInfo *orderproc = NULL;
2704 
2705  if (arrayKeyData && (eq->sk_flags & SK_SEARCHARRAY))
2706  {
2707  int eq_in_ikey,
2708  eq_arrayidx;
2709 
2710  eq_in_ikey = xform[BTEqualStrategyNumber - 1].ikey;
2711  eq_arrayidx = xform[BTEqualStrategyNumber - 1].arrayidx;
2712  array = &so->arrayKeys[eq_arrayidx - 1];
2713  orderproc = so->orderProcs + eq_in_ikey;
2714 
2715  Assert(array->scan_key == eq_in_ikey);
2716  Assert(OidIsValid(orderproc->fn_oid));
2717  }
2718 
2719  for (j = BTMaxStrategyNumber; --j >= 0;)
2720  {
2721  ScanKey chk = xform[j].skey;
2722 
2723  if (!chk || j == (BTEqualStrategyNumber - 1))
2724  continue;
2725 
2726  if (eq->sk_flags & SK_SEARCHNULL)
2727  {
2728  /* IS NULL is contradictory to anything else */
2729  so->qual_ok = false;
2730  return;
2731  }
2732 
2733  if (_bt_compare_scankey_args(scan, chk, eq, chk,
2734  array, orderproc,
2735  &test_result))
2736  {
2737  if (!test_result)
2738  {
2739  /* keys proven mutually contradictory */
2740  so->qual_ok = false;
2741  return;
2742  }
2743  /* else discard the redundant non-equality key */
2744  Assert(!array || array->num_elems > 0);
2745  xform[j].skey = NULL;
2746  xform[j].ikey = -1;
2747  }
2748  /* else, cannot determine redundancy, keep both keys */
2749  }
2750  /* track number of attrs for which we have "=" keys */
2751  numberOfEqualCols++;
2752  }
2753 
2754  /* try to keep only one of <, <= */
2755  if (xform[BTLessStrategyNumber - 1].skey
2756  && xform[BTLessEqualStrategyNumber - 1].skey)
2757  {
2758  ScanKey lt = xform[BTLessStrategyNumber - 1].skey;
2759  ScanKey le = xform[BTLessEqualStrategyNumber - 1].skey;
2760 
2761  if (_bt_compare_scankey_args(scan, le, lt, le, NULL, NULL,
2762  &test_result))
2763  {
2764  if (test_result)
2765  xform[BTLessEqualStrategyNumber - 1].skey = NULL;
2766  else
2767  xform[BTLessStrategyNumber - 1].skey = NULL;
2768  }
2769  }
2770 
2771  /* try to keep only one of >, >= */
2772  if (xform[BTGreaterStrategyNumber - 1].skey
2773  && xform[BTGreaterEqualStrategyNumber - 1].skey)
2774  {
2775  ScanKey gt = xform[BTGreaterStrategyNumber - 1].skey;
2776  ScanKey ge = xform[BTGreaterEqualStrategyNumber - 1].skey;
2777 
2778  if (_bt_compare_scankey_args(scan, ge, gt, ge, NULL, NULL,
2779  &test_result))
2780  {
2781  if (test_result)
2782  xform[BTGreaterEqualStrategyNumber - 1].skey = NULL;
2783  else
2784  xform[BTGreaterStrategyNumber - 1].skey = NULL;
2785  }
2786  }
2787 
2788  /*
2789  * Emit the cleaned-up keys into the outkeys[] array, and then
2790  * mark them if they are required. They are required (possibly
2791  * only in one direction) if all attrs before this one had "=".
2792  */
2793  for (j = BTMaxStrategyNumber; --j >= 0;)
2794  {
2795  if (xform[j].skey)
2796  {
2797  ScanKey outkey = &outkeys[new_numberOfKeys++];
2798 
2799  memcpy(outkey, xform[j].skey, sizeof(ScanKeyData));
2800  if (arrayKeyData)
2801  keyDataMap[new_numberOfKeys - 1] = xform[j].ikey;
2802  if (priorNumberOfEqualCols == attno - 1)
2803  _bt_mark_scankey_required(outkey);
2804  }
2805  }
2806 
2807  /*
2808  * Exit loop here if done.
2809  */
2810  if (i == numberOfKeys)
2811  break;
2812 
2813  /* Re-initialize for new attno */
2814  attno = cur->sk_attno;
2815  memset(xform, 0, sizeof(xform));
2816  }
2817 
2818  /* check strategy this key's operator corresponds to */
2819  j = cur->sk_strategy - 1;
2820 
2821  /* if row comparison, push it directly to the output array */
2822  if (cur->sk_flags & SK_ROW_HEADER)
2823  {
2824  ScanKey outkey = &outkeys[new_numberOfKeys++];
2825 
2826  memcpy(outkey, cur, sizeof(ScanKeyData));
2827  if (arrayKeyData)
2828  keyDataMap[new_numberOfKeys - 1] = i;
2829  if (numberOfEqualCols == attno - 1)
2830  _bt_mark_scankey_required(outkey);
2831 
2832  /*
2833  * We don't support RowCompare using equality; such a qual would
2834  * mess up the numberOfEqualCols tracking.
2835  */
2836  Assert(j != (BTEqualStrategyNumber - 1));
2837  continue;
2838  }
2839 
2840  /*
2841  * Does this input scan key require further processing as an array?
2842  */
2843  if (cur->sk_strategy == InvalidStrategy)
2844  {
2845  /* _bt_preprocess_array_keys marked this array key redundant */
2846  Assert(arrayKeyData);
2847  Assert(cur->sk_flags & SK_SEARCHARRAY);
2848  continue;
2849  }
2850 
2851  if (cur->sk_strategy == BTEqualStrategyNumber &&
2852  (cur->sk_flags & SK_SEARCHARRAY))
2853  {
2854  /* _bt_preprocess_array_keys kept this array key */
2855  Assert(arrayKeyData);
2856  arrayidx++;
2857  }
2858 
2859  /*
2860  * have we seen a scan key for this same attribute and using this same
2861  * operator strategy before now?
2862  */
2863  if (xform[j].skey == NULL)
2864  {
2865  /* nope, so this scan key wins by default (at least for now) */
2866  xform[j].skey = cur;
2867  xform[j].ikey = i;
2868  xform[j].arrayidx = arrayidx;
2869  }
2870  else
2871  {
2872  FmgrInfo *orderproc = NULL;
2873  BTArrayKeyInfo *array = NULL;
2874 
2875  /*
2876  * Seen one of these before, so keep only the more restrictive key
2877  * if possible
2878  */
2879  if (j == (BTEqualStrategyNumber - 1) && arrayKeyData)
2880  {
2881  /*
2882  * Have to set up array keys
2883  */
2884  if ((cur->sk_flags & SK_SEARCHARRAY))
2885  {
2886  array = &so->arrayKeys[arrayidx - 1];
2887  orderproc = so->orderProcs + i;
2888 
2889  Assert(array->scan_key == i);
2890  Assert(OidIsValid(orderproc->fn_oid));
2891  }
2892  else if ((xform[j].skey->sk_flags & SK_SEARCHARRAY))
2893  {
2894  array = &so->arrayKeys[xform[j].arrayidx - 1];
2895  orderproc = so->orderProcs + xform[j].ikey;
2896 
2897  Assert(array->scan_key == xform[j].ikey);
2898  Assert(OidIsValid(orderproc->fn_oid));
2899  }
2900 
2901  /*
2902  * Both scan keys might have arrays, in which case we'll
2903  * arbitrarily pass only one of the arrays. That won't
2904  * matter, since _bt_compare_scankey_args is aware that two
2905  * SEARCHARRAY scan keys mean that _bt_preprocess_array_keys
2906  * failed to eliminate redundant arrays through array merging.
2907  * _bt_compare_scankey_args just returns false when it sees
2908  * this; it won't even try to examine either array.
2909  */
2910  }
2911 
2912  if (_bt_compare_scankey_args(scan, cur, cur, xform[j].skey,
2913  array, orderproc, &test_result))
2914  {
2915  /* Have all we need to determine redundancy */
2916  if (test_result)
2917  {
2918  Assert(!array || array->num_elems > 0);
2919 
2920  /*
2921  * New key is more restrictive, and so replaces old key...
2922  */
2923  if (j != (BTEqualStrategyNumber - 1) ||
2924  !(xform[j].skey->sk_flags & SK_SEARCHARRAY))
2925  {
2926  xform[j].skey = cur;
2927  xform[j].ikey = i;
2928  xform[j].arrayidx = arrayidx;
2929  }
2930  else
2931  {
2932  /*
2933  * ...unless we have to keep the old key because it's
2934  * an array that rendered the new key redundant. We
2935  * need to make sure that we don't throw away an array
2936  * scan key. _bt_compare_scankey_args expects us to
2937  * always keep arrays (and discard non-arrays).
2938  */
2939  Assert(!(cur->sk_flags & SK_SEARCHARRAY));
2940  }
2941  }
2942  else if (j == (BTEqualStrategyNumber - 1))
2943  {
2944  /* key == a && key == b, but a != b */
2945  so->qual_ok = false;
2946  return;
2947  }
2948  /* else old key is more restrictive, keep it */
2949  }
2950  else
2951  {
2952  /*
2953  * We can't determine which key is more restrictive. Push
2954  * xform[j] directly to the output array, then set xform[j] to
2955  * the new scan key.
2956  *
2957  * Note: We do things this way around so that our arrays are
2958  * always in the same order as their corresponding scan keys,
2959  * even with incomplete opfamilies. _bt_advance_array_keys
2960  * depends on this.
2961  */
2962  ScanKey outkey = &outkeys[new_numberOfKeys++];
2963 
2964  memcpy(outkey, xform[j].skey, sizeof(ScanKeyData));
2965  if (arrayKeyData)
2966  keyDataMap[new_numberOfKeys - 1] = xform[j].ikey;
2967  if (numberOfEqualCols == attno - 1)
2968  _bt_mark_scankey_required(outkey);
2969  xform[j].skey = cur;
2970  xform[j].ikey = i;
2971  xform[j].arrayidx = arrayidx;
2972  }
2973  }
2974  }
2975 
2976  so->numberOfKeys = new_numberOfKeys;
2977 
2978  /*
2979  * Now that we've built a temporary mapping from so->keyData[] (output
2980  * scan keys) to scan->keyData[] (input scan keys), fix array->scan_key
2981  * references. Also consolidate the so->orderProc[] array such that it
2982  * can be subscripted using so->keyData[]-wise offsets.
2983  */
2984  if (arrayKeyData)
2985  _bt_preprocess_array_keys_final(scan, keyDataMap);
2986 
2987  /* Could pfree arrayKeyData/keyDataMap now, but not worth the cycles */
2988 }
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
static ScanKey _bt_preprocess_array_keys(IndexScanDesc scan)
Definition: nbtutils.c:269
static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
Definition: nbtutils.c:3323
static void _bt_mark_scankey_required(ScanKey skey)
Definition: nbtutils.c:3432
static void _bt_preprocess_array_keys_final(IndexScanDesc scan, int *keyDataMap)
Definition: nbtutils.c:551
static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, BTArrayKeyInfo *array, FmgrInfo *orderproc, bool *result)
Definition: nbtutils.c:3116
#define BTMaxStrategyNumber
Definition: stratnum.h:35
Oid fn_oid
Definition: fmgr.h:59

References _bt_compare_scankey_args(), _bt_fix_scankey_strategy(), _bt_mark_scankey_required(), _bt_preprocess_array_keys(), _bt_preprocess_array_keys_final(), BTScanOpaqueData::arrayContext, BTScanOpaqueData::arrayKeys, Assert, BTEqualStrategyNumber, BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, BTLessEqualStrategyNumber, BTLessStrategyNumber, BTMaxStrategyNumber, cur, elog, ERROR, FmgrInfo::fn_oid, i, if(), IndexScanDescData::indexRelation, InvalidStrategy, j, BTScanOpaqueData::keyData, IndexScanDescData::keyData, MemoryContextAlloc(), BTArrayKeyInfo::num_elems, BTScanOpaqueData::numberOfKeys, IndexScanDescData::numberOfKeys, OidIsValid, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, BTScanOpaqueData::qual_ok, RelationData::rd_indoption, BTArrayKeyInfo::scan_key, ScanKeyData::sk_flags, SK_ROW_HEADER, SK_SEARCHARRAY, and SK_SEARCHNULL.

Referenced by _bt_first().

◆ _bt_rewind_nonrequired_arrays()

static void _bt_rewind_nonrequired_arrays ( IndexScanDesc  scan,
ScanDirection  dir 
)
static

Definition at line 1467 of file nbtutils.c.

1468 {
1469  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1470  int arrayidx = 0;
1471 
1472  for (int ikey = 0; ikey < so->numberOfKeys; ikey++)
1473  {
1474  ScanKey cur = so->keyData + ikey;
1475  BTArrayKeyInfo *array = NULL;
1476  int first_elem_dir;
1477 
1478  if (!(cur->sk_flags & SK_SEARCHARRAY) ||
1479  cur->sk_strategy != BTEqualStrategyNumber)
1480  continue;
1481 
1482  array = &so->arrayKeys[arrayidx++];
1483  Assert(array->scan_key == ikey);
1484 
1485  if ((cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)))
1486  continue;
1487 
1488  if (ScanDirectionIsForward(dir))
1489  first_elem_dir = 0;
1490  else
1491  first_elem_dir = array->num_elems - 1;
1492 
1493  if (array->cur_elem != first_elem_dir)
1494  {
1495  array->cur_elem = first_elem_dir;
1496  cur->sk_argument = array->elem_values[first_elem_dir];
1497  }
1498  }
1499 }

References BTScanOpaqueData::arrayKeys, Assert, BTEqualStrategyNumber, cur, BTArrayKeyInfo::cur_elem, BTArrayKeyInfo::elem_values, for(), BTScanOpaqueData::keyData, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTArrayKeyInfo::scan_key, ScanDirectionIsForward, SK_BT_REQBKWD, SK_BT_REQFWD, and SK_SEARCHARRAY.

Referenced by _bt_advance_array_keys().

◆ _bt_setup_array_cmp()

static void _bt_setup_array_cmp ( IndexScanDesc  scan,
ScanKey  skey,
Oid  elemtype,
FmgrInfo orderproc,
FmgrInfo **  sortprocp 
)
static

Definition at line 712 of file nbtutils.c.

714 {
715  BTScanOpaque so = (BTScanOpaque) scan->opaque;
716  Relation rel = scan->indexRelation;
717  RegProcedure cmp_proc;
718  Oid opcintype = rel->rd_opcintype[skey->sk_attno - 1];
719 
721  Assert(OidIsValid(elemtype));
722 
723  /*
724  * If scankey operator is not a cross-type comparison, we can use the
725  * cached comparison function; otherwise gotta look it up in the catalogs
726  */
727  if (elemtype == opcintype)
728  {
729  /* Set same-type ORDER procs for caller */
730  *orderproc = *index_getprocinfo(rel, skey->sk_attno, BTORDER_PROC);
731  if (sortprocp)
732  *sortprocp = orderproc;
733 
734  return;
735  }
736 
737  /*
738  * Look up the appropriate cross-type comparison function in the opfamily.
739  *
740  * Use the opclass input type as the left hand arg type, and the array
741  * element type as the right hand arg type (since binary searches use an
742  * index tuple's attribute value to search for a matching array element).
743  *
744  * Note: it's possible that this would fail, if the opfamily is
745  * incomplete, but only in cases where it's quite likely that _bt_first
746  * would fail in just the same way (had we not failed before it could).
747  */
748  cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
749  opcintype, elemtype, BTORDER_PROC);
750  if (!RegProcedureIsValid(cmp_proc))
751  elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"",
752  BTORDER_PROC, opcintype, elemtype, skey->sk_attno,
754 
755  /* Set cross-type ORDER proc for caller */
756  fmgr_info_cxt(cmp_proc, orderproc, so->arrayContext);
757 
758  /* Done if caller doesn't actually have an array they'll need to sort */
759  if (!sortprocp)
760  return;
761 
762  /*
763  * Look up the appropriate same-type comparison function in the opfamily.
764  *
765  * Note: it's possible that this would fail, if the opfamily is
766  * incomplete, but it seems quite unlikely that an opfamily would omit
767  * non-cross-type comparison procs for any datatype that it supports at
768  * all.
769  */
770  cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
771  elemtype, elemtype, BTORDER_PROC);
772  if (!RegProcedureIsValid(cmp_proc))
773  elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"",
774  BTORDER_PROC, elemtype, elemtype,
775  skey->sk_attno, RelationGetRelationName(rel));
776 
777  /* Set same-type ORDER proc for caller */
778  fmgr_info_cxt(cmp_proc, *sortprocp, so->arrayContext);
779 }

References BTScanOpaqueData::arrayContext, Assert, BTEqualStrategyNumber, BTORDER_PROC, elog, ERROR, fmgr_info_cxt(), get_opfamily_proc(), index_getprocinfo(), IndexScanDescData::indexRelation, OidIsValid, IndexScanDescData::opaque, RelationData::rd_opcintype, RegProcedureIsValid, RelationGetRelationName, ScanKeyData::sk_attno, and ScanKeyData::sk_strategy.

Referenced by _bt_preprocess_array_keys(), and _bt_preprocess_array_keys_final().

◆ _bt_sort_array_elements()

static int _bt_sort_array_elements ( ScanKey  skey,
FmgrInfo sortproc,
bool  reverse,
Datum elems,
int  nelems 
)
static

Definition at line 849 of file nbtutils.c.

851 {
852  BTSortArrayContext cxt;
853 
854  if (nelems <= 1)
855  return nelems; /* no work to do */
856 
857  /* Sort the array elements */
858  cxt.sortproc = sortproc;
859  cxt.collation = skey->sk_collation;
860  cxt.reverse = reverse;
861  qsort_arg(elems, nelems, sizeof(Datum),
863 
864  /* Now scan the sorted elements and remove duplicates */
865  return qunique_arg(elems, nelems, sizeof(Datum),
867 }
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
static size_t qunique_arg(void *array, size_t elements, size_t width, int(*compare)(const void *, const void *, void *), void *arg)
Definition: qunique.h:46

References _bt_compare_array_elements(), BTSortArrayContext::collation, qsort_arg(), qunique_arg(), BTSortArrayContext::reverse, ScanKeyData::sk_collation, and BTSortArrayContext::sortproc.

Referenced by _bt_preprocess_array_keys().

◆ _bt_start_array_keys()

void _bt_start_array_keys ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 1343 of file nbtutils.c.

1344 {
1345  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1346  int i;
1347 
1348  Assert(so->numArrayKeys);
1349  Assert(so->qual_ok);
1350 
1351  for (i = 0; i < so->numArrayKeys; i++)
1352  {
1353  BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
1354  ScanKey skey = &so->keyData[curArrayKey->scan_key];
1355 
1356  Assert(curArrayKey->num_elems > 0);
1357  Assert(skey->sk_flags & SK_SEARCHARRAY);
1358 
1359  if (ScanDirectionIsBackward(dir))
1360  curArrayKey->cur_elem = curArrayKey->num_elems - 1;
1361  else
1362  curArrayKey->cur_elem = 0;
1363  skey->sk_argument = curArrayKey->elem_values[curArrayKey->cur_elem];
1364  }
1365  so->scanBehind = false;
1366 }

References BTScanOpaqueData::arrayKeys, Assert, BTArrayKeyInfo::cur_elem, BTArrayKeyInfo::elem_values, i, BTScanOpaqueData::keyData, BTArrayKeyInfo::num_elems, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, BTScanOpaqueData::qual_ok, BTArrayKeyInfo::scan_key, BTScanOpaqueData::scanBehind, ScanDirectionIsBackward, ScanKeyData::sk_argument, ScanKeyData::sk_flags, and SK_SEARCHARRAY.

Referenced by _bt_advance_array_keys_increment(), _bt_first(), and btrestrpos().

◆ _bt_start_prim_scan()

bool _bt_start_prim_scan ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 1668 of file nbtutils.c.

1669 {
1670  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1671 
1672  Assert(so->numArrayKeys);
1673 
1674  /* scanBehind flag doesn't persist across primitive index scans - reset */
1675  so->scanBehind = false;
1676 
1677  /*
1678  * Array keys are advanced within _bt_checkkeys when the scan reaches the
1679  * leaf level (more precisely, they're advanced when the scan reaches the
1680  * end of each distinct set of array elements). This process avoids
1681  * repeat access to leaf pages (across multiple primitive index scans) by
1682  * advancing the scan's array keys when it allows the primitive index scan
1683  * to find nearby matching tuples (or when it eliminates ranges of array
1684  * key space that can't possibly be satisfied by any index tuple).
1685  *
1686  * _bt_checkkeys sets a simple flag variable to schedule another primitive
1687  * index scan. The flag tells us what to do.
1688  *
1689  * We cannot rely on _bt_first always reaching _bt_checkkeys. There are
1690  * various cases where that won't happen. For example, if the index is
1691  * completely empty, then _bt_first won't call _bt_readpage/_bt_checkkeys.
1692  * We also don't expect a call to _bt_checkkeys during searches for a
1693  * non-existent value that happens to be lower/higher than any existing
1694  * value in the index.
1695  *
1696  * We don't require special handling for these cases -- we don't need to
1697  * be explicitly instructed to _not_ perform another primitive index scan.
1698  * It's up to code under the control of _bt_first to always set the flag
1699  * when another primitive index scan will be required.
1700  *
1701  * This works correctly, even with the tricky cases listed above, which
1702  * all involve access to leaf pages "near the boundaries of the key space"
1703  * (whether it's from a leftmost/rightmost page, or an imaginary empty
1704  * leaf root page). If _bt_checkkeys cannot be reached by a primitive
1705  * index scan for one set of array keys, then it also won't be reached for
1706  * any later set ("later" in terms of the direction that we scan the index
1707  * and advance the arrays). The array keys won't have advanced in these
1708  * cases, but that's the correct behavior (even _bt_advance_array_keys
1709  * won't always advance the arrays at the point they become "exhausted").
1710  */
1711  if (so->needPrimScan)
1712  {
1713  Assert(_bt_verify_arrays_bt_first(scan, dir));
1714 
1715  /*
1716  * Flag was set -- must call _bt_first again, which will reset the
1717  * scan's needPrimScan flag
1718  */
1719  return true;
1720  }
1721 
1722  /* The top-level index scan ran out of tuples in this scan direction */
1723  if (scan->parallel_scan != NULL)
1724  _bt_parallel_done(scan);
1725 
1726  return false;
1727 }
void _bt_parallel_done(IndexScanDesc scan)
Definition: nbtree.c:732

References _bt_parallel_done(), Assert, BTScanOpaqueData::needPrimScan, BTScanOpaqueData::numArrayKeys, IndexScanDescData::opaque, IndexScanDescData::parallel_scan, and BTScanOpaqueData::scanBehind.

◆ _bt_start_vacuum()

BTCycleId _bt_start_vacuum ( Relation  rel)

Definition at line 4416 of file nbtutils.c.

4417 {
4418  BTCycleId result;
4419  int i;
4420  BTOneVacInfo *vac;
4421 
4422  LWLockAcquire(BtreeVacuumLock, LW_EXCLUSIVE);
4423 
4424  /*
4425  * Assign the next cycle ID, being careful to avoid zero as well as the
4426  * reserved high values.
4427  */
4428  result = ++(btvacinfo->cycle_ctr);
4429  if (result == 0 || result > MAX_BT_CYCLE_ID)
4430  result = btvacinfo->cycle_ctr = 1;
4431 
4432  /* Let's just make sure there's no entry already for this index */
4433  for (i = 0; i < btvacinfo->num_vacuums; i++)
4434  {
4435  vac = &btvacinfo->vacuums[i];
4436  if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
4437  vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
4438  {
4439  /*
4440  * Unlike most places in the backend, we have to explicitly
4441  * release our LWLock before throwing an error. This is because
4442  * we expect _bt_end_vacuum() to be called before transaction
4443  * abort cleanup can run to release LWLocks.
4444  */
4445  LWLockRelease(BtreeVacuumLock);
4446  elog(ERROR, "multiple active vacuums for index \"%s\"",
4448  }
4449  }
4450 
4451  /* OK, add an entry */
4453  {
4454  LWLockRelease(BtreeVacuumLock);
4455  elog(ERROR, "out of btvacinfo slots");
4456  }
4458  vac->relid = rel->rd_lockInfo.lockRelId;
4459  vac->cycleid = result;
4461 
4462  LWLockRelease(BtreeVacuumLock);
4463  return result;
4464 }
#define MAX_BT_CYCLE_ID
Definition: nbtree.h:93
uint16 BTCycleId
Definition: nbtree.h:29
BTCycleId cycleid
Definition: nbtutils.c:4358
BTCycleId cycle_ctr
Definition: nbtutils.c:4363
int max_vacuums
Definition: nbtutils.c:4365

References btvacinfo, BTVacInfo::cycle_ctr, BTOneVacInfo::cycleid, LockRelId::dbId, elog, ERROR, i, LockInfoData::lockRelId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MAX_BT_CYCLE_ID, BTVacInfo::max_vacuums, BTVacInfo::num_vacuums, RelationData::rd_lockInfo, RelationGetRelationName, BTOneVacInfo::relid, LockRelId::relId, and BTVacInfo::vacuums.

Referenced by btbulkdelete().

◆ _bt_truncate()

IndexTuple _bt_truncate ( Relation  rel,
IndexTuple  lastleft,
IndexTuple  firstright,
BTScanInsert  itup_key 
)

Definition at line 4645 of file nbtutils.c.

4647 {
4648  TupleDesc itupdesc = RelationGetDescr(rel);
4650  int keepnatts;
4651  IndexTuple pivot;
4652  IndexTuple tidpivot;
4653  ItemPointer pivotheaptid;
4654  Size newsize;
4655 
4656  /*
4657  * We should only ever truncate non-pivot tuples from leaf pages. It's
4658  * never okay to truncate when splitting an internal page.
4659  */
4660  Assert(!BTreeTupleIsPivot(lastleft) && !BTreeTupleIsPivot(firstright));
4661 
4662  /* Determine how many attributes must be kept in truncated tuple */
4663  keepnatts = _bt_keep_natts(rel, lastleft, firstright, itup_key);
4664 
4665 #ifdef DEBUG_NO_TRUNCATE
4666  /* Force truncation to be ineffective for testing purposes */
4667  keepnatts = nkeyatts + 1;
4668 #endif
4669 
4670  pivot = index_truncate_tuple(itupdesc, firstright,
4671  Min(keepnatts, nkeyatts));
4672 
4673  if (BTreeTupleIsPosting(pivot))
4674  {
4675  /*
4676  * index_truncate_tuple() just returns a straight copy of firstright
4677  * when it has no attributes to truncate. When that happens, we may
4678  * need to truncate away a posting list here instead.
4679  */
4680  Assert(keepnatts == nkeyatts || keepnatts == nkeyatts + 1);
4681  Assert(IndexRelationGetNumberOfAttributes(rel) == nkeyatts);
4682  pivot->t_info &= ~INDEX_SIZE_MASK;
4683  pivot->t_info |= MAXALIGN(BTreeTupleGetPostingOffset(firstright));
4684  }
4685 
4686  /*
4687  * If there is a distinguishing key attribute within pivot tuple, we're
4688  * done
4689  */
4690  if (keepnatts <= nkeyatts)
4691  {
4692  BTreeTupleSetNAtts(pivot, keepnatts, false);
4693  return pivot;
4694  }
4695 
4696  /*
4697  * We have to store a heap TID in the new pivot tuple, since no non-TID
4698  * key attribute value in firstright distinguishes the right side of the
4699  * split from the left side. nbtree conceptualizes this case as an
4700  * inability to truncate away any key attributes, since heap TID is
4701  * treated as just another key attribute (despite lacking a pg_attribute
4702  * entry).
4703  *
4704  * Use enlarged space that holds a copy of pivot. We need the extra space
4705  * to store a heap TID at the end (using the special pivot tuple
4706  * representation). Note that the original pivot already has firstright's
4707  * possible posting list/non-key attribute values removed at this point.
4708  */
4709  newsize = MAXALIGN(IndexTupleSize(pivot)) + MAXALIGN(sizeof(ItemPointerData));
4710  tidpivot = palloc0(newsize);
4711  memcpy(tidpivot, pivot, MAXALIGN(IndexTupleSize(pivot)));
4712  /* Cannot leak memory here */
4713  pfree(pivot);
4714 
4715  /*
4716  * Store all of firstright's key attribute values plus a tiebreaker heap
4717  * TID value in enlarged pivot tuple
4718  */
4719  tidpivot->t_info &= ~INDEX_SIZE_MASK;
4720  tidpivot->t_info |= newsize;
4721  BTreeTupleSetNAtts(tidpivot, nkeyatts, true);
4722  pivotheaptid = BTreeTupleGetHeapTID(tidpivot);
4723 
4724  /*
4725  * Lehman & Yao use lastleft as the leaf high key in all cases, but don't
4726  * consider suffix truncation. It seems like a good idea to follow that
4727  * example in cases where no truncation takes place -- use lastleft's heap
4728  * TID. (This is also the closest value to negative infinity that's
4729  * legally usable.)
4730  */
4731  ItemPointerCopy(BTreeTupleGetMaxHeapTID(lastleft), pivotheaptid);
4732 
4733  /*
4734  * We're done. Assert() that heap TID invariants hold before returning.
4735  *
4736  * Lehman and Yao require that the downlink to the right page, which is to
4737  * be inserted into the parent page in the second phase of a page split be
4738  * a strict lower bound on items on the right page, and a non-strict upper
4739  * bound for items on the left page. Assert that heap TIDs follow these
4740  * invariants, since a heap TID value is apparently needed as a
4741  * tiebreaker.
4742  */
4743 #ifndef DEBUG_NO_TRUNCATE
4745  BTreeTupleGetHeapTID(firstright)) < 0);
4746  Assert(ItemPointerCompare(pivotheaptid,
4747  BTreeTupleGetHeapTID(lastleft)) >= 0);
4748  Assert(ItemPointerCompare(pivotheaptid,
4749  BTreeTupleGetHeapTID(firstright)) < 0);
4750 #else
4751 
4752  /*
4753  * Those invariants aren't guaranteed to hold for lastleft + firstright
4754  * heap TID attribute values when they're considered here only because
4755  * DEBUG_NO_TRUNCATE is defined (a heap TID is probably not actually
4756  * needed as a tiebreaker). DEBUG_NO_TRUNCATE must therefore use a heap
4757  * TID value that always works as a strict lower bound for items to the
4758  * right. In particular, it must avoid using firstright's leading key
4759  * attribute values along with lastleft's heap TID value when lastleft's
4760  * TID happens to be greater than firstright's TID.
4761  */
4762  ItemPointerCopy(BTreeTupleGetHeapTID(firstright), pivotheaptid);
4763 
4764  /*
4765  * Pivot heap TID should never be fully equal to firstright. Note that
4766  * the pivot heap TID will still end up equal to lastleft's heap TID when
4767  * that's the only usable value.
4768  */
4769  ItemPointerSetOffsetNumber(pivotheaptid,
4771  Assert(ItemPointerCompare(pivotheaptid,
4772  BTreeTupleGetHeapTID(firstright)) < 0);
4773 #endif
4774 
4775  return tidpivot;
4776 }
IndexTuple index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source, int leavenatts)
Definition: indextuple.c:576
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition: itemptr.h:158
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
#define INDEX_SIZE_MASK
Definition: itup.h:65
void * palloc0(Size size)
Definition: mcxt.c:1347
static uint32 BTreeTupleGetPostingOffset(IndexTuple posting)
Definition: nbtree.h:529
static ItemPointer BTreeTupleGetMaxHeapTID(IndexTuple itup)
Definition: nbtree.h:664
static void BTreeTupleSetNAtts(IndexTuple itup, uint16 nkeyatts, bool heaptid)
Definition: nbtree.h:595
static int _bt_keep_natts(Relation rel, IndexTuple lastleft, IndexTuple firstright, BTScanInsert itup_key)
Definition: nbtutils.c:4790
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
unsigned short t_info
Definition: itup.h:49

References _bt_keep_natts(), Assert, BTreeTupleGetHeapTID(), BTreeTupleGetMaxHeapTID(), BTreeTupleGetPostingOffset(), BTreeTupleIsPivot(), BTreeTupleIsPosting(), BTreeTupleSetNAtts(), INDEX_SIZE_MASK, index_truncate_tuple(), IndexRelationGetNumberOfAttributes, IndexRelationGetNumberOfKeyAttributes, IndexTupleSize, ItemPointerCompare(), ItemPointerCopy(), ItemPointerGetOffsetNumber(), ItemPointerSetOffsetNumber(), MAXALIGN, Min, OffsetNumberPrev, palloc0(), pfree(), RelationGetDescr, and IndexTupleData::t_info.

Referenced by _bt_buildadd(), and _bt_split().

◆ _bt_tuple_before_array_skeys()

static bool _bt_tuple_before_array_skeys ( IndexScanDesc  scan,
ScanDirection  dir,
IndexTuple  tuple,
TupleDesc  tupdesc,
int  tupnatts,
bool  readpagetup,
int  sktrig,
bool scanBehind 
)
static

Definition at line 1544 of file nbtutils.c.

1547 {
1548  BTScanOpaque so = (BTScanOpaque) scan->opaque;
1549 
1550  Assert(so->numArrayKeys);
1551  Assert(so->numberOfKeys);
1552  Assert(sktrig == 0 || readpagetup);
1553  Assert(!readpagetup || scanBehind == NULL);
1554 
1555  if (scanBehind)
1556  *scanBehind = false;
1557 
1558  for (int ikey = sktrig; ikey < so->numberOfKeys; ikey++)
1559  {
1560  ScanKey cur = so->keyData + ikey;
1561  Datum tupdatum;
1562  bool tupnull;
1563  int32 result;
1564 
1565  /* readpagetup calls require one ORDER proc comparison (at most) */
1566  Assert(!readpagetup || ikey == sktrig);
1567 
1568  /*
1569  * Once we reach a non-required scan key, we're completely done.
1570  *
1571  * Note: we deliberately don't consider the scan direction here.
1572  * _bt_advance_array_keys caller requires that we track *scanBehind
1573  * without concern for scan direction.
1574  */
1575  if ((cur->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) == 0)
1576  {
1577  Assert(!readpagetup);
1578  Assert(ikey > sktrig || ikey == 0);
1579  return false;
1580  }
1581 
1582  if (cur->sk_attno > tupnatts)
1583  {
1584  Assert(!readpagetup);
1585 
1586  /*
1587  * When we reach a high key's truncated attribute, assume that the
1588  * tuple attribute's value is >= the scan's equality constraint
1589  * scan keys (but set *scanBehind to let interested callers know
1590  * that a truncated attribute might have affected our answer).
1591  */
1592  if (scanBehind)
1593  *scanBehind = true;
1594 
1595  return false;
1596  }
1597 
1598  /*
1599  * Deal with inequality strategy scan keys that _bt_check_compare set
1600  * continuescan=false for
1601  */
1602  if (cur->sk_strategy != BTEqualStrategyNumber)
1603  {
1604  /*
1605  * When _bt_check_compare indicated that a required inequality
1606  * scan key wasn't satisfied, there's no need to verify anything;
1607  * caller always calls _bt_advance_array_keys with this sktrig.
1608  */
1609  if (readpagetup)
1610  return false;
1611 
1612  /*
1613  * Otherwise we can't give up, since we must check all required
1614  * scan keys (required in either direction) in order to correctly
1615  * track *scanBehind for caller
1616  */
1617  continue;
1618  }
1619 
1620  tupdatum = index_getattr(tuple, cur->sk_attno, tupdesc, &tupnull);
1621 
1622  result = _bt_compare_array_skey(&so->orderProcs[ikey],
1623  tupdatum, tupnull,
1624  cur->sk_argument, cur);
1625 
1626  /*
1627  * Does this comparison indicate that caller must _not_ advance the
1628  * scan's arrays just yet?
1629  */
1630  if ((ScanDirectionIsForward(dir) && result < 0) ||
1631  (ScanDirectionIsBackward(dir) && result > 0))
1632  return true;
1633 
1634  /*
1635  * Does this comparison indicate that caller should now advance the
1636  * scan's arrays? (Must be if we get here during a readpagetup call.)
1637  */
1638  if (readpagetup || result != 0)
1639  {
1640  Assert(result != 0);
1641  return false;
1642  }
1643 
1644  /*
1645  * Inconclusive -- need to check later scan keys, too.
1646  *
1647  * This must be a finaltup precheck, or a call made from an assertion.
1648  */
1649  Assert(result == 0);
1650  }
1651 
1652  Assert(!readpagetup);
1653 
1654  return false;
1655 }

References _bt_compare_array_skey(), Assert, BTEqualStrategyNumber, cur, index_getattr(), BTScanOpaqueData::keyData, BTScanOpaqueData::numArrayKeys, BTScanOpaqueData::numberOfKeys, IndexScanDescData::opaque, BTScanOpaqueData::orderProcs, ScanDirectionIsBackward, ScanDirectionIsForward, SK_BT_REQBKWD, and SK_BT_REQFWD.

Referenced by _bt_advance_array_keys(), _bt_checkkeys(), and _bt_checkkeys_look_ahead().

◆ _bt_vacuum_cycleid()

BTCycleId _bt_vacuum_cycleid ( Relation  rel)

Definition at line 4382 of file nbtutils.c.

4383 {
4384  BTCycleId result = 0;
4385  int i;
4386 
4387  /* Share lock is enough since this is a read-only operation */
4388  LWLockAcquire(BtreeVacuumLock, LW_SHARED);
4389 
4390  for (i = 0; i < btvacinfo->num_vacuums; i++)
4391  {
4392  BTOneVacInfo *vac = &btvacinfo->vacuums[i];
4393 
4394  if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
4395  vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
4396  {
4397  result = vac->cycleid;
4398  break;
4399  }
4400  }
4401 
4402  LWLockRelease(BtreeVacuumLock);
4403  return result;
4404 }
@ LW_SHARED
Definition: lwlock.h:115

References btvacinfo, BTOneVacInfo::cycleid, LockRelId::dbId, i, LockInfoData::lockRelId, LW_SHARED, LWLockAcquire(), LWLockRelease(), BTVacInfo::num_vacuums, RelationData::rd_lockInfo, BTOneVacInfo::relid, LockRelId::relId, and BTVacInfo::vacuums.

Referenced by _bt_split().

◆ btbuildphasename()

char* btbuildphasename ( int64  phasenum)

Definition at line 4597 of file nbtutils.c.

4598 {
4599  switch (phasenum)
4600  {
4602  return "initializing";
4604  return "scanning table";
4606  return "sorting live tuples";
4608  return "sorting dead tuples";
4610  return "loading tuples in tree";
4611  default:
4612  return NULL;
4613  }
4614 }
#define PROGRESS_BTREE_PHASE_PERFORMSORT_2
Definition: nbtree.h:1159
#define PROGRESS_BTREE_PHASE_LEAF_LOAD
Definition: nbtree.h:1160
#define PROGRESS_BTREE_PHASE_INDEXBUILD_TABLESCAN
Definition: nbtree.h:1157
#define PROGRESS_BTREE_PHASE_PERFORMSORT_1
Definition: nbtree.h:1158
#define PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE
Definition: progress.h:107

References PROGRESS_BTREE_PHASE_INDEXBUILD_TABLESCAN, PROGRESS_BTREE_PHASE_LEAF_LOAD, PROGRESS_BTREE_PHASE_PERFORMSORT_1, PROGRESS_BTREE_PHASE_PERFORMSORT_2, and PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE.

Referenced by bthandler().

◆ btoptions()

bytea* btoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 4551 of file nbtutils.c.

4552 {
4553  static const relopt_parse_elt tab[] = {
4554  {"fillfactor", RELOPT_TYPE_INT, offsetof(BTOptions, fillfactor)},
4555  {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
4556  offsetof(BTOptions, vacuum_cleanup_index_scale_factor)},
4557  {"deduplicate_items", RELOPT_TYPE_BOOL,
4558  offsetof(BTOptions, deduplicate_items)}
4559  };
4560 
4561  return (bytea *) build_reloptions(reloptions, validate,
4563  sizeof(BTOptions),
4564  tab, lengthof(tab));
4565 }
#define lengthof(array)
Definition: c.h:788
static int fillfactor
Definition: pgbench.c:187
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1908
@ RELOPT_KIND_BTREE
Definition: reloptions.h:44
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
@ RELOPT_TYPE_BOOL
Definition: reloptions.h:31
@ RELOPT_TYPE_REAL
Definition: reloptions.h:33
Definition: c.h:687

References build_reloptions(), fillfactor, lengthof, RELOPT_KIND_BTREE, RELOPT_TYPE_BOOL, RELOPT_TYPE_INT, and RELOPT_TYPE_REAL.

Referenced by bthandler().

◆ btproperty()

bool btproperty ( Oid  index_oid,
int  attno,
IndexAMProperty  prop,
const char *  propname,
bool res,
bool isnull 
)

Definition at line 4574 of file nbtutils.c.

4577 {
4578  switch (prop)
4579  {
4580  case AMPROP_RETURNABLE:
4581  /* answer only for columns, not AM or whole index */
4582  if (attno == 0)
4583  return false;
4584  /* otherwise, btree can always return data */
4585  *res = true;
4586  return true;
4587 
4588  default:
4589  return false; /* punt to generic code */
4590  }
4591 }
@ AMPROP_RETURNABLE
Definition: amapi.h:43

References AMPROP_RETURNABLE, and res.

Referenced by bthandler().

◆ BTreeShmemInit()

void BTreeShmemInit ( void  )

Definition at line 4523 of file nbtutils.c.

4524 {
4525  bool found;
4526 
4527  btvacinfo = (BTVacInfo *) ShmemInitStruct("BTree Vacuum State",
4528  BTreeShmemSize(),
4529  &found);
4530 
4531  if (!IsUnderPostmaster)
4532  {
4533  /* Initialize shared memory area */
4534  Assert(!found);
4535 
4536  /*
4537  * It doesn't really matter what the cycle counter starts at, but
4538  * having it always start the same doesn't seem good. Seed with
4539  * low-order bits of time() instead.
4540  */
4541  btvacinfo->cycle_ctr = (BTCycleId) time(NULL);
4542 
4543  btvacinfo->num_vacuums = 0;
4545  }
4546  else
4547  Assert(found);
4548 }
bool IsUnderPostmaster
Definition: globals.c:118
int MaxBackends
Definition: globals.c:144
Size BTreeShmemSize(void)
Definition: nbtutils.c:4510
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:387

References Assert, BTreeShmemSize(), btvacinfo, BTVacInfo::cycle_ctr, IsUnderPostmaster, BTVacInfo::max_vacuums, MaxBackends, BTVacInfo::num_vacuums, and ShmemInitStruct().

Referenced by CreateOrAttachShmemStructs().

◆ BTreeShmemSize()

Size BTreeShmemSize ( void  )

Definition at line 4510 of file nbtutils.c.

4511 {
4512  Size size;
4513 
4514  size = offsetof(BTVacInfo, vacuums);
4516  return size;
4517 }
Size add_size(Size s1, Size s2)
Definition: shmem.c:493
Size mul_size(Size s1, Size s2)
Definition: shmem.c:510
static pg_noinline void Size size
Definition: slab.c:607

References add_size(), MaxBackends, mul_size(), and size.

Referenced by BTreeShmemInit(), and CalculateShmemSize().

Variable Documentation

◆ btvacinfo

BTVacInfo* btvacinfo
static

Definition at line 4369 of file nbtutils.c.

Referenced by _bt_end_vacuum(), _bt_start_vacuum(), _bt_vacuum_cycleid(), and BTreeShmemInit().