1 | /*------------------------------------------------------------------------ |
2 | * |
3 | * regress.c |
4 | * Code for various C-language functions defined as part of the |
5 | * regression tests. |
6 | * |
7 | * This code is released under the terms of the PostgreSQL License. |
8 | * |
9 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
10 | * Portions Copyright (c) 1994, Regents of the University of California |
11 | * |
12 | * src/test/regress/regress.c |
13 | * |
14 | *------------------------------------------------------------------------- |
15 | */ |
16 | |
17 | #include "postgres.h" |
18 | |
19 | #include <math.h> |
20 | #include <signal.h> |
21 | |
22 | #include "access/htup_details.h" |
23 | #include "access/transam.h" |
24 | #include "access/tuptoaster.h" |
25 | #include "access/xact.h" |
26 | #include "catalog/pg_operator.h" |
27 | #include "catalog/pg_type.h" |
28 | #include "commands/sequence.h" |
29 | #include "commands/trigger.h" |
30 | #include "executor/executor.h" |
31 | #include "executor/spi.h" |
32 | #include "miscadmin.h" |
33 | #include "nodes/supportnodes.h" |
34 | #include "optimizer/optimizer.h" |
35 | #include "optimizer/plancat.h" |
36 | #include "port/atomics.h" |
37 | #include "utils/builtins.h" |
38 | #include "utils/geo_decls.h" |
39 | #include "utils/rel.h" |
40 | #include "utils/typcache.h" |
41 | #include "utils/memutils.h" |
42 | |
43 | |
44 | #define LDELIM '(' |
45 | #define RDELIM ')' |
46 | #define DELIM ',' |
47 | |
48 | static void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2); |
49 | |
50 | PG_MODULE_MAGIC; |
51 | |
52 | |
53 | /* return the point where two paths intersect, or NULL if no intersection. */ |
54 | PG_FUNCTION_INFO_V1(interpt_pp); |
55 | |
56 | Datum |
57 | interpt_pp(PG_FUNCTION_ARGS) |
58 | { |
59 | PATH *p1 = PG_GETARG_PATH_P(0); |
60 | PATH *p2 = PG_GETARG_PATH_P(1); |
61 | int i, |
62 | j; |
63 | LSEG seg1, |
64 | seg2; |
65 | bool found; /* We've found the intersection */ |
66 | |
67 | found = false; /* Haven't found it yet */ |
68 | |
69 | for (i = 0; i < p1->npts - 1 && !found; i++) |
70 | { |
71 | regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]); |
72 | for (j = 0; j < p2->npts - 1 && !found; j++) |
73 | { |
74 | regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]); |
75 | if (DatumGetBool(DirectFunctionCall2(lseg_intersect, |
76 | LsegPGetDatum(&seg1), |
77 | LsegPGetDatum(&seg2)))) |
78 | found = true; |
79 | } |
80 | } |
81 | |
82 | if (!found) |
83 | PG_RETURN_NULL(); |
84 | |
85 | /* |
86 | * Note: DirectFunctionCall2 will kick out an error if lseg_interpt() |
87 | * returns NULL, but that should be impossible since we know the two |
88 | * segments intersect. |
89 | */ |
90 | PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt, |
91 | LsegPGetDatum(&seg1), |
92 | LsegPGetDatum(&seg2))); |
93 | } |
94 | |
95 | |
96 | /* like lseg_construct, but assume space already allocated */ |
97 | static void |
98 | regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2) |
99 | { |
100 | lseg->p[0].x = pt1->x; |
101 | lseg->p[0].y = pt1->y; |
102 | lseg->p[1].x = pt2->x; |
103 | lseg->p[1].y = pt2->y; |
104 | } |
105 | |
106 | PG_FUNCTION_INFO_V1(overpaid); |
107 | |
108 | Datum |
109 | overpaid(PG_FUNCTION_ARGS) |
110 | { |
111 | HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0); |
112 | bool isnull; |
113 | int32 salary; |
114 | |
115 | salary = DatumGetInt32(GetAttributeByName(tuple, "salary" , &isnull)); |
116 | if (isnull) |
117 | PG_RETURN_NULL(); |
118 | PG_RETURN_BOOL(salary > 699); |
119 | } |
120 | |
121 | /* New type "widget" |
122 | * This used to be "circle", but I added circle to builtins, |
123 | * so needed to make sure the names do not collide. - tgl 97/04/21 |
124 | */ |
125 | |
126 | typedef struct |
127 | { |
128 | Point center; |
129 | double radius; |
130 | } WIDGET; |
131 | |
132 | PG_FUNCTION_INFO_V1(widget_in); |
133 | PG_FUNCTION_INFO_V1(widget_out); |
134 | |
135 | #define NARGS 3 |
136 | |
137 | Datum |
138 | widget_in(PG_FUNCTION_ARGS) |
139 | { |
140 | char *str = PG_GETARG_CSTRING(0); |
141 | char *p, |
142 | *coord[NARGS]; |
143 | int i; |
144 | WIDGET *result; |
145 | |
146 | for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++) |
147 | { |
148 | if (*p == DELIM || (*p == LDELIM && i == 0)) |
149 | coord[i++] = p + 1; |
150 | } |
151 | |
152 | if (i < NARGS) |
153 | ereport(ERROR, |
154 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
155 | errmsg("invalid input syntax for type %s: \"%s\"" , |
156 | "widget" , str))); |
157 | |
158 | result = (WIDGET *) palloc(sizeof(WIDGET)); |
159 | result->center.x = atof(coord[0]); |
160 | result->center.y = atof(coord[1]); |
161 | result->radius = atof(coord[2]); |
162 | |
163 | PG_RETURN_POINTER(result); |
164 | } |
165 | |
166 | Datum |
167 | widget_out(PG_FUNCTION_ARGS) |
168 | { |
169 | WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(0); |
170 | char *str = psprintf("(%g,%g,%g)" , |
171 | widget->center.x, widget->center.y, widget->radius); |
172 | |
173 | PG_RETURN_CSTRING(str); |
174 | } |
175 | |
176 | PG_FUNCTION_INFO_V1(pt_in_widget); |
177 | |
178 | Datum |
179 | pt_in_widget(PG_FUNCTION_ARGS) |
180 | { |
181 | Point *point = PG_GETARG_POINT_P(0); |
182 | WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1); |
183 | float8 distance; |
184 | |
185 | distance = DatumGetFloat8(DirectFunctionCall2(point_distance, |
186 | PointPGetDatum(point), |
187 | PointPGetDatum(&widget->center))); |
188 | |
189 | PG_RETURN_BOOL(distance < widget->radius); |
190 | } |
191 | |
192 | PG_FUNCTION_INFO_V1(reverse_name); |
193 | |
194 | Datum |
195 | reverse_name(PG_FUNCTION_ARGS) |
196 | { |
197 | char *string = PG_GETARG_CSTRING(0); |
198 | int i; |
199 | int len; |
200 | char *new_string; |
201 | |
202 | new_string = palloc0(NAMEDATALEN); |
203 | for (i = 0; i < NAMEDATALEN && string[i]; ++i) |
204 | ; |
205 | if (i == NAMEDATALEN || !string[i]) |
206 | --i; |
207 | len = i; |
208 | for (; i >= 0; --i) |
209 | new_string[len - i] = string[i]; |
210 | PG_RETURN_CSTRING(new_string); |
211 | } |
212 | |
213 | PG_FUNCTION_INFO_V1(trigger_return_old); |
214 | |
215 | Datum |
216 | trigger_return_old(PG_FUNCTION_ARGS) |
217 | { |
218 | TriggerData *trigdata = (TriggerData *) fcinfo->context; |
219 | HeapTuple tuple; |
220 | |
221 | if (!CALLED_AS_TRIGGER(fcinfo)) |
222 | elog(ERROR, "trigger_return_old: not fired by trigger manager" ); |
223 | |
224 | tuple = trigdata->tg_trigtuple; |
225 | |
226 | return PointerGetDatum(tuple); |
227 | } |
228 | |
229 | #define TTDUMMY_INFINITY 999999 |
230 | |
231 | static SPIPlanPtr splan = NULL; |
232 | static bool ttoff = false; |
233 | |
234 | PG_FUNCTION_INFO_V1(ttdummy); |
235 | |
236 | Datum |
237 | ttdummy(PG_FUNCTION_ARGS) |
238 | { |
239 | TriggerData *trigdata = (TriggerData *) fcinfo->context; |
240 | Trigger *trigger; /* to get trigger name */ |
241 | char **args; /* arguments */ |
242 | int attnum[2]; /* fnumbers of start/stop columns */ |
243 | Datum oldon, |
244 | oldoff; |
245 | Datum newon, |
246 | newoff; |
247 | Datum *cvals; /* column values */ |
248 | char *cnulls; /* column nulls */ |
249 | char *relname; /* triggered relation name */ |
250 | Relation rel; /* triggered relation */ |
251 | HeapTuple trigtuple; |
252 | HeapTuple newtuple = NULL; |
253 | HeapTuple rettuple; |
254 | TupleDesc tupdesc; /* tuple description */ |
255 | int natts; /* # of attributes */ |
256 | bool isnull; /* to know is some column NULL or not */ |
257 | int ret; |
258 | int i; |
259 | |
260 | if (!CALLED_AS_TRIGGER(fcinfo)) |
261 | elog(ERROR, "ttdummy: not fired by trigger manager" ); |
262 | if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) |
263 | elog(ERROR, "ttdummy: must be fired for row" ); |
264 | if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) |
265 | elog(ERROR, "ttdummy: must be fired before event" ); |
266 | if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) |
267 | elog(ERROR, "ttdummy: cannot process INSERT event" ); |
268 | if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) |
269 | newtuple = trigdata->tg_newtuple; |
270 | |
271 | trigtuple = trigdata->tg_trigtuple; |
272 | |
273 | rel = trigdata->tg_relation; |
274 | relname = SPI_getrelname(rel); |
275 | |
276 | /* check if TT is OFF for this relation */ |
277 | if (ttoff) /* OFF - nothing to do */ |
278 | { |
279 | pfree(relname); |
280 | return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple); |
281 | } |
282 | |
283 | trigger = trigdata->tg_trigger; |
284 | |
285 | if (trigger->tgnargs != 2) |
286 | elog(ERROR, "ttdummy (%s): invalid (!= 2) number of arguments %d" , |
287 | relname, trigger->tgnargs); |
288 | |
289 | args = trigger->tgargs; |
290 | tupdesc = rel->rd_att; |
291 | natts = tupdesc->natts; |
292 | |
293 | for (i = 0; i < 2; i++) |
294 | { |
295 | attnum[i] = SPI_fnumber(tupdesc, args[i]); |
296 | if (attnum[i] <= 0) |
297 | elog(ERROR, "ttdummy (%s): there is no attribute %s" , |
298 | relname, args[i]); |
299 | if (SPI_gettypeid(tupdesc, attnum[i]) != INT4OID) |
300 | elog(ERROR, "ttdummy (%s): attribute %s must be of integer type" , |
301 | relname, args[i]); |
302 | } |
303 | |
304 | oldon = SPI_getbinval(trigtuple, tupdesc, attnum[0], &isnull); |
305 | if (isnull) |
306 | elog(ERROR, "ttdummy (%s): %s must be NOT NULL" , relname, args[0]); |
307 | |
308 | oldoff = SPI_getbinval(trigtuple, tupdesc, attnum[1], &isnull); |
309 | if (isnull) |
310 | elog(ERROR, "ttdummy (%s): %s must be NOT NULL" , relname, args[1]); |
311 | |
312 | if (newtuple != NULL) /* UPDATE */ |
313 | { |
314 | newon = SPI_getbinval(newtuple, tupdesc, attnum[0], &isnull); |
315 | if (isnull) |
316 | elog(ERROR, "ttdummy (%s): %s must be NOT NULL" , relname, args[0]); |
317 | newoff = SPI_getbinval(newtuple, tupdesc, attnum[1], &isnull); |
318 | if (isnull) |
319 | elog(ERROR, "ttdummy (%s): %s must be NOT NULL" , relname, args[1]); |
320 | |
321 | if (oldon != newon || oldoff != newoff) |
322 | ereport(ERROR, |
323 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
324 | errmsg("ttdummy (%s): you cannot change %s and/or %s columns (use set_ttdummy)" , |
325 | relname, args[0], args[1]))); |
326 | |
327 | if (newoff != TTDUMMY_INFINITY) |
328 | { |
329 | pfree(relname); /* allocated in upper executor context */ |
330 | return PointerGetDatum(NULL); |
331 | } |
332 | } |
333 | else if (oldoff != TTDUMMY_INFINITY) /* DELETE */ |
334 | { |
335 | pfree(relname); |
336 | return PointerGetDatum(NULL); |
337 | } |
338 | |
339 | newoff = DirectFunctionCall1(nextval, CStringGetTextDatum("ttdummy_seq" )); |
340 | /* nextval now returns int64; coerce down to int32 */ |
341 | newoff = Int32GetDatum((int32) DatumGetInt64(newoff)); |
342 | |
343 | /* Connect to SPI manager */ |
344 | if ((ret = SPI_connect()) < 0) |
345 | elog(ERROR, "ttdummy (%s): SPI_connect returned %d" , relname, ret); |
346 | |
347 | /* Fetch tuple values and nulls */ |
348 | cvals = (Datum *) palloc(natts * sizeof(Datum)); |
349 | cnulls = (char *) palloc(natts * sizeof(char)); |
350 | for (i = 0; i < natts; i++) |
351 | { |
352 | cvals[i] = SPI_getbinval((newtuple != NULL) ? newtuple : trigtuple, |
353 | tupdesc, i + 1, &isnull); |
354 | cnulls[i] = (isnull) ? 'n' : ' '; |
355 | } |
356 | |
357 | /* change date column(s) */ |
358 | if (newtuple) /* UPDATE */ |
359 | { |
360 | cvals[attnum[0] - 1] = newoff; /* start_date eq current date */ |
361 | cnulls[attnum[0] - 1] = ' '; |
362 | cvals[attnum[1] - 1] = TTDUMMY_INFINITY; /* stop_date eq INFINITY */ |
363 | cnulls[attnum[1] - 1] = ' '; |
364 | } |
365 | else |
366 | /* DELETE */ |
367 | { |
368 | cvals[attnum[1] - 1] = newoff; /* stop_date eq current date */ |
369 | cnulls[attnum[1] - 1] = ' '; |
370 | } |
371 | |
372 | /* if there is no plan ... */ |
373 | if (splan == NULL) |
374 | { |
375 | SPIPlanPtr pplan; |
376 | Oid *ctypes; |
377 | char *query; |
378 | |
379 | /* allocate space in preparation */ |
380 | ctypes = (Oid *) palloc(natts * sizeof(Oid)); |
381 | query = (char *) palloc(100 + 16 * natts); |
382 | |
383 | /* |
384 | * Construct query: INSERT INTO _relation_ VALUES ($1, ...) |
385 | */ |
386 | sprintf(query, "INSERT INTO %s VALUES (" , relname); |
387 | for (i = 1; i <= natts; i++) |
388 | { |
389 | sprintf(query + strlen(query), "$%d%s" , |
390 | i, (i < natts) ? ", " : ")" ); |
391 | ctypes[i - 1] = SPI_gettypeid(tupdesc, i); |
392 | } |
393 | |
394 | /* Prepare plan for query */ |
395 | pplan = SPI_prepare(query, natts, ctypes); |
396 | if (pplan == NULL) |
397 | elog(ERROR, "ttdummy (%s): SPI_prepare returned %s" , relname, SPI_result_code_string(SPI_result)); |
398 | |
399 | if (SPI_keepplan(pplan)) |
400 | elog(ERROR, "ttdummy (%s): SPI_keepplan failed" , relname); |
401 | |
402 | splan = pplan; |
403 | } |
404 | |
405 | ret = SPI_execp(splan, cvals, cnulls, 0); |
406 | |
407 | if (ret < 0) |
408 | elog(ERROR, "ttdummy (%s): SPI_execp returned %d" , relname, ret); |
409 | |
410 | /* Tuple to return to upper Executor ... */ |
411 | if (newtuple) /* UPDATE */ |
412 | rettuple = SPI_modifytuple(rel, trigtuple, 1, &(attnum[1]), &newoff, NULL); |
413 | else /* DELETE */ |
414 | rettuple = trigtuple; |
415 | |
416 | SPI_finish(); /* don't forget say Bye to SPI mgr */ |
417 | |
418 | pfree(relname); |
419 | |
420 | return PointerGetDatum(rettuple); |
421 | } |
422 | |
423 | PG_FUNCTION_INFO_V1(set_ttdummy); |
424 | |
425 | Datum |
426 | set_ttdummy(PG_FUNCTION_ARGS) |
427 | { |
428 | int32 on = PG_GETARG_INT32(0); |
429 | |
430 | if (ttoff) /* OFF currently */ |
431 | { |
432 | if (on == 0) |
433 | PG_RETURN_INT32(0); |
434 | |
435 | /* turn ON */ |
436 | ttoff = false; |
437 | PG_RETURN_INT32(0); |
438 | } |
439 | |
440 | /* ON currently */ |
441 | if (on != 0) |
442 | PG_RETURN_INT32(1); |
443 | |
444 | /* turn OFF */ |
445 | ttoff = true; |
446 | |
447 | PG_RETURN_INT32(1); |
448 | } |
449 | |
450 | |
451 | /* |
452 | * Type int44 has no real-world use, but the regression tests use it |
453 | * (under the alias "city_budget"). It's a four-element vector of int4's. |
454 | */ |
455 | |
456 | /* |
457 | * int44in - converts "num, num, ..." to internal form |
458 | * |
459 | * Note: Fills any missing positions with zeroes. |
460 | */ |
461 | PG_FUNCTION_INFO_V1(int44in); |
462 | |
463 | Datum |
464 | int44in(PG_FUNCTION_ARGS) |
465 | { |
466 | char *input_string = PG_GETARG_CSTRING(0); |
467 | int32 *result = (int32 *) palloc(4 * sizeof(int32)); |
468 | int i; |
469 | |
470 | i = sscanf(input_string, |
471 | "%d, %d, %d, %d" , |
472 | &result[0], |
473 | &result[1], |
474 | &result[2], |
475 | &result[3]); |
476 | while (i < 4) |
477 | result[i++] = 0; |
478 | |
479 | PG_RETURN_POINTER(result); |
480 | } |
481 | |
482 | /* |
483 | * int44out - converts internal form to "num, num, ..." |
484 | */ |
485 | PG_FUNCTION_INFO_V1(int44out); |
486 | |
487 | Datum |
488 | int44out(PG_FUNCTION_ARGS) |
489 | { |
490 | int32 *an_array = (int32 *) PG_GETARG_POINTER(0); |
491 | char *result = (char *) palloc(16 * 4); |
492 | |
493 | snprintf(result, 16 * 4, "%d,%d,%d,%d" , |
494 | an_array[0], |
495 | an_array[1], |
496 | an_array[2], |
497 | an_array[3]); |
498 | |
499 | PG_RETURN_CSTRING(result); |
500 | } |
501 | |
502 | PG_FUNCTION_INFO_V1(make_tuple_indirect); |
503 | Datum |
504 | make_tuple_indirect(PG_FUNCTION_ARGS) |
505 | { |
506 | HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); |
507 | HeapTupleData tuple; |
508 | int ncolumns; |
509 | Datum *values; |
510 | bool *nulls; |
511 | |
512 | Oid tupType; |
513 | int32 tupTypmod; |
514 | TupleDesc tupdesc; |
515 | |
516 | HeapTuple newtup; |
517 | |
518 | int i; |
519 | |
520 | MemoryContext old_context; |
521 | |
522 | /* Extract type info from the tuple itself */ |
523 | tupType = HeapTupleHeaderGetTypeId(rec); |
524 | tupTypmod = HeapTupleHeaderGetTypMod(rec); |
525 | tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); |
526 | ncolumns = tupdesc->natts; |
527 | |
528 | /* Build a temporary HeapTuple control structure */ |
529 | tuple.t_len = HeapTupleHeaderGetDatumLength(rec); |
530 | ItemPointerSetInvalid(&(tuple.t_self)); |
531 | tuple.t_tableOid = InvalidOid; |
532 | tuple.t_data = rec; |
533 | |
534 | values = (Datum *) palloc(ncolumns * sizeof(Datum)); |
535 | nulls = (bool *) palloc(ncolumns * sizeof(bool)); |
536 | |
537 | heap_deform_tuple(&tuple, tupdesc, values, nulls); |
538 | |
539 | old_context = MemoryContextSwitchTo(TopTransactionContext); |
540 | |
541 | for (i = 0; i < ncolumns; i++) |
542 | { |
543 | struct varlena *attr; |
544 | struct varlena *new_attr; |
545 | struct varatt_indirect redirect_pointer; |
546 | |
547 | /* only work on existing, not-null varlenas */ |
548 | if (TupleDescAttr(tupdesc, i)->attisdropped || |
549 | nulls[i] || |
550 | TupleDescAttr(tupdesc, i)->attlen != -1) |
551 | continue; |
552 | |
553 | attr = (struct varlena *) DatumGetPointer(values[i]); |
554 | |
555 | /* don't recursively indirect */ |
556 | if (VARATT_IS_EXTERNAL_INDIRECT(attr)) |
557 | continue; |
558 | |
559 | /* copy datum, so it still lives later */ |
560 | if (VARATT_IS_EXTERNAL_ONDISK(attr)) |
561 | attr = heap_tuple_fetch_attr(attr); |
562 | else |
563 | { |
564 | struct varlena *oldattr = attr; |
565 | |
566 | attr = palloc0(VARSIZE_ANY(oldattr)); |
567 | memcpy(attr, oldattr, VARSIZE_ANY(oldattr)); |
568 | } |
569 | |
570 | /* build indirection Datum */ |
571 | new_attr = (struct varlena *) palloc0(INDIRECT_POINTER_SIZE); |
572 | redirect_pointer.pointer = attr; |
573 | SET_VARTAG_EXTERNAL(new_attr, VARTAG_INDIRECT); |
574 | memcpy(VARDATA_EXTERNAL(new_attr), &redirect_pointer, |
575 | sizeof(redirect_pointer)); |
576 | |
577 | values[i] = PointerGetDatum(new_attr); |
578 | } |
579 | |
580 | newtup = heap_form_tuple(tupdesc, values, nulls); |
581 | pfree(values); |
582 | pfree(nulls); |
583 | ReleaseTupleDesc(tupdesc); |
584 | |
585 | MemoryContextSwitchTo(old_context); |
586 | |
587 | /* |
588 | * We intentionally don't use PG_RETURN_HEAPTUPLEHEADER here, because that |
589 | * would cause the indirect toast pointers to be flattened out of the |
590 | * tuple immediately, rendering subsequent testing irrelevant. So just |
591 | * return the HeapTupleHeader pointer as-is. This violates the general |
592 | * rule that composite Datums shouldn't contain toast pointers, but so |
593 | * long as the regression test scripts don't insert the result of this |
594 | * function into a container type (record, array, etc) it should be OK. |
595 | */ |
596 | PG_RETURN_POINTER(newtup->t_data); |
597 | } |
598 | |
599 | PG_FUNCTION_INFO_V1(regress_putenv); |
600 | |
601 | Datum |
602 | regress_putenv(PG_FUNCTION_ARGS) |
603 | { |
604 | MemoryContext oldcontext; |
605 | char *envbuf; |
606 | |
607 | if (!superuser()) |
608 | elog(ERROR, "must be superuser to change environment variables" ); |
609 | |
610 | oldcontext = MemoryContextSwitchTo(TopMemoryContext); |
611 | envbuf = text_to_cstring((text *) PG_GETARG_POINTER(0)); |
612 | MemoryContextSwitchTo(oldcontext); |
613 | |
614 | if (putenv(envbuf) != 0) |
615 | elog(ERROR, "could not set environment variable: %m" ); |
616 | |
617 | PG_RETURN_VOID(); |
618 | } |
619 | |
620 | /* Sleep until no process has a given PID. */ |
621 | PG_FUNCTION_INFO_V1(wait_pid); |
622 | |
623 | Datum |
624 | wait_pid(PG_FUNCTION_ARGS) |
625 | { |
626 | int pid = PG_GETARG_INT32(0); |
627 | |
628 | if (!superuser()) |
629 | elog(ERROR, "must be superuser to check PID liveness" ); |
630 | |
631 | while (kill(pid, 0) == 0) |
632 | { |
633 | CHECK_FOR_INTERRUPTS(); |
634 | pg_usleep(50000); |
635 | } |
636 | |
637 | if (errno != ESRCH) |
638 | elog(ERROR, "could not check PID %d liveness: %m" , pid); |
639 | |
640 | PG_RETURN_VOID(); |
641 | } |
642 | |
643 | static void |
644 | test_atomic_flag(void) |
645 | { |
646 | pg_atomic_flag flag; |
647 | |
648 | pg_atomic_init_flag(&flag); |
649 | |
650 | if (!pg_atomic_unlocked_test_flag(&flag)) |
651 | elog(ERROR, "flag: unexpectedly set" ); |
652 | |
653 | if (!pg_atomic_test_set_flag(&flag)) |
654 | elog(ERROR, "flag: couldn't set" ); |
655 | |
656 | if (pg_atomic_unlocked_test_flag(&flag)) |
657 | elog(ERROR, "flag: unexpectedly unset" ); |
658 | |
659 | if (pg_atomic_test_set_flag(&flag)) |
660 | elog(ERROR, "flag: set spuriously #2" ); |
661 | |
662 | pg_atomic_clear_flag(&flag); |
663 | |
664 | if (!pg_atomic_unlocked_test_flag(&flag)) |
665 | elog(ERROR, "flag: unexpectedly set #2" ); |
666 | |
667 | if (!pg_atomic_test_set_flag(&flag)) |
668 | elog(ERROR, "flag: couldn't set" ); |
669 | |
670 | pg_atomic_clear_flag(&flag); |
671 | } |
672 | |
673 | static void |
674 | test_atomic_uint32(void) |
675 | { |
676 | pg_atomic_uint32 var; |
677 | uint32 expected; |
678 | int i; |
679 | |
680 | pg_atomic_init_u32(&var, 0); |
681 | |
682 | if (pg_atomic_read_u32(&var) != 0) |
683 | elog(ERROR, "atomic_read_u32() #1 wrong" ); |
684 | |
685 | pg_atomic_write_u32(&var, 3); |
686 | |
687 | if (pg_atomic_read_u32(&var) != 3) |
688 | elog(ERROR, "atomic_read_u32() #2 wrong" ); |
689 | |
690 | if (pg_atomic_fetch_add_u32(&var, pg_atomic_read_u32(&var) - 2) != 3) |
691 | elog(ERROR, "atomic_fetch_add_u32() #1 wrong" ); |
692 | |
693 | if (pg_atomic_fetch_sub_u32(&var, 1) != 4) |
694 | elog(ERROR, "atomic_fetch_sub_u32() #1 wrong" ); |
695 | |
696 | if (pg_atomic_sub_fetch_u32(&var, 3) != 0) |
697 | elog(ERROR, "atomic_sub_fetch_u32() #1 wrong" ); |
698 | |
699 | if (pg_atomic_add_fetch_u32(&var, 10) != 10) |
700 | elog(ERROR, "atomic_add_fetch_u32() #1 wrong" ); |
701 | |
702 | if (pg_atomic_exchange_u32(&var, 5) != 10) |
703 | elog(ERROR, "pg_atomic_exchange_u32() #1 wrong" ); |
704 | |
705 | if (pg_atomic_exchange_u32(&var, 0) != 5) |
706 | elog(ERROR, "pg_atomic_exchange_u32() #0 wrong" ); |
707 | |
708 | /* test around numerical limits */ |
709 | if (pg_atomic_fetch_add_u32(&var, INT_MAX) != 0) |
710 | elog(ERROR, "pg_atomic_fetch_add_u32() #2 wrong" ); |
711 | |
712 | if (pg_atomic_fetch_add_u32(&var, INT_MAX) != INT_MAX) |
713 | elog(ERROR, "pg_atomic_add_fetch_u32() #3 wrong" ); |
714 | |
715 | pg_atomic_fetch_add_u32(&var, 2); /* wrap to 0 */ |
716 | |
717 | if (pg_atomic_fetch_add_u32(&var, PG_INT16_MAX) != 0) |
718 | elog(ERROR, "pg_atomic_fetch_add_u32() #3 wrong" ); |
719 | |
720 | if (pg_atomic_fetch_add_u32(&var, PG_INT16_MAX + 1) != PG_INT16_MAX) |
721 | elog(ERROR, "pg_atomic_fetch_add_u32() #4 wrong" ); |
722 | |
723 | if (pg_atomic_fetch_add_u32(&var, PG_INT16_MIN) != 2 * PG_INT16_MAX + 1) |
724 | elog(ERROR, "pg_atomic_fetch_add_u32() #5 wrong" ); |
725 | |
726 | if (pg_atomic_fetch_add_u32(&var, PG_INT16_MIN - 1) != PG_INT16_MAX) |
727 | elog(ERROR, "pg_atomic_fetch_add_u32() #6 wrong" ); |
728 | |
729 | pg_atomic_fetch_add_u32(&var, 1); /* top up to UINT_MAX */ |
730 | |
731 | if (pg_atomic_read_u32(&var) != UINT_MAX) |
732 | elog(ERROR, "atomic_read_u32() #2 wrong" ); |
733 | |
734 | if (pg_atomic_fetch_sub_u32(&var, INT_MAX) != UINT_MAX) |
735 | elog(ERROR, "pg_atomic_fetch_sub_u32() #2 wrong" ); |
736 | |
737 | if (pg_atomic_read_u32(&var) != (uint32) INT_MAX + 1) |
738 | elog(ERROR, "atomic_read_u32() #3 wrong: %u" , pg_atomic_read_u32(&var)); |
739 | |
740 | expected = pg_atomic_sub_fetch_u32(&var, INT_MAX); |
741 | if (expected != 1) |
742 | elog(ERROR, "pg_atomic_sub_fetch_u32() #3 wrong: %u" , expected); |
743 | |
744 | pg_atomic_sub_fetch_u32(&var, 1); |
745 | |
746 | /* fail exchange because of old expected */ |
747 | expected = 10; |
748 | if (pg_atomic_compare_exchange_u32(&var, &expected, 1)) |
749 | elog(ERROR, "atomic_compare_exchange_u32() changed value spuriously" ); |
750 | |
751 | /* CAS is allowed to fail due to interrupts, try a couple of times */ |
752 | for (i = 0; i < 1000; i++) |
753 | { |
754 | expected = 0; |
755 | if (!pg_atomic_compare_exchange_u32(&var, &expected, 1)) |
756 | break; |
757 | } |
758 | if (i == 1000) |
759 | elog(ERROR, "atomic_compare_exchange_u32() never succeeded" ); |
760 | if (pg_atomic_read_u32(&var) != 1) |
761 | elog(ERROR, "atomic_compare_exchange_u32() didn't set value properly" ); |
762 | |
763 | pg_atomic_write_u32(&var, 0); |
764 | |
765 | /* try setting flagbits */ |
766 | if (pg_atomic_fetch_or_u32(&var, 1) & 1) |
767 | elog(ERROR, "pg_atomic_fetch_or_u32() #1 wrong" ); |
768 | |
769 | if (!(pg_atomic_fetch_or_u32(&var, 2) & 1)) |
770 | elog(ERROR, "pg_atomic_fetch_or_u32() #2 wrong" ); |
771 | |
772 | if (pg_atomic_read_u32(&var) != 3) |
773 | elog(ERROR, "invalid result after pg_atomic_fetch_or_u32()" ); |
774 | |
775 | /* try clearing flagbits */ |
776 | if ((pg_atomic_fetch_and_u32(&var, ~2) & 3) != 3) |
777 | elog(ERROR, "pg_atomic_fetch_and_u32() #1 wrong" ); |
778 | |
779 | if (pg_atomic_fetch_and_u32(&var, ~1) != 1) |
780 | elog(ERROR, "pg_atomic_fetch_and_u32() #2 wrong: is %u" , |
781 | pg_atomic_read_u32(&var)); |
782 | /* no bits set anymore */ |
783 | if (pg_atomic_fetch_and_u32(&var, ~0) != 0) |
784 | elog(ERROR, "pg_atomic_fetch_and_u32() #3 wrong" ); |
785 | } |
786 | |
787 | static void |
788 | test_atomic_uint64(void) |
789 | { |
790 | pg_atomic_uint64 var; |
791 | uint64 expected; |
792 | int i; |
793 | |
794 | pg_atomic_init_u64(&var, 0); |
795 | |
796 | if (pg_atomic_read_u64(&var) != 0) |
797 | elog(ERROR, "atomic_read_u64() #1 wrong" ); |
798 | |
799 | pg_atomic_write_u64(&var, 3); |
800 | |
801 | if (pg_atomic_read_u64(&var) != 3) |
802 | elog(ERROR, "atomic_read_u64() #2 wrong" ); |
803 | |
804 | if (pg_atomic_fetch_add_u64(&var, pg_atomic_read_u64(&var) - 2) != 3) |
805 | elog(ERROR, "atomic_fetch_add_u64() #1 wrong" ); |
806 | |
807 | if (pg_atomic_fetch_sub_u64(&var, 1) != 4) |
808 | elog(ERROR, "atomic_fetch_sub_u64() #1 wrong" ); |
809 | |
810 | if (pg_atomic_sub_fetch_u64(&var, 3) != 0) |
811 | elog(ERROR, "atomic_sub_fetch_u64() #1 wrong" ); |
812 | |
813 | if (pg_atomic_add_fetch_u64(&var, 10) != 10) |
814 | elog(ERROR, "atomic_add_fetch_u64() #1 wrong" ); |
815 | |
816 | if (pg_atomic_exchange_u64(&var, 5) != 10) |
817 | elog(ERROR, "pg_atomic_exchange_u64() #1 wrong" ); |
818 | |
819 | if (pg_atomic_exchange_u64(&var, 0) != 5) |
820 | elog(ERROR, "pg_atomic_exchange_u64() #0 wrong" ); |
821 | |
822 | /* fail exchange because of old expected */ |
823 | expected = 10; |
824 | if (pg_atomic_compare_exchange_u64(&var, &expected, 1)) |
825 | elog(ERROR, "atomic_compare_exchange_u64() changed value spuriously" ); |
826 | |
827 | /* CAS is allowed to fail due to interrupts, try a couple of times */ |
828 | for (i = 0; i < 100; i++) |
829 | { |
830 | expected = 0; |
831 | if (!pg_atomic_compare_exchange_u64(&var, &expected, 1)) |
832 | break; |
833 | } |
834 | if (i == 100) |
835 | elog(ERROR, "atomic_compare_exchange_u64() never succeeded" ); |
836 | if (pg_atomic_read_u64(&var) != 1) |
837 | elog(ERROR, "atomic_compare_exchange_u64() didn't set value properly" ); |
838 | |
839 | pg_atomic_write_u64(&var, 0); |
840 | |
841 | /* try setting flagbits */ |
842 | if (pg_atomic_fetch_or_u64(&var, 1) & 1) |
843 | elog(ERROR, "pg_atomic_fetch_or_u64() #1 wrong" ); |
844 | |
845 | if (!(pg_atomic_fetch_or_u64(&var, 2) & 1)) |
846 | elog(ERROR, "pg_atomic_fetch_or_u64() #2 wrong" ); |
847 | |
848 | if (pg_atomic_read_u64(&var) != 3) |
849 | elog(ERROR, "invalid result after pg_atomic_fetch_or_u64()" ); |
850 | |
851 | /* try clearing flagbits */ |
852 | if ((pg_atomic_fetch_and_u64(&var, ~2) & 3) != 3) |
853 | elog(ERROR, "pg_atomic_fetch_and_u64() #1 wrong" ); |
854 | |
855 | if (pg_atomic_fetch_and_u64(&var, ~1) != 1) |
856 | elog(ERROR, "pg_atomic_fetch_and_u64() #2 wrong: is " UINT64_FORMAT, |
857 | pg_atomic_read_u64(&var)); |
858 | /* no bits set anymore */ |
859 | if (pg_atomic_fetch_and_u64(&var, ~0) != 0) |
860 | elog(ERROR, "pg_atomic_fetch_and_u64() #3 wrong" ); |
861 | } |
862 | |
863 | |
864 | PG_FUNCTION_INFO_V1(test_atomic_ops); |
865 | Datum |
866 | test_atomic_ops(PG_FUNCTION_ARGS) |
867 | { |
868 | test_atomic_flag(); |
869 | |
870 | test_atomic_uint32(); |
871 | |
872 | test_atomic_uint64(); |
873 | |
874 | PG_RETURN_BOOL(true); |
875 | } |
876 | |
877 | PG_FUNCTION_INFO_V1(test_fdw_handler); |
878 | Datum |
879 | test_fdw_handler(PG_FUNCTION_ARGS) |
880 | { |
881 | elog(ERROR, "test_fdw_handler is not implemented" ); |
882 | PG_RETURN_NULL(); |
883 | } |
884 | |
885 | PG_FUNCTION_INFO_V1(test_support_func); |
886 | Datum |
887 | test_support_func(PG_FUNCTION_ARGS) |
888 | { |
889 | Node *rawreq = (Node *) PG_GETARG_POINTER(0); |
890 | Node *ret = NULL; |
891 | |
892 | if (IsA(rawreq, SupportRequestSelectivity)) |
893 | { |
894 | /* |
895 | * Assume that the target is int4eq; that's safe as long as we don't |
896 | * attach this to any other boolean-returning function. |
897 | */ |
898 | SupportRequestSelectivity *req = (SupportRequestSelectivity *) rawreq; |
899 | Selectivity s1; |
900 | |
901 | if (req->is_join) |
902 | s1 = join_selectivity(req->root, Int4EqualOperator, |
903 | req->args, |
904 | req->inputcollid, |
905 | req->jointype, |
906 | req->sjinfo); |
907 | else |
908 | s1 = restriction_selectivity(req->root, Int4EqualOperator, |
909 | req->args, |
910 | req->inputcollid, |
911 | req->varRelid); |
912 | |
913 | req->selectivity = s1; |
914 | ret = (Node *) req; |
915 | } |
916 | |
917 | if (IsA(rawreq, SupportRequestCost)) |
918 | { |
919 | /* Provide some generic estimate */ |
920 | SupportRequestCost *req = (SupportRequestCost *) rawreq; |
921 | |
922 | req->startup = 0; |
923 | req->per_tuple = 2 * cpu_operator_cost; |
924 | ret = (Node *) req; |
925 | } |
926 | |
927 | if (IsA(rawreq, SupportRequestRows)) |
928 | { |
929 | /* |
930 | * Assume that the target is generate_series_int4; that's safe as long |
931 | * as we don't attach this to any other set-returning function. |
932 | */ |
933 | SupportRequestRows *req = (SupportRequestRows *) rawreq; |
934 | |
935 | if (req->node && IsA(req->node, FuncExpr)) /* be paranoid */ |
936 | { |
937 | List *args = ((FuncExpr *) req->node)->args; |
938 | Node *arg1 = linitial(args); |
939 | Node *arg2 = lsecond(args); |
940 | |
941 | if (IsA(arg1, Const) && |
942 | !((Const *) arg1)->constisnull && |
943 | IsA(arg2, Const) && |
944 | !((Const *) arg2)->constisnull) |
945 | { |
946 | int32 val1 = DatumGetInt32(((Const *) arg1)->constvalue); |
947 | int32 val2 = DatumGetInt32(((Const *) arg2)->constvalue); |
948 | |
949 | req->rows = val2 - val1 + 1; |
950 | ret = (Node *) req; |
951 | } |
952 | } |
953 | } |
954 | |
955 | PG_RETURN_POINTER(ret); |
956 | } |
957 | |