You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brin_minmax.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. * brin_minmax.c
  3. * Implementation of Min/Max opclass for BRIN
  4. *
  5. * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  6. * Portions Copyright (c) 1994, Regents of the University of California
  7. *
  8. * IDENTIFICATION
  9. * src/backend/access/brin/brin_minmax.c
  10. */
  11. #include "postgres.h"
  12. #include "access/genam.h"
  13. #include "access/brin_internal.h"
  14. #include "access/brin_tuple.h"
  15. #include "access/stratnum.h"
  16. #include "catalog/pg_type.h"
  17. #include "catalog/pg_amop.h"
  18. #include "utils/builtins.h"
  19. #include "utils/datum.h"
  20. #include "utils/lsyscache.h"
  21. #include "utils/rel.h"
  22. #include "utils/syscache.h"
  23. typedef struct MinmaxOpaque
  24. {
  25. Oid cached_subtype;
  26. FmgrInfo strategy_procinfos[BTMaxStrategyNumber];
  27. } MinmaxOpaque;
  28. static FmgrInfo *minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
  29. Oid subtype, uint16 strategynum);
  30. Datum
  31. brin_minmax_opcinfo(PG_FUNCTION_ARGS)
  32. {
  33. Oid typoid = PG_GETARG_OID(0);
  34. BrinOpcInfo *result;
  35. /*
  36. * opaque->strategy_procinfos is initialized lazily; here it is set to
  37. * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
  38. */
  39. result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
  40. sizeof(MinmaxOpaque));
  41. result->oi_nstored = 2;
  42. result->oi_opaque = (MinmaxOpaque *)
  43. MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
  44. result->oi_typcache[0] = result->oi_typcache[1] =
  45. lookup_type_cache(typoid, 0);
  46. PG_RETURN_POINTER(result);
  47. }
  48. /*
  49. * Examine the given index tuple (which contains partial status of a certain
  50. * page range) by comparing it to the given value that comes from another heap
  51. * tuple. If the new value is outside the min/max range specified by the
  52. * existing tuple values, update the index tuple and return true. Otherwise,
  53. * return false and do not modify in this case.
  54. */
  55. Datum
  56. brin_minmax_add_value(PG_FUNCTION_ARGS)
  57. {
  58. BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
  59. BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
  60. Datum newval = PG_GETARG_DATUM(2);
  61. bool isnull = PG_GETARG_DATUM(3);
  62. Oid colloid = PG_GET_COLLATION();
  63. FmgrInfo *cmpFn;
  64. Datum compar;
  65. bool updated = false;
  66. Form_pg_attribute attr;
  67. AttrNumber attno;
  68. /*
  69. * If the new value is null, we record that we saw it if it's the first
  70. * one; otherwise, there's nothing to do.
  71. */
  72. if (isnull)
  73. {
  74. if (column->bv_hasnulls)
  75. PG_RETURN_BOOL(false);
  76. column->bv_hasnulls = true;
  77. PG_RETURN_BOOL(true);
  78. }
  79. attno = column->bv_attno;
  80. attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
  81. /*
  82. * If the recorded value is null, store the new value (which we know to be
  83. * not null) as both minimum and maximum, and we're done.
  84. */
  85. if (column->bv_allnulls)
  86. {
  87. column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
  88. column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
  89. column->bv_allnulls = false;
  90. PG_RETURN_BOOL(true);
  91. }
  92. /*
  93. * Otherwise, need to compare the new value with the existing boundaries
  94. * and update them accordingly. First check if it's less than the
  95. * existing minimum.
  96. */
  97. cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
  98. BTLessStrategyNumber);
  99. compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
  100. if (DatumGetBool(compar))
  101. {
  102. if (!attr->attbyval)
  103. pfree(DatumGetPointer(column->bv_values[0]));
  104. column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
  105. updated = true;
  106. }
  107. /*
  108. * And now compare it to the existing maximum.
  109. */
  110. cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
  111. BTGreaterStrategyNumber);
  112. compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
  113. if (DatumGetBool(compar))
  114. {
  115. if (!attr->attbyval)
  116. pfree(DatumGetPointer(column->bv_values[1]));
  117. column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
  118. updated = true;
  119. }
  120. PG_RETURN_BOOL(updated);
  121. }
  122. /*
  123. * Given an index tuple corresponding to a certain page range and a scan key,
  124. * return whether the scan key is consistent with the index tuple's min/max
  125. * values. Return true if so, false otherwise.
  126. */
  127. Datum
  128. brin_minmax_consistent(PG_FUNCTION_ARGS)
  129. {
  130. BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
  131. BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
  132. ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
  133. Oid colloid = PG_GET_COLLATION(),
  134. subtype;
  135. AttrNumber attno;
  136. Datum value;
  137. Datum matches;
  138. FmgrInfo *finfo;
  139. Assert(key->sk_attno == column->bv_attno);
  140. /* handle IS NULL/IS NOT NULL tests */
  141. if (key->sk_flags & SK_ISNULL)
  142. {
  143. if (key->sk_flags & SK_SEARCHNULL)
  144. {
  145. if (column->bv_allnulls || column->bv_hasnulls)
  146. PG_RETURN_BOOL(true);
  147. PG_RETURN_BOOL(false);
  148. }
  149. /*
  150. * For IS NOT NULL, we can only skip ranges that are known to have
  151. * only nulls.
  152. */
  153. if (key->sk_flags & SK_SEARCHNOTNULL)
  154. PG_RETURN_BOOL(!column->bv_allnulls);
  155. /*
  156. * Neither IS NULL nor IS NOT NULL was used; assume all indexable
  157. * operators are strict and return false.
  158. */
  159. PG_RETURN_BOOL(false);
  160. }
  161. /* if the range is all empty, it cannot possibly be consistent */
  162. if (column->bv_allnulls)
  163. PG_RETURN_BOOL(false);
  164. attno = key->sk_attno;
  165. subtype = key->sk_subtype;
  166. value = key->sk_argument;
  167. switch (key->sk_strategy)
  168. {
  169. case BTLessStrategyNumber:
  170. case BTLessEqualStrategyNumber:
  171. finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
  172. key->sk_strategy);
  173. matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
  174. value);
  175. break;
  176. case BTEqualStrategyNumber:
  177. /*
  178. * In the equality case (WHERE col = someval), we want to return
  179. * the current page range if the minimum value in the range <=
  180. * scan key, and the maximum value >= scan key.
  181. */
  182. finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
  183. BTLessEqualStrategyNumber);
  184. matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
  185. value);
  186. if (!DatumGetBool(matches))
  187. break;
  188. /* max() >= scankey */
  189. finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
  190. BTGreaterEqualStrategyNumber);
  191. matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
  192. value);
  193. break;
  194. case BTGreaterEqualStrategyNumber:
  195. case BTGreaterStrategyNumber:
  196. finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
  197. key->sk_strategy);
  198. matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
  199. value);
  200. break;
  201. default:
  202. /* shouldn't happen */
  203. elog(ERROR, "invalid strategy number %d", key->sk_strategy);
  204. matches = 0;
  205. break;
  206. }
  207. PG_RETURN_DATUM(matches);
  208. }
  209. /*
  210. * Given two BrinValues, update the first of them as a union of the summary
  211. * values contained in both. The second one is untouched.
  212. */
  213. Datum
  214. brin_minmax_union(PG_FUNCTION_ARGS)
  215. {
  216. BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
  217. BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
  218. BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
  219. Oid colloid = PG_GET_COLLATION();
  220. AttrNumber attno;
  221. Form_pg_attribute attr;
  222. FmgrInfo *finfo;
  223. bool needsadj;
  224. Assert(col_a->bv_attno == col_b->bv_attno);
  225. /* Adjust "hasnulls" */
  226. if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
  227. col_a->bv_hasnulls = true;
  228. /* If there are no values in B, there's nothing left to do */
  229. if (col_b->bv_allnulls)
  230. PG_RETURN_VOID();
  231. attno = col_a->bv_attno;
  232. attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
  233. /*
  234. * Adjust "allnulls". If A doesn't have values, just copy the values from
  235. * B into A, and we're done. We cannot run the operators in this case,
  236. * because values in A might contain garbage. Note we already established
  237. * that B contains values.
  238. */
  239. if (col_a->bv_allnulls)
  240. {
  241. col_a->bv_allnulls = false;
  242. col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
  243. attr->attbyval, attr->attlen);
  244. col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
  245. attr->attbyval, attr->attlen);
  246. PG_RETURN_VOID();
  247. }
  248. /* Adjust minimum, if B's min is less than A's min */
  249. finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
  250. BTLessStrategyNumber);
  251. needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
  252. col_a->bv_values[0]);
  253. if (needsadj)
  254. {
  255. if (!attr->attbyval)
  256. pfree(DatumGetPointer(col_a->bv_values[0]));
  257. col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
  258. attr->attbyval, attr->attlen);
  259. }
  260. /* Adjust maximum, if B's max is greater than A's max */
  261. finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
  262. BTGreaterStrategyNumber);
  263. needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
  264. col_a->bv_values[1]);
  265. if (needsadj)
  266. {
  267. if (!attr->attbyval)
  268. pfree(DatumGetPointer(col_a->bv_values[1]));
  269. col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
  270. attr->attbyval, attr->attlen);
  271. }
  272. PG_RETURN_VOID();
  273. }
  274. /*
  275. * Cache and return the procedure for the given strategy.
  276. *
  277. * Note: this function mirrors inclusion_get_strategy_procinfo; see notes
  278. * there. If changes are made here, see that function too.
  279. */
  280. static FmgrInfo *
  281. minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
  282. uint16 strategynum)
  283. {
  284. MinmaxOpaque *opaque;
  285. Assert(strategynum >= 1 &&
  286. strategynum <= BTMaxStrategyNumber);
  287. opaque = (MinmaxOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
  288. /*
  289. * We cache the procedures for the previous subtype in the opaque struct,
  290. * to avoid repetitive syscache lookups. If the subtype changed,
  291. * invalidate all the cached entries.
  292. */
  293. if (opaque->cached_subtype != subtype)
  294. {
  295. uint16 i;
  296. for (i = 1; i <= BTMaxStrategyNumber; i++)
  297. opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
  298. opaque->cached_subtype = subtype;
  299. }
  300. if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
  301. {
  302. Form_pg_attribute attr;
  303. HeapTuple tuple;
  304. Oid opfamily,
  305. oprid;
  306. bool isNull;
  307. opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
  308. attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
  309. tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
  310. ObjectIdGetDatum(attr->atttypid),
  311. ObjectIdGetDatum(subtype),
  312. Int16GetDatum(strategynum));
  313. if (!HeapTupleIsValid(tuple))
  314. elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
  315. strategynum, attr->atttypid, subtype, opfamily);
  316. oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
  317. Anum_pg_amop_amopopr, &isNull));
  318. ReleaseSysCache(tuple);
  319. Assert(!isNull && RegProcedureIsValid(oprid));
  320. fmgr_info_cxt(get_opcode(oprid),
  321. &opaque->strategy_procinfos[strategynum - 1],
  322. bdesc->bd_context);
  323. }
  324. return &opaque->strategy_procinfos[strategynum - 1];
  325. }