Age Owner Branch data TLA Line data Source code
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-2025, 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/detoast.h"
23 : : #include "access/htup_details.h"
24 : : #include "catalog/catalog.h"
25 : : #include "catalog/namespace.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 "funcapi.h"
33 : : #include "mb/pg_wchar.h"
34 : : #include "miscadmin.h"
35 : : #include "nodes/supportnodes.h"
36 : : #include "optimizer/optimizer.h"
37 : : #include "optimizer/plancat.h"
38 : : #include "parser/parse_coerce.h"
39 : : #include "port/atomics.h"
40 : : #include "postmaster/postmaster.h" /* for MAX_BACKENDS */
41 : : #include "storage/spin.h"
42 : : #include "utils/array.h"
43 : : #include "utils/builtins.h"
44 : : #include "utils/geo_decls.h"
45 : : #include "utils/memutils.h"
46 : : #include "utils/rel.h"
47 : : #include "utils/typcache.h"
48 : :
49 : : #define EXPECT_TRUE(expr) \
50 : : do { \
51 : : if (!(expr)) \
52 : : elog(ERROR, \
53 : : "%s was unexpectedly false in file \"%s\" line %u", \
54 : : #expr, __FILE__, __LINE__); \
55 : : } while (0)
56 : :
57 : : #define EXPECT_EQ_U32(result_expr, expected_expr) \
58 : : do { \
59 : : uint32 actual_result = (result_expr); \
60 : : uint32 expected_result = (expected_expr); \
61 : : if (actual_result != expected_result) \
62 : : elog(ERROR, \
63 : : "%s yielded %u, expected %s in file \"%s\" line %u", \
64 : : #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
65 : : } while (0)
66 : :
67 : : #define EXPECT_EQ_U64(result_expr, expected_expr) \
68 : : do { \
69 : : uint64 actual_result = (result_expr); \
70 : : uint64 expected_result = (expected_expr); \
71 : : if (actual_result != expected_result) \
72 : : elog(ERROR, \
73 : : "%s yielded " UINT64_FORMAT ", expected %s in file \"%s\" line %u", \
74 : : #result_expr, actual_result, #expected_expr, __FILE__, __LINE__); \
75 : : } while (0)
76 : :
77 : : #define LDELIM '('
78 : : #define RDELIM ')'
79 : : #define DELIM ','
80 : :
81 : : static void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
82 : :
164 tgl@sss.pgh.pa.us 83 :CBC 59 : PG_MODULE_MAGIC_EXT(
84 : : .name = "regress",
85 : : .version = PG_VERSION
86 : : );
87 : :
88 : :
89 : : /* return the point where two paths intersect, or NULL if no intersection. */
9056 90 : 7 : PG_FUNCTION_INFO_V1(interpt_pp);
91 : :
92 : : Datum
9169 93 : 2688 : interpt_pp(PG_FUNCTION_ARGS)
94 : : {
95 : 2688 : PATH *p1 = PG_GETARG_PATH_P(0);
96 : 2688 : PATH *p2 = PG_GETARG_PATH_P(1);
97 : : int i,
98 : : j;
99 : : LSEG seg1,
100 : : seg2;
101 : : bool found; /* We've found the intersection */
102 : :
10226 bruce@momjian.us 103 : 2688 : found = false; /* Haven't found it yet */
104 : :
105 [ + + + + ]: 8823 : for (i = 0; i < p1->npts - 1 && !found; i++)
106 : : {
9169 tgl@sss.pgh.pa.us 107 : 6135 : regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i + 1]);
10226 bruce@momjian.us 108 [ + + + + ]: 18819 : for (j = 0; j < p2->npts - 1 && !found; j++)
109 : : {
110 : 12684 : regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j + 1]);
9169 tgl@sss.pgh.pa.us 111 [ + + ]: 12684 : if (DatumGetBool(DirectFunctionCall2(lseg_intersect,
112 : : LsegPGetDatum(&seg1),
113 : : LsegPGetDatum(&seg2))))
10226 bruce@momjian.us 114 : 2682 : found = true;
115 : : }
116 : : }
117 : :
9169 tgl@sss.pgh.pa.us 118 [ + + ]: 2688 : if (!found)
119 : 6 : PG_RETURN_NULL();
120 : :
121 : : /*
122 : : * Note: DirectFunctionCall2 will kick out an error if lseg_interpt()
123 : : * returns NULL, but that should be impossible since we know the two
124 : : * segments intersect.
125 : : */
126 : 2682 : PG_RETURN_DATUM(DirectFunctionCall2(lseg_interpt,
127 : : LsegPGetDatum(&seg1),
128 : : LsegPGetDatum(&seg2)));
129 : : }
130 : :
131 : :
132 : : /* like lseg_construct, but assume space already allocated */
133 : : static void
8206 bruce@momjian.us 134 : 18819 : regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
135 : : {
10226 136 : 18819 : lseg->p[0].x = pt1->x;
137 : 18819 : lseg->p[0].y = pt1->y;
138 : 18819 : lseg->p[1].x = pt2->x;
139 : 18819 : lseg->p[1].y = pt2->y;
10651 scrappy@hub.org 140 : 18819 : }
141 : :
9056 tgl@sss.pgh.pa.us 142 : 7 : PG_FUNCTION_INFO_V1(overpaid);
143 : :
144 : : Datum
9224 145 : 18 : overpaid(PG_FUNCTION_ARGS)
146 : : {
7828 147 : 18 : HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
148 : : bool isnull;
149 : : int32 salary;
150 : :
9144 151 : 18 : salary = DatumGetInt32(GetAttributeByName(tuple, "salary", &isnull));
9224 152 [ - + ]: 18 : if (isnull)
9224 tgl@sss.pgh.pa.us 153 :UBC 0 : PG_RETURN_NULL();
9224 tgl@sss.pgh.pa.us 154 :CBC 18 : PG_RETURN_BOOL(salary > 699);
155 : : }
156 : :
157 : : /* New type "widget"
158 : : * This used to be "circle", but I added circle to builtins,
159 : : * so needed to make sure the names do not collide. - tgl 97/04/21
160 : : */
161 : :
162 : : typedef struct
163 : : {
164 : : Point center;
165 : : double radius;
166 : : } WIDGET;
167 : :
3083 andres@anarazel.de 168 : 10 : PG_FUNCTION_INFO_V1(widget_in);
169 : 7 : PG_FUNCTION_INFO_V1(widget_out);
170 : :
171 : : #define NARGS 3
172 : :
173 : : Datum
174 : 33 : widget_in(PG_FUNCTION_ARGS)
175 : : {
176 : 33 : char *str = PG_GETARG_CSTRING(0);
177 : : char *p,
178 : : *coord[NARGS];
179 : : int i;
180 : : WIDGET *result;
181 : :
10651 scrappy@hub.org 182 [ + + + + : 189 : for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
+ + ]
183 : : {
3528 tgl@sss.pgh.pa.us 184 [ + + + + : 156 : if (*p == DELIM || (*p == LDELIM && i == 0))
+ - ]
10651 scrappy@hub.org 185 : 81 : coord[i++] = p + 1;
186 : : }
187 : :
188 : : /*
189 : : * Note: DON'T convert this error to "soft" style (errsave/ereturn). We
190 : : * want this data type to stay permanently in the hard-error world so that
191 : : * it can be used for testing that such cases still work reasonably.
192 : : */
3528 tgl@sss.pgh.pa.us 193 [ + + ]: 33 : if (i < NARGS)
194 [ + - ]: 12 : ereport(ERROR,
195 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
196 : : errmsg("invalid input syntax for type %s: \"%s\"",
197 : : "widget", str)));
198 : :
10364 scrappy@hub.org 199 : 21 : result = (WIDGET *) palloc(sizeof(WIDGET));
10651 200 : 21 : result->center.x = atof(coord[0]);
201 : 21 : result->center.y = atof(coord[1]);
202 : 21 : result->radius = atof(coord[2]);
203 : :
3083 andres@anarazel.de 204 : 21 : PG_RETURN_POINTER(result);
205 : : }
206 : :
207 : : Datum
208 : 6 : widget_out(PG_FUNCTION_ARGS)
209 : : {
3034 bruce@momjian.us 210 : 6 : WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(0);
211 : 6 : char *str = psprintf("(%g,%g,%g)",
212 : : widget->center.x, widget->center.y, widget->radius);
213 : :
3083 andres@anarazel.de 214 : 6 : PG_RETURN_CSTRING(str);
215 : : }
216 : :
9056 tgl@sss.pgh.pa.us 217 : 7 : PG_FUNCTION_INFO_V1(pt_in_widget);
218 : :
219 : : Datum
9216 220 : 6 : pt_in_widget(PG_FUNCTION_ARGS)
221 : : {
222 : 6 : Point *point = PG_GETARG_POINT_P(0);
223 : 6 : WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(1);
224 : : float8 distance;
225 : :
2596 tomas.vondra@postgre 226 : 6 : distance = DatumGetFloat8(DirectFunctionCall2(point_distance,
227 : : PointPGetDatum(point),
228 : : PointPGetDatum(&widget->center)));
229 : :
230 : 6 : PG_RETURN_BOOL(distance < widget->radius);
231 : : }
232 : :
3083 andres@anarazel.de 233 : 7 : PG_FUNCTION_INFO_V1(reverse_name);
234 : :
235 : : Datum
236 : 24 : reverse_name(PG_FUNCTION_ARGS)
237 : : {
238 : 24 : char *string = PG_GETARG_CSTRING(0);
239 : : int i;
240 : : int len;
241 : : char *new_string;
242 : :
8077 tgl@sss.pgh.pa.us 243 : 24 : new_string = palloc0(NAMEDATALEN);
9910 bruce@momjian.us 244 [ + - + + ]: 168 : for (i = 0; i < NAMEDATALEN && string[i]; ++i)
245 : : ;
246 [ + - + - ]: 24 : if (i == NAMEDATALEN || !string[i])
10226 247 : 24 : --i;
248 : 24 : len = i;
249 [ + + ]: 168 : for (; i >= 0; --i)
250 : 144 : new_string[len - i] = string[i];
3083 andres@anarazel.de 251 : 24 : PG_RETURN_CSTRING(new_string);
252 : : }
253 : :
2748 tgl@sss.pgh.pa.us 254 : 7 : PG_FUNCTION_INFO_V1(trigger_return_old);
255 : :
256 : : Datum
257 : 45 : trigger_return_old(PG_FUNCTION_ARGS)
258 : : {
259 : 45 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
260 : : HeapTuple tuple;
261 : :
262 [ + - - + ]: 45 : if (!CALLED_AS_TRIGGER(fcinfo))
2748 tgl@sss.pgh.pa.us 263 [ # # ]:UBC 0 : elog(ERROR, "trigger_return_old: not fired by trigger manager");
264 : :
2748 tgl@sss.pgh.pa.us 265 :CBC 45 : tuple = trigdata->tg_trigtuple;
266 : :
267 : 45 : return PointerGetDatum(tuple);
268 : : }
269 : :
270 : :
271 : : /*
272 : : * Type int44 has no real-world use, but the regression tests use it
273 : : * (under the alias "city_budget"). It's a four-element vector of int4's.
274 : : */
275 : :
276 : : /*
277 : : * int44in - converts "num, num, ..." to internal form
278 : : *
279 : : * Note: Fills any missing positions with zeroes.
280 : : */
281 : 7 : PG_FUNCTION_INFO_V1(int44in);
282 : :
283 : : Datum
284 : 6 : int44in(PG_FUNCTION_ARGS)
285 : : {
8416 286 : 6 : char *input_string = PG_GETARG_CSTRING(0);
287 : 6 : int32 *result = (int32 *) palloc(4 * sizeof(int32));
288 : : int i;
289 : :
290 : 6 : i = sscanf(input_string,
291 : : "%d, %d, %d, %d",
292 : : &result[0],
293 : : &result[1],
294 : : &result[2],
295 : : &result[3]);
296 [ + + ]: 9 : while (i < 4)
297 : 3 : result[i++] = 0;
298 : :
299 : 6 : PG_RETURN_POINTER(result);
300 : : }
301 : :
302 : : /*
303 : : * int44out - converts internal form to "num, num, ..."
304 : : */
2748 305 : 11 : PG_FUNCTION_INFO_V1(int44out);
306 : :
307 : : Datum
308 : 14 : int44out(PG_FUNCTION_ARGS)
309 : : {
8416 310 : 14 : int32 *an_array = (int32 *) PG_GETARG_POINTER(0);
2748 311 : 14 : char *result = (char *) palloc(16 * 4);
312 : :
313 : 14 : snprintf(result, 16 * 4, "%d,%d,%d,%d",
314 : : an_array[0],
315 : 14 : an_array[1],
316 : 14 : an_array[2],
317 : 14 : an_array[3]);
318 : :
8416 319 : 14 : PG_RETURN_CSTRING(result);
320 : : }
321 : :
1314 322 : 7 : PG_FUNCTION_INFO_V1(test_canonicalize_path);
323 : : Datum
324 : 66 : test_canonicalize_path(PG_FUNCTION_ARGS)
325 : : {
326 : 66 : char *path = text_to_cstring(PG_GETARG_TEXT_PP(0));
327 : :
328 : 66 : canonicalize_path(path);
329 : 66 : PG_RETURN_TEXT_P(cstring_to_text(path));
330 : : }
331 : :
4449 rhaas@postgresql.org 332 : 7 : PG_FUNCTION_INFO_V1(make_tuple_indirect);
333 : : Datum
334 : 63 : make_tuple_indirect(PG_FUNCTION_ARGS)
335 : : {
336 : 63 : HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
337 : : HeapTupleData tuple;
338 : : int ncolumns;
339 : : Datum *values;
340 : : bool *nulls;
341 : :
342 : : Oid tupType;
343 : : int32 tupTypmod;
344 : : TupleDesc tupdesc;
345 : :
346 : : HeapTuple newtup;
347 : :
348 : : int i;
349 : :
350 : : MemoryContext old_context;
351 : :
352 : : /* Extract type info from the tuple itself */
353 : 63 : tupType = HeapTupleHeaderGetTypeId(rec);
354 : 63 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
355 : 63 : tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
356 : 63 : ncolumns = tupdesc->natts;
357 : :
358 : : /* Build a temporary HeapTuple control structure */
359 : 63 : tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
360 : 63 : ItemPointerSetInvalid(&(tuple.t_self));
361 : 63 : tuple.t_tableOid = InvalidOid;
362 : 63 : tuple.t_data = rec;
363 : :
364 : 63 : values = (Datum *) palloc(ncolumns * sizeof(Datum));
365 : 63 : nulls = (bool *) palloc(ncolumns * sizeof(bool));
366 : :
367 : 63 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
368 : :
369 : 63 : old_context = MemoryContextSwitchTo(TopTransactionContext);
370 : :
371 [ + + ]: 315 : for (i = 0; i < ncolumns; i++)
372 : : {
373 : : struct varlena *attr;
374 : : struct varlena *new_attr;
375 : : struct varatt_indirect redirect_pointer;
376 : :
377 : : /* only work on existing, not-null varlenas */
2939 andres@anarazel.de 378 [ + - ]: 252 : if (TupleDescAttr(tupdesc, i)->attisdropped ||
4449 rhaas@postgresql.org 379 [ + + ]: 252 : nulls[i] ||
446 michael@paquier.xyz 380 [ + + ]: 219 : TupleDescAttr(tupdesc, i)->attlen != -1 ||
381 [ - + ]: 156 : TupleDescAttr(tupdesc, i)->attstorage == TYPSTORAGE_PLAIN)
4449 rhaas@postgresql.org 382 : 96 : continue;
383 : :
384 : 156 : attr = (struct varlena *) DatumGetPointer(values[i]);
385 : :
386 : : /* don't recursively indirect */
387 [ - + - - ]: 156 : if (VARATT_IS_EXTERNAL_INDIRECT(attr))
4449 rhaas@postgresql.org 388 :UBC 0 : continue;
389 : :
390 : : /* copy datum, so it still lives later */
4449 rhaas@postgresql.org 391 [ - + - - ]:CBC 156 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
2164 rhaas@postgresql.org 392 :UBC 0 : attr = detoast_external_attr(attr);
393 : : else
394 : : {
4449 rhaas@postgresql.org 395 :CBC 156 : struct varlena *oldattr = attr;
396 : :
397 [ - + - - : 156 : attr = palloc0(VARSIZE_ANY(oldattr));
- - - - +
+ ]
398 [ - + - - : 156 : memcpy(attr, oldattr, VARSIZE_ANY(oldattr));
- - - - +
+ ]
399 : : }
400 : :
401 : : /* build indirection Datum */
402 : 156 : new_attr = (struct varlena *) palloc0(INDIRECT_POINTER_SIZE);
403 : 156 : redirect_pointer.pointer = attr;
404 : 156 : SET_VARTAG_EXTERNAL(new_attr, VARTAG_INDIRECT);
405 : 156 : memcpy(VARDATA_EXTERNAL(new_attr), &redirect_pointer,
406 : : sizeof(redirect_pointer));
407 : :
408 : 156 : values[i] = PointerGetDatum(new_attr);
409 : : }
410 : :
411 : 63 : newtup = heap_form_tuple(tupdesc, values, nulls);
412 : 63 : pfree(values);
413 : 63 : pfree(nulls);
414 [ + - ]: 63 : ReleaseTupleDesc(tupdesc);
415 : :
416 : 63 : MemoryContextSwitchTo(old_context);
417 : :
418 : : /*
419 : : * We intentionally don't use PG_RETURN_HEAPTUPLEHEADER here, because that
420 : : * would cause the indirect toast pointers to be flattened out of the
421 : : * tuple immediately, rendering subsequent testing irrelevant. So just
422 : : * return the HeapTupleHeader pointer as-is. This violates the general
423 : : * rule that composite Datums shouldn't contain toast pointers, but so
424 : : * long as the regression test scripts don't insert the result of this
425 : : * function into a container type (record, array, etc) it should be OK.
426 : : */
4146 tgl@sss.pgh.pa.us 427 : 63 : PG_RETURN_POINTER(newtup->t_data);
428 : : }
429 : :
299 noah@leadboat.com 430 : 2 : PG_FUNCTION_INFO_V1(get_environ);
431 : :
432 : : Datum
433 : 1 : get_environ(PG_FUNCTION_ARGS)
434 : : {
435 : : #if !defined(WIN32) || defined(_MSC_VER)
436 : : extern char **environ;
437 : : #endif
438 : 1 : int nvals = 0;
439 : : ArrayType *result;
440 : : Datum *env;
441 : :
442 [ + + ]: 140 : for (char **s = environ; *s; s++)
443 : 139 : nvals++;
444 : :
445 : 1 : env = palloc(nvals * sizeof(Datum));
446 : :
447 [ + + ]: 140 : for (int i = 0; i < nvals; i++)
448 : 139 : env[i] = CStringGetTextDatum(environ[i]);
449 : :
450 : 1 : result = construct_array_builtin(env, nvals, TEXTOID);
451 : :
452 : 1 : PG_RETURN_POINTER(result);
453 : : }
454 : :
1711 tgl@sss.pgh.pa.us 455 : 2 : PG_FUNCTION_INFO_V1(regress_setenv);
456 : :
457 : : Datum
458 : 1 : regress_setenv(PG_FUNCTION_ARGS)
459 : : {
460 : 1 : char *envvar = text_to_cstring(PG_GETARG_TEXT_PP(0));
461 : 1 : char *envval = text_to_cstring(PG_GETARG_TEXT_PP(1));
462 : :
4064 noah@leadboat.com 463 [ - + ]: 1 : if (!superuser())
4064 noah@leadboat.com 464 [ # # ]:UBC 0 : elog(ERROR, "must be superuser to change environment variables");
465 : :
1711 tgl@sss.pgh.pa.us 466 [ - + ]:CBC 1 : if (setenv(envvar, envval, 1) != 0)
4064 noah@leadboat.com 467 [ # # ]:UBC 0 : elog(ERROR, "could not set environment variable: %m");
468 : :
4064 noah@leadboat.com 469 :CBC 1 : PG_RETURN_VOID();
470 : : }
471 : :
472 : : /* Sleep until no process has a given PID. */
473 : 3 : PG_FUNCTION_INFO_V1(wait_pid);
474 : :
475 : : Datum
476 : 1 : wait_pid(PG_FUNCTION_ARGS)
477 : : {
478 : 1 : int pid = PG_GETARG_INT32(0);
479 : :
480 [ - + ]: 1 : if (!superuser())
4064 noah@leadboat.com 481 [ # # ]:UBC 0 : elog(ERROR, "must be superuser to check PID liveness");
482 : :
4064 noah@leadboat.com 483 [ + + ]:CBC 5 : while (kill(pid, 0) == 0)
484 : : {
3836 485 [ - + ]: 4 : CHECK_FOR_INTERRUPTS();
4064 486 : 4 : pg_usleep(50000);
487 : : }
488 : :
489 [ - + ]: 1 : if (errno != ESRCH)
4064 noah@leadboat.com 490 [ # # ]:UBC 0 : elog(ERROR, "could not check PID %d liveness: %m", pid);
491 : :
4064 noah@leadboat.com 492 :CBC 1 : PG_RETURN_VOID();
493 : : }
494 : :
495 : : static void
3999 andres@anarazel.de 496 : 3 : test_atomic_flag(void)
497 : : {
498 : : pg_atomic_flag flag;
499 : :
500 : 3 : pg_atomic_init_flag(&flag);
2163 noah@leadboat.com 501 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag));
502 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_test_set_flag(&flag));
503 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_unlocked_test_flag(&flag));
504 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_test_set_flag(&flag));
3999 andres@anarazel.de 505 : 3 : pg_atomic_clear_flag(&flag);
2163 noah@leadboat.com 506 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_unlocked_test_flag(&flag));
507 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_test_set_flag(&flag));
3999 andres@anarazel.de 508 : 3 : pg_atomic_clear_flag(&flag);
509 : 3 : }
510 : :
511 : : static void
512 : 3 : test_atomic_uint32(void)
513 : : {
514 : : pg_atomic_uint32 var;
515 : : uint32 expected;
516 : : int i;
517 : :
518 : 3 : pg_atomic_init_u32(&var, 0);
2163 noah@leadboat.com 519 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 0);
3999 andres@anarazel.de 520 : 3 : pg_atomic_write_u32(&var, 3);
2163 noah@leadboat.com 521 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 3);
522 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, pg_atomic_read_u32(&var) - 2),
523 : : 3);
524 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var, 1), 4);
525 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var, 3), 0);
526 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_add_fetch_u32(&var, 10), 10);
527 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_exchange_u32(&var, 5), 10);
528 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_exchange_u32(&var, 0), 5);
529 : :
530 : : /* test around numerical limits */
531 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, INT_MAX), 0);
532 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, INT_MAX), INT_MAX);
2185 533 : 3 : pg_atomic_fetch_add_u32(&var, 2); /* wrap to 0 */
2163 534 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MAX), 0);
535 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MAX + 1),
536 : : PG_INT16_MAX);
537 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MIN),
538 : : 2 * PG_INT16_MAX + 1);
539 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_add_u32(&var, PG_INT16_MIN - 1),
540 : : PG_INT16_MAX);
3759 bruce@momjian.us 541 : 3 : pg_atomic_fetch_add_u32(&var, 1); /* top up to UINT_MAX */
2163 noah@leadboat.com 542 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), UINT_MAX);
543 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_sub_u32(&var, INT_MAX), UINT_MAX);
544 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), (uint32) INT_MAX + 1);
545 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_sub_fetch_u32(&var, INT_MAX), 1);
3999 andres@anarazel.de 546 : 3 : pg_atomic_sub_fetch_u32(&var, 1);
1791 noah@leadboat.com 547 : 3 : expected = PG_INT16_MAX;
548 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
549 : 3 : expected = PG_INT16_MAX + 1;
550 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
551 : 3 : expected = PG_INT16_MIN;
552 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
553 : 3 : expected = PG_INT16_MIN - 1;
554 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
555 : :
556 : : /* fail exchange because of old expected */
3999 andres@anarazel.de 557 : 3 : expected = 10;
2163 noah@leadboat.com 558 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u32(&var, &expected, 1));
559 : :
560 : : /* CAS is allowed to fail due to interrupts, try a couple of times */
3999 andres@anarazel.de 561 [ + - ]: 6 : for (i = 0; i < 1000; i++)
562 : : {
563 : 6 : expected = 0;
564 [ + + ]: 6 : if (!pg_atomic_compare_exchange_u32(&var, &expected, 1))
565 : 3 : break;
566 : : }
567 [ - + ]: 3 : if (i == 1000)
3999 andres@anarazel.de 568 [ # # ]:UBC 0 : elog(ERROR, "atomic_compare_exchange_u32() never succeeded");
2163 noah@leadboat.com 569 [ - + - - ]:CBC 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 1);
3999 andres@anarazel.de 570 : 3 : pg_atomic_write_u32(&var, 0);
571 : :
572 : : /* try setting flagbits */
2163 noah@leadboat.com 573 [ - + - - ]: 3 : EXPECT_TRUE(!(pg_atomic_fetch_or_u32(&var, 1) & 1));
574 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_fetch_or_u32(&var, 2) & 1);
575 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_read_u32(&var), 3);
576 : : /* try clearing flagbits */
577 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~2) & 3, 3);
578 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~1), 1);
579 : : /* no bits set anymore */
580 [ - + - - ]: 3 : EXPECT_EQ_U32(pg_atomic_fetch_and_u32(&var, ~0), 0);
3999 andres@anarazel.de 581 : 3 : }
582 : :
583 : : static void
584 : 3 : test_atomic_uint64(void)
585 : : {
586 : : pg_atomic_uint64 var;
587 : : uint64 expected;
588 : : int i;
589 : :
590 : 3 : pg_atomic_init_u64(&var, 0);
2163 noah@leadboat.com 591 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 0);
3999 andres@anarazel.de 592 : 3 : pg_atomic_write_u64(&var, 3);
2163 noah@leadboat.com 593 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 3);
594 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_fetch_add_u64(&var, pg_atomic_read_u64(&var) - 2),
595 : : 3);
596 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_fetch_sub_u64(&var, 1), 4);
597 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_sub_fetch_u64(&var, 3), 0);
598 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_add_fetch_u64(&var, 10), 10);
599 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_exchange_u64(&var, 5), 10);
600 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_exchange_u64(&var, 0), 5);
601 : :
602 : : /* fail exchange because of old expected */
3999 andres@anarazel.de 603 : 3 : expected = 10;
2163 noah@leadboat.com 604 [ - + - - ]: 3 : EXPECT_TRUE(!pg_atomic_compare_exchange_u64(&var, &expected, 1));
605 : :
606 : : /* CAS is allowed to fail due to interrupts, try a couple of times */
3999 andres@anarazel.de 607 [ + - ]: 6 : for (i = 0; i < 100; i++)
608 : : {
609 : 6 : expected = 0;
610 [ + + ]: 6 : if (!pg_atomic_compare_exchange_u64(&var, &expected, 1))
611 : 3 : break;
612 : : }
613 [ - + ]: 3 : if (i == 100)
3999 andres@anarazel.de 614 [ # # ]:UBC 0 : elog(ERROR, "atomic_compare_exchange_u64() never succeeded");
2163 noah@leadboat.com 615 [ - + - - ]:CBC 3 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 1);
616 : :
3999 andres@anarazel.de 617 : 3 : pg_atomic_write_u64(&var, 0);
618 : :
619 : : /* try setting flagbits */
2163 noah@leadboat.com 620 [ - + - - ]: 3 : EXPECT_TRUE(!(pg_atomic_fetch_or_u64(&var, 1) & 1));
621 [ - + - - ]: 3 : EXPECT_TRUE(pg_atomic_fetch_or_u64(&var, 2) & 1);
622 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_read_u64(&var), 3);
623 : : /* try clearing flagbits */
624 [ - + - - ]: 3 : EXPECT_EQ_U64((pg_atomic_fetch_and_u64(&var, ~2) & 3), 3);
625 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var, ~1), 1);
626 : : /* no bits set anymore */
627 [ - + - - ]: 3 : EXPECT_EQ_U64(pg_atomic_fetch_and_u64(&var, ~0), 0);
3999 andres@anarazel.de 628 : 3 : }
629 : :
630 : : /*
631 : : * Perform, fairly minimal, testing of the spinlock implementation.
632 : : *
633 : : * It's likely worth expanding these to actually test concurrency etc, but
634 : : * having some regularly run tests is better than none.
635 : : */
636 : : static void
1916 637 : 3 : test_spinlock(void)
638 : : {
639 : : /*
640 : : * Basic tests for spinlocks, as well as the underlying operations.
641 : : *
642 : : * We embed the spinlock in a struct with other members to test that the
643 : : * spinlock operations don't perform too wide writes.
644 : : */
645 : : {
646 : : struct test_lock_struct
647 : : {
648 : : char data_before[4];
649 : : slock_t lock;
650 : : char data_after[4];
651 : : } struct_w_lock;
652 : :
653 : 3 : memcpy(struct_w_lock.data_before, "abcd", 4);
654 : 3 : memcpy(struct_w_lock.data_after, "ef12", 4);
655 : :
656 : : /* test basic operations via the SpinLock* API */
657 : 3 : SpinLockInit(&struct_w_lock.lock);
658 [ - + ]: 3 : SpinLockAcquire(&struct_w_lock.lock);
659 : 3 : SpinLockRelease(&struct_w_lock.lock);
660 : :
661 : : /* test basic operations via underlying S_* API */
662 : 3 : S_INIT_LOCK(&struct_w_lock.lock);
663 [ - + ]: 3 : S_LOCK(&struct_w_lock.lock);
664 : 3 : S_UNLOCK(&struct_w_lock.lock);
665 : :
666 : : /* and that "contended" acquisition works */
667 : 3 : s_lock(&struct_w_lock.lock, "testfile", 17, "testfunc");
668 : 3 : S_UNLOCK(&struct_w_lock.lock);
669 : :
670 : : /*
671 : : * Check, using TAS directly, that a single spin cycle doesn't block
672 : : * when acquiring an already acquired lock.
673 : : */
674 : : #ifdef TAS
675 [ - + ]: 3 : S_LOCK(&struct_w_lock.lock);
676 : :
677 [ - + ]: 3 : if (!TAS(&struct_w_lock.lock))
1916 andres@anarazel.de 678 [ # # ]:UBC 0 : elog(ERROR, "acquired already held spinlock");
679 : :
680 : : #ifdef TAS_SPIN
1916 andres@anarazel.de 681 [ - + - - ]:CBC 3 : if (!TAS_SPIN(&struct_w_lock.lock))
1916 andres@anarazel.de 682 [ # # ]:UBC 0 : elog(ERROR, "acquired already held spinlock");
683 : : #endif /* defined(TAS_SPIN) */
684 : :
1916 andres@anarazel.de 685 :CBC 3 : S_UNLOCK(&struct_w_lock.lock);
686 : : #endif /* defined(TAS) */
687 : :
688 : : /*
689 : : * Verify that after all of this the non-lock contents are still
690 : : * correct.
691 : : */
692 [ - + ]: 3 : if (memcmp(struct_w_lock.data_before, "abcd", 4) != 0)
1916 andres@anarazel.de 693 [ # # ]:UBC 0 : elog(ERROR, "padding before spinlock modified");
1916 andres@anarazel.de 694 [ - + ]:CBC 3 : if (memcmp(struct_w_lock.data_after, "ef12", 4) != 0)
1916 andres@anarazel.de 695 [ # # ]:UBC 0 : elog(ERROR, "padding after spinlock modified");
696 : : }
1916 andres@anarazel.de 697 :CBC 3 : }
698 : :
3999 699 : 7 : PG_FUNCTION_INFO_V1(test_atomic_ops);
700 : : Datum
701 : 3 : test_atomic_ops(PG_FUNCTION_ARGS)
702 : : {
703 : 3 : test_atomic_flag();
704 : :
705 : 3 : test_atomic_uint32();
706 : :
707 : 3 : test_atomic_uint64();
708 : :
709 : : /*
710 : : * Arguably this shouldn't be tested as part of this function, but it's
711 : : * closely enough related that that seems ok for now.
712 : : */
1916 713 : 3 : test_spinlock();
714 : :
3999 715 : 3 : PG_RETURN_BOOL(true);
716 : : }
717 : :
2913 rhaas@postgresql.org 718 : 4 : PG_FUNCTION_INFO_V1(test_fdw_handler);
719 : : Datum
2913 rhaas@postgresql.org 720 :UBC 0 : test_fdw_handler(PG_FUNCTION_ARGS)
721 : : {
2748 tgl@sss.pgh.pa.us 722 [ # # ]: 0 : elog(ERROR, "test_fdw_handler is not implemented");
723 : : PG_RETURN_NULL();
724 : : }
725 : :
142 noah@leadboat.com 726 :CBC 7 : PG_FUNCTION_INFO_V1(is_catalog_text_unique_index_oid);
727 : : Datum
728 : 614 : is_catalog_text_unique_index_oid(PG_FUNCTION_ARGS)
729 : : {
29 peter@eisentraut.org 730 :GNC 614 : return BoolGetDatum(IsCatalogTextUniqueIndexOid(PG_GETARG_OID(0)));
731 : : }
732 : :
2401 tgl@sss.pgh.pa.us 733 :CBC 7 : PG_FUNCTION_INFO_V1(test_support_func);
734 : : Datum
735 : 30 : test_support_func(PG_FUNCTION_ARGS)
736 : : {
737 : 30 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
738 : 30 : Node *ret = NULL;
739 : :
740 [ + + ]: 30 : if (IsA(rawreq, SupportRequestSelectivity))
741 : : {
742 : : /*
743 : : * Assume that the target is int4eq; that's safe as long as we don't
744 : : * attach this to any other boolean-returning function.
745 : : */
746 : 3 : SupportRequestSelectivity *req = (SupportRequestSelectivity *) rawreq;
747 : : Selectivity s1;
748 : :
749 [ - + ]: 3 : if (req->is_join)
2401 tgl@sss.pgh.pa.us 750 :UBC 0 : s1 = join_selectivity(req->root, Int4EqualOperator,
751 : : req->args,
752 : : req->inputcollid,
753 : : req->jointype,
754 : 0 : req->sjinfo);
755 : : else
2401 tgl@sss.pgh.pa.us 756 :CBC 3 : s1 = restriction_selectivity(req->root, Int4EqualOperator,
757 : : req->args,
758 : : req->inputcollid,
759 : : req->varRelid);
760 : :
761 : 3 : req->selectivity = s1;
762 : 3 : ret = (Node *) req;
763 : : }
764 : :
765 [ + + ]: 30 : if (IsA(rawreq, SupportRequestCost))
766 : : {
767 : : /* Provide some generic estimate */
768 : 9 : SupportRequestCost *req = (SupportRequestCost *) rawreq;
769 : :
770 : 9 : req->startup = 0;
771 : 9 : req->per_tuple = 2 * cpu_operator_cost;
772 : 9 : ret = (Node *) req;
773 : : }
774 : :
775 [ + + ]: 30 : if (IsA(rawreq, SupportRequestRows))
776 : : {
777 : : /*
778 : : * Assume that the target is generate_series_int4; that's safe as long
779 : : * as we don't attach this to any other set-returning function.
780 : : */
781 : 6 : SupportRequestRows *req = (SupportRequestRows *) rawreq;
782 : :
783 [ + - + - ]: 6 : if (req->node && IsA(req->node, FuncExpr)) /* be paranoid */
784 : : {
785 : 6 : List *args = ((FuncExpr *) req->node)->args;
786 : 6 : Node *arg1 = linitial(args);
787 : 6 : Node *arg2 = lsecond(args);
788 : :
789 [ + - ]: 6 : if (IsA(arg1, Const) &&
790 [ + - ]: 6 : !((Const *) arg1)->constisnull &&
791 [ + - ]: 6 : IsA(arg2, Const) &&
792 [ + - ]: 6 : !((Const *) arg2)->constisnull)
793 : : {
794 : 6 : int32 val1 = DatumGetInt32(((Const *) arg1)->constvalue);
795 : 6 : int32 val2 = DatumGetInt32(((Const *) arg2)->constvalue);
796 : :
797 : 6 : req->rows = val2 - val1 + 1;
798 : 6 : ret = (Node *) req;
799 : : }
800 : : }
801 : : }
802 : :
803 : 30 : PG_RETURN_POINTER(ret);
804 : : }
805 : :
1986 akorotkov@postgresql 806 : 4 : PG_FUNCTION_INFO_V1(test_opclass_options_func);
807 : : Datum
1986 akorotkov@postgresql 808 :UBC 0 : test_opclass_options_func(PG_FUNCTION_ARGS)
809 : : {
810 : 0 : PG_RETURN_NULL();
811 : : }
812 : :
813 : : /* one-time tests for encoding infrastructure */
208 andres@anarazel.de 814 :CBC 7 : PG_FUNCTION_INFO_V1(test_enc_setup);
815 : : Datum
816 : 3 : test_enc_setup(PG_FUNCTION_ARGS)
817 : : {
818 : : /* Test pg_encoding_set_invalid() */
819 [ + + ]: 129 : for (int i = 0; i < _PG_LAST_ENCODING_; i++)
820 : : {
821 : : char buf[2],
822 : : bigbuf[16];
823 : : int len,
824 : : mblen,
825 : : valid;
826 : :
827 [ + + ]: 126 : if (pg_encoding_max_length(i) == 1)
828 : 84 : continue;
829 : 42 : pg_encoding_set_invalid(i, buf);
830 : 42 : len = strnlen(buf, 2);
831 [ - + ]: 42 : if (len != 2)
208 andres@anarazel.de 832 [ # # ]:UBC 0 : elog(WARNING,
833 : : "official invalid string for encoding \"%s\" has length %d",
834 : : pg_enc2name_tbl[i].name, len);
208 andres@anarazel.de 835 :CBC 42 : mblen = pg_encoding_mblen(i, buf);
836 [ - + ]: 42 : if (mblen != 2)
208 andres@anarazel.de 837 [ # # ]:UBC 0 : elog(WARNING,
838 : : "official invalid string for encoding \"%s\" has mblen %d",
839 : : pg_enc2name_tbl[i].name, mblen);
208 andres@anarazel.de 840 :CBC 42 : valid = pg_encoding_verifymbstr(i, buf, len);
841 [ - + ]: 42 : if (valid != 0)
208 andres@anarazel.de 842 [ # # ]:UBC 0 : elog(WARNING,
843 : : "official invalid string for encoding \"%s\" has valid prefix of length %d",
844 : : pg_enc2name_tbl[i].name, valid);
208 andres@anarazel.de 845 :CBC 42 : valid = pg_encoding_verifymbstr(i, buf, 1);
846 [ - + ]: 42 : if (valid != 0)
208 andres@anarazel.de 847 [ # # ]:UBC 0 : elog(WARNING,
848 : : "first byte of official invalid string for encoding \"%s\" has valid prefix of length %d",
849 : : pg_enc2name_tbl[i].name, valid);
208 andres@anarazel.de 850 :CBC 42 : memset(bigbuf, ' ', sizeof(bigbuf));
851 : 42 : bigbuf[0] = buf[0];
852 : 42 : bigbuf[1] = buf[1];
853 : 42 : valid = pg_encoding_verifymbstr(i, bigbuf, sizeof(bigbuf));
854 [ - + ]: 42 : if (valid != 0)
208 andres@anarazel.de 855 [ # # ]:UBC 0 : elog(WARNING,
856 : : "trailing data changed official invalid string for encoding \"%s\" to have valid prefix of length %d",
857 : : pg_enc2name_tbl[i].name, valid);
858 : : }
859 : :
208 andres@anarazel.de 860 :CBC 3 : PG_RETURN_VOID();
861 : : }
862 : :
863 : : /*
864 : : * Call an encoding conversion or verification function.
865 : : *
866 : : * Arguments:
867 : : * string bytea -- string to convert
868 : : * src_enc name -- source encoding
869 : : * dest_enc name -- destination encoding
870 : : * noError bool -- if set, don't ereport() on invalid or untranslatable
871 : : * input
872 : : *
873 : : * Result is a tuple with two attributes:
874 : : * int4 -- number of input bytes successfully converted
875 : : * bytea -- converted string
876 : : */
1619 heikki.linnakangas@i 877 : 7 : PG_FUNCTION_INFO_V1(test_enc_conversion);
878 : : Datum
879 : 4935 : test_enc_conversion(PG_FUNCTION_ARGS)
880 : : {
881 : 4935 : bytea *string = PG_GETARG_BYTEA_PP(0);
882 : 4935 : char *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
883 : 4935 : int src_encoding = pg_char_to_encoding(src_encoding_name);
884 : 4935 : char *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
885 : 4935 : int dest_encoding = pg_char_to_encoding(dest_encoding_name);
886 : 4935 : bool noError = PG_GETARG_BOOL(3);
887 : : TupleDesc tupdesc;
888 : : char *src;
889 : : char *dst;
890 : : bytea *retval;
891 : : Size srclen;
892 : : Size dstsize;
893 : : Oid proc;
894 : : int convertedbytes;
895 : : int dstlen;
896 : : Datum values[2];
1148 peter@eisentraut.org 897 : 4935 : bool nulls[2] = {0};
898 : : HeapTuple tuple;
899 : :
1619 heikki.linnakangas@i 900 [ - + ]: 4935 : if (src_encoding < 0)
1619 heikki.linnakangas@i 901 [ # # ]:UBC 0 : ereport(ERROR,
902 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
903 : : errmsg("invalid source encoding name \"%s\"",
904 : : src_encoding_name)));
1619 heikki.linnakangas@i 905 [ - + ]:CBC 4935 : if (dest_encoding < 0)
1619 heikki.linnakangas@i 906 [ # # ]:UBC 0 : ereport(ERROR,
907 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
908 : : errmsg("invalid destination encoding name \"%s\"",
909 : : dest_encoding_name)));
910 : :
911 : : /* Build a tuple descriptor for our result type */
1619 heikki.linnakangas@i 912 [ - + ]:CBC 4935 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1619 heikki.linnakangas@i 913 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
1619 heikki.linnakangas@i 914 :CBC 4935 : tupdesc = BlessTupleDesc(tupdesc);
915 : :
916 [ - + - - : 4935 : srclen = VARSIZE_ANY_EXHDR(string);
- - - - +
+ ]
917 [ + + ]: 4935 : src = VARDATA_ANY(string);
918 : :
919 [ + + ]: 4935 : if (src_encoding == dest_encoding)
920 : : {
921 : : /* just check that the source string is valid */
922 : : int oklen;
923 : :
924 : 2064 : oklen = pg_encoding_verifymbstr(src_encoding, src, srclen);
925 : :
926 [ + + ]: 2064 : if (oklen == srclen)
927 : : {
928 : 516 : convertedbytes = oklen;
929 : 516 : retval = string;
930 : : }
931 [ + + ]: 1548 : else if (!noError)
932 : : {
933 : 774 : report_invalid_encoding(src_encoding, src + oklen, srclen - oklen);
934 : : }
935 : : else
936 : : {
937 : : /*
938 : : * build bytea data type structure.
939 : : */
940 [ - + ]: 774 : Assert(oklen < srclen);
941 : 774 : convertedbytes = oklen;
942 : 774 : retval = (bytea *) palloc(oklen + VARHDRSZ);
943 : 774 : SET_VARSIZE(retval, oklen + VARHDRSZ);
944 : 774 : memcpy(VARDATA(retval), src, oklen);
945 : : }
946 : : }
947 : : else
948 : : {
949 : 2871 : proc = FindDefaultConversionProc(src_encoding, dest_encoding);
950 [ - + ]: 2871 : if (!OidIsValid(proc))
1619 heikki.linnakangas@i 951 [ # # ]:UBC 0 : ereport(ERROR,
952 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
953 : : errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
954 : : pg_encoding_to_char(src_encoding),
955 : : pg_encoding_to_char(dest_encoding))));
956 : :
1619 heikki.linnakangas@i 957 [ - + ]:CBC 2871 : if (srclen >= (MaxAllocSize / (Size) MAX_CONVERSION_GROWTH))
1619 heikki.linnakangas@i 958 [ # # ]:UBC 0 : ereport(ERROR,
959 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
960 : : errmsg("out of memory"),
961 : : errdetail("String of %d bytes is too long for encoding conversion.",
962 : : (int) srclen)));
963 : :
1619 heikki.linnakangas@i 964 :CBC 2871 : dstsize = (Size) srclen * MAX_CONVERSION_GROWTH + 1;
965 : 2871 : dst = MemoryContextAlloc(CurrentMemoryContext, dstsize);
966 : :
967 : : /* perform conversion */
968 : 2871 : convertedbytes = pg_do_encoding_conversion_buf(proc,
969 : : src_encoding,
970 : : dest_encoding,
971 : : (unsigned char *) src, srclen,
972 : : (unsigned char *) dst, dstsize,
973 : : noError);
974 : 1692 : dstlen = strlen(dst);
975 : :
976 : : /*
977 : : * build bytea data type structure.
978 : : */
979 : 1692 : retval = (bytea *) palloc(dstlen + VARHDRSZ);
980 : 1692 : SET_VARSIZE(retval, dstlen + VARHDRSZ);
981 : 1692 : memcpy(VARDATA(retval), dst, dstlen);
982 : :
983 : 1692 : pfree(dst);
984 : : }
985 : :
986 : 2982 : values[0] = Int32GetDatum(convertedbytes);
987 : 2982 : values[1] = PointerGetDatum(retval);
988 : 2982 : tuple = heap_form_tuple(tupdesc, values, nulls);
989 : :
990 : 2982 : PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
991 : : }
992 : :
993 : : /* Provide SQL access to IsBinaryCoercible() */
1579 tgl@sss.pgh.pa.us 994 : 7 : PG_FUNCTION_INFO_V1(binary_coercible);
995 : : Datum
996 : 18798 : binary_coercible(PG_FUNCTION_ARGS)
997 : : {
998 : 18798 : Oid srctype = PG_GETARG_OID(0);
999 : 18798 : Oid targettype = PG_GETARG_OID(1);
1000 : :
1001 : 18798 : PG_RETURN_BOOL(IsBinaryCoercible(srctype, targettype));
1002 : : }
1003 : :
1004 : : /*
1005 : : * Sanity checks for functions in relpath.h
1006 : : */
193 andres@anarazel.de 1007 : 7 : PG_FUNCTION_INFO_V1(test_relpath);
1008 : : Datum
1009 : 3 : test_relpath(PG_FUNCTION_ARGS)
1010 : : {
1011 : : RelPathStr rpath;
1012 : :
1013 : : /*
1014 : : * Verify that PROCNUMBER_CHARS and MAX_BACKENDS stay in sync.
1015 : : * Unfortunately I don't know how to express that in a way suitable for a
1016 : : * static assert.
1017 : : */
1018 : : if ((int) ceil(log10(MAX_BACKENDS)) != PROCNUMBER_CHARS)
1019 : : elog(WARNING, "mismatch between MAX_BACKENDS and PROCNUMBER_CHARS");
1020 : :
1021 : : /* verify that the max-length relpath is generated ok */
1022 : 3 : rpath = GetRelationPath(OID_MAX, OID_MAX, OID_MAX, MAX_BACKENDS - 1,
1023 : : INIT_FORKNUM);
1024 : :
1025 [ - + ]: 3 : if (strlen(rpath.str) != REL_PATH_STR_MAXLEN)
193 andres@anarazel.de 1026 [ # # ]:UBC 0 : elog(WARNING, "maximum length relpath is if length %zu instead of %zu",
1027 : : strlen(rpath.str), REL_PATH_STR_MAXLEN);
1028 : :
193 andres@anarazel.de 1029 :CBC 3 : PG_RETURN_VOID();
1030 : : }
|