@@ -266,7 +266,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
266266
267267 <para>
268268 There are five methods that an index operator class for
269- <acronym>GiST</acronym> must provide, and seven that are optional.
269+ <acronym>GiST</acronym> must provide, and eight that are optional.
270270 Correctness of the index is ensured
271271 by proper implementation of the <function>same</function>, <function>consistent</function>
272272 and <function>union</function> methods, while efficiency (size and speed) of the
@@ -294,6 +294,9 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
294294 <filename>src/include/nodes/primnodes.h</filename>) into strategy numbers
295295 used by the operator class. This lets the core code look up operators for
296296 temporal constraint indexes.
297+ The optional thirteenth method <function>without_portion</function> is used by
298+ <literal>RESTRICT</literal> foreign keys to compute the portion of history
299+ that was lost.
297300 </para>
298301
299302 <variablelist>
@@ -1241,6 +1244,108 @@ my_stratnum(PG_FUNCTION_ARGS)
12411244 </para>
12421245 </listitem>
12431246 </varlistentry>
1247+ <varlistentry>
1248+ <term><function>without_portion</function></term>
1249+ <listitem>
1250+ <para>
1251+ Given two values of this opclass, it subtracts the second for the first
1252+ and returns an array of the results.
1253+ </para>
1254+ <para>
1255+ This is used by temporal foreign keys to compute the part
1256+ of history that was lost by an update.
1257+ </para>
1258+
1259+ <para>
1260+ The <acronym>SQL</acronym> declaration of the function must look like
1261+ this (using <literal>my_range_without_portion</literal> as an example):
1262+
1263+ <programlisting>
1264+ CREATE OR REPLACE FUNCTION my_range_without_portion(anyrange, anyrange)
1265+ RETURNS anyarray
1266+ AS 'MODULE_PATHNAME'
1267+ LANGUAGE C STRICT;
1268+ </programlisting>
1269+ </para>
1270+
1271+ <para>
1272+ The matching code in the C module could then follow this example:
1273+
1274+ <programlisting>
1275+ Datum
1276+ my_range_without_portion(PG_FUNCTION_ARGS)
1277+ {
1278+ typedef struct {
1279+ RangeType *rs[2];
1280+ int n;
1281+ } range_without_portion_fctx;
1282+
1283+ FuncCallContext *funcctx;
1284+ range_without_portion_fctx *fctx;
1285+ MemoryContext oldcontext;
1286+
1287+ /* stuff done only on the first call of the function */
1288+ if (SRF_IS_FIRSTCALL())
1289+ {
1290+ RangeType *r1;
1291+ RangeType *r2;
1292+ Oid rngtypid;
1293+ TypeCacheEntry *typcache;
1294+
1295+ /* create a function context for cross-call persistence */
1296+ funcctx = SRF_FIRSTCALL_INIT();
1297+
1298+ /*
1299+ * switch to memory context appropriate for multiple function calls
1300+ */
1301+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1302+
1303+ r1 = PG_GETARG_RANGE_P(0);
1304+ r2 = PG_GETARG_RANGE_P(1);
1305+
1306+ /* Different types should be prevented by ANYRANGE matching rules */
1307+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
1308+ elog(ERROR, "range types do not match");
1309+
1310+ /* allocate memory for user context */
1311+ fctx = (range_without_portion_fctx *) palloc(sizeof(range_without_portion_fctx));
1312+
1313+ /*
1314+ * Initialize state.
1315+ * We can't store the range typcache in fn_extra because the caller
1316+ * uses that for the SRF state.
1317+ */
1318+ rngtypid = RangeTypeGetOid(r1);
1319+ typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
1320+ if (typcache->rngelemtype == NULL)
1321+ elog(ERROR, "type %u is not a range type", rngtypid);
1322+ range_without_portion_internal(typcache, r1, r2, fctx->rs, &fctx->n);
1323+
1324+ funcctx->user_fctx = fctx;
1325+ MemoryContextSwitchTo(oldcontext);
1326+ }
1327+
1328+ /* stuff done on every call of the function */
1329+ funcctx = SRF_PERCALL_SETUP();
1330+ fctx = funcctx->user_fctx;
1331+
1332+ if (funcctx->call_cntr < fctx->n)
1333+ {
1334+ /*
1335+ * We must keep these on separate lines
1336+ * because SRF_RETURN_NEXT does call_cntr++:
1337+ */
1338+ RangeType *ret = fctx->rs[funcctx->call_cntr];
1339+ SRF_RETURN_NEXT(funcctx, RangeTypePGetDatum(ret));
1340+ }
1341+ else
1342+ /* do when there is no more left */
1343+ SRF_RETURN_DONE(funcctx);
1344+ }
1345+ </programlisting>
1346+ </para>
1347+ </listitem>
1348+ </varlistentry>
12441349 </variablelist>
12451350
12461351 <para>
0 commit comments