Line data Source code
1 : /*-------------------------------------------------------------------------
2 : * oracle_compat.c
3 : * Oracle compatible functions.
4 : *
5 : * Copyright (c) 1996-2022, PostgreSQL Global Development Group
6 : *
7 : * Author: Edmund Mergl <E.Mergl@bawue.de>
8 : * Multibyte enhancement: Tatsuo Ishii <ishii@postgresql.org>
9 : *
10 : *
11 : * IDENTIFICATION
12 : * src/backend/utils/adt/oracle_compat.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 : #include "postgres.h"
17 :
18 : #include "common/int.h"
19 : #include "mb/pg_wchar.h"
20 : #include "miscadmin.h"
21 : #include "utils/builtins.h"
22 : #include "utils/formatting.h"
23 :
24 : static text *dotrim(const char *string, int stringlen,
25 : const char *set, int setlen,
26 : bool doltrim, bool dortrim);
27 : static bytea *dobyteatrim(bytea *string, bytea *set,
28 : bool doltrim, bool dortrim);
29 :
30 :
31 : /********************************************************************
32 : *
33 : * lower
34 : *
35 : * Syntax:
36 : *
37 : * text lower(text string)
38 : *
39 : * Purpose:
40 : *
41 : * Returns string, with all letters forced to lowercase.
42 : *
43 : ********************************************************************/
44 :
45 : Datum
46 26598 : lower(PG_FUNCTION_ARGS)
47 : {
48 26598 : text *in_string = PG_GETARG_TEXT_PP(0);
49 : char *out_string;
50 : text *result;
51 :
52 26598 : out_string = str_tolower(VARDATA_ANY(in_string),
53 26598 : VARSIZE_ANY_EXHDR(in_string),
54 : PG_GET_COLLATION());
55 26598 : result = cstring_to_text(out_string);
56 26598 : pfree(out_string);
57 :
58 26598 : PG_RETURN_TEXT_P(result);
59 : }
60 :
61 :
62 : /********************************************************************
63 : *
64 : * upper
65 : *
66 : * Syntax:
67 : *
68 : * text upper(text string)
69 : *
70 : * Purpose:
71 : *
72 : * Returns string, with all letters forced to uppercase.
73 : *
74 : ********************************************************************/
75 :
76 : Datum
77 965498 : upper(PG_FUNCTION_ARGS)
78 : {
79 965498 : text *in_string = PG_GETARG_TEXT_PP(0);
80 : char *out_string;
81 : text *result;
82 :
83 965498 : out_string = str_toupper(VARDATA_ANY(in_string),
84 965498 : VARSIZE_ANY_EXHDR(in_string),
85 : PG_GET_COLLATION());
86 965498 : result = cstring_to_text(out_string);
87 965498 : pfree(out_string);
88 :
89 965498 : PG_RETURN_TEXT_P(result);
90 : }
91 :
92 :
93 : /********************************************************************
94 : *
95 : * initcap
96 : *
97 : * Syntax:
98 : *
99 : * text initcap(text string)
100 : *
101 : * Purpose:
102 : *
103 : * Returns string, with first letter of each word in uppercase, all
104 : * other letters in lowercase. A word is defined as a sequence of
105 : * alphanumeric characters, delimited by non-alphanumeric
106 : * characters.
107 : *
108 : ********************************************************************/
109 :
110 : Datum
111 34 : initcap(PG_FUNCTION_ARGS)
112 : {
113 34 : text *in_string = PG_GETARG_TEXT_PP(0);
114 : char *out_string;
115 : text *result;
116 :
117 34 : out_string = str_initcap(VARDATA_ANY(in_string),
118 34 : VARSIZE_ANY_EXHDR(in_string),
119 : PG_GET_COLLATION());
120 34 : result = cstring_to_text(out_string);
121 34 : pfree(out_string);
122 :
123 34 : PG_RETURN_TEXT_P(result);
124 : }
125 :
126 :
127 : /********************************************************************
128 : *
129 : * lpad
130 : *
131 : * Syntax:
132 : *
133 : * text lpad(text string1, int4 len, text string2)
134 : *
135 : * Purpose:
136 : *
137 : * Returns string1, left-padded to length len with the sequence of
138 : * characters in string2. If len is less than the length of string1,
139 : * instead truncate (on the right) to len.
140 : *
141 : ********************************************************************/
142 :
143 : Datum
144 32770 : lpad(PG_FUNCTION_ARGS)
145 : {
146 32770 : text *string1 = PG_GETARG_TEXT_PP(0);
147 32770 : int32 len = PG_GETARG_INT32(1);
148 32770 : text *string2 = PG_GETARG_TEXT_PP(2);
149 : text *ret;
150 : char *ptr1,
151 : *ptr2,
152 : *ptr2start,
153 : *ptr2end,
154 : *ptr_ret;
155 : int m,
156 : s1len,
157 : s2len;
158 :
159 : int bytelen;
160 :
161 : /* Negative len is silently taken as zero */
162 32770 : if (len < 0)
163 6 : len = 0;
164 :
165 32770 : s1len = VARSIZE_ANY_EXHDR(string1);
166 32770 : if (s1len < 0)
167 0 : s1len = 0; /* shouldn't happen */
168 :
169 32770 : s2len = VARSIZE_ANY_EXHDR(string2);
170 32770 : if (s2len < 0)
171 0 : s2len = 0; /* shouldn't happen */
172 :
173 32770 : s1len = pg_mbstrlen_with_len(VARDATA_ANY(string1), s1len);
174 :
175 32770 : if (s1len > len)
176 70 : s1len = len; /* truncate string1 to len chars */
177 :
178 32770 : if (s2len <= 0)
179 6 : len = s1len; /* nothing to pad with, so don't pad */
180 :
181 32770 : bytelen = pg_database_encoding_max_length() * len;
182 :
183 : /* check for integer overflow */
184 32770 : if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
185 0 : ereport(ERROR,
186 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
187 : errmsg("requested length too large")));
188 :
189 32770 : ret = (text *) palloc(VARHDRSZ + bytelen);
190 :
191 32770 : m = len - s1len;
192 :
193 32770 : ptr2 = ptr2start = VARDATA_ANY(string2);
194 32770 : ptr2end = ptr2 + s2len;
195 32770 : ptr_ret = VARDATA(ret);
196 :
197 34886 : while (m--)
198 : {
199 2116 : int mlen = pg_mblen(ptr2);
200 :
201 2116 : memcpy(ptr_ret, ptr2, mlen);
202 2116 : ptr_ret += mlen;
203 2116 : ptr2 += mlen;
204 2116 : if (ptr2 == ptr2end) /* wrap around at end of s2 */
205 2092 : ptr2 = ptr2start;
206 : }
207 :
208 32770 : ptr1 = VARDATA_ANY(string1);
209 :
210 96242 : while (s1len--)
211 : {
212 63472 : int mlen = pg_mblen(ptr1);
213 :
214 63472 : memcpy(ptr_ret, ptr1, mlen);
215 63472 : ptr_ret += mlen;
216 63472 : ptr1 += mlen;
217 : }
218 :
219 32770 : SET_VARSIZE(ret, ptr_ret - (char *) ret);
220 :
221 32770 : PG_RETURN_TEXT_P(ret);
222 : }
223 :
224 :
225 : /********************************************************************
226 : *
227 : * rpad
228 : *
229 : * Syntax:
230 : *
231 : * text rpad(text string1, int4 len, text string2)
232 : *
233 : * Purpose:
234 : *
235 : * Returns string1, right-padded to length len with the sequence of
236 : * characters in string2. If len is less than the length of string1,
237 : * instead truncate (on the right) to len.
238 : *
239 : ********************************************************************/
240 :
241 : Datum
242 44 : rpad(PG_FUNCTION_ARGS)
243 : {
244 44 : text *string1 = PG_GETARG_TEXT_PP(0);
245 44 : int32 len = PG_GETARG_INT32(1);
246 44 : text *string2 = PG_GETARG_TEXT_PP(2);
247 : text *ret;
248 : char *ptr1,
249 : *ptr2,
250 : *ptr2start,
251 : *ptr2end,
252 : *ptr_ret;
253 : int m,
254 : s1len,
255 : s2len;
256 :
257 : int bytelen;
258 :
259 : /* Negative len is silently taken as zero */
260 44 : if (len < 0)
261 6 : len = 0;
262 :
263 44 : s1len = VARSIZE_ANY_EXHDR(string1);
264 44 : if (s1len < 0)
265 0 : s1len = 0; /* shouldn't happen */
266 :
267 44 : s2len = VARSIZE_ANY_EXHDR(string2);
268 44 : if (s2len < 0)
269 0 : s2len = 0; /* shouldn't happen */
270 :
271 44 : s1len = pg_mbstrlen_with_len(VARDATA_ANY(string1), s1len);
272 :
273 44 : if (s1len > len)
274 12 : s1len = len; /* truncate string1 to len chars */
275 :
276 44 : if (s2len <= 0)
277 6 : len = s1len; /* nothing to pad with, so don't pad */
278 :
279 44 : bytelen = pg_database_encoding_max_length() * len;
280 :
281 : /* Check for integer overflow */
282 44 : if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
283 0 : ereport(ERROR,
284 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
285 : errmsg("requested length too large")));
286 :
287 44 : ret = (text *) palloc(VARHDRSZ + bytelen);
288 44 : m = len - s1len;
289 :
290 44 : ptr1 = VARDATA_ANY(string1);
291 44 : ptr_ret = VARDATA(ret);
292 :
293 108 : while (s1len--)
294 : {
295 64 : int mlen = pg_mblen(ptr1);
296 :
297 64 : memcpy(ptr_ret, ptr1, mlen);
298 64 : ptr_ret += mlen;
299 64 : ptr1 += mlen;
300 : }
301 :
302 44 : ptr2 = ptr2start = VARDATA_ANY(string2);
303 44 : ptr2end = ptr2 + s2len;
304 :
305 1920104 : while (m--)
306 : {
307 1920060 : int mlen = pg_mblen(ptr2);
308 :
309 1920060 : memcpy(ptr_ret, ptr2, mlen);
310 1920060 : ptr_ret += mlen;
311 1920060 : ptr2 += mlen;
312 1920060 : if (ptr2 == ptr2end) /* wrap around at end of s2 */
313 1920036 : ptr2 = ptr2start;
314 : }
315 :
316 44 : SET_VARSIZE(ret, ptr_ret - (char *) ret);
317 :
318 44 : PG_RETURN_TEXT_P(ret);
319 : }
320 :
321 :
322 : /********************************************************************
323 : *
324 : * btrim
325 : *
326 : * Syntax:
327 : *
328 : * text btrim(text string, text set)
329 : *
330 : * Purpose:
331 : *
332 : * Returns string with characters removed from the front and back
333 : * up to the first character not in set.
334 : *
335 : ********************************************************************/
336 :
337 : Datum
338 20 : btrim(PG_FUNCTION_ARGS)
339 : {
340 20 : text *string = PG_GETARG_TEXT_PP(0);
341 20 : text *set = PG_GETARG_TEXT_PP(1);
342 : text *ret;
343 :
344 40 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
345 40 : VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
346 : true, true);
347 :
348 20 : PG_RETURN_TEXT_P(ret);
349 : }
350 :
351 : /********************************************************************
352 : *
353 : * btrim1 --- btrim with set fixed as ' '
354 : *
355 : ********************************************************************/
356 :
357 : Datum
358 562 : btrim1(PG_FUNCTION_ARGS)
359 : {
360 562 : text *string = PG_GETARG_TEXT_PP(0);
361 : text *ret;
362 :
363 562 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
364 : " ", 1,
365 : true, true);
366 :
367 562 : PG_RETURN_TEXT_P(ret);
368 : }
369 :
370 : /*
371 : * Common implementation for btrim, ltrim, rtrim
372 : */
373 : static text *
374 34606 : dotrim(const char *string, int stringlen,
375 : const char *set, int setlen,
376 : bool doltrim, bool dortrim)
377 : {
378 : int i;
379 :
380 : /* Nothing to do if either string or set is empty */
381 34606 : if (stringlen > 0 && setlen > 0)
382 : {
383 34606 : if (pg_database_encoding_max_length() > 1)
384 : {
385 : /*
386 : * In the multibyte-encoding case, build arrays of pointers to
387 : * character starts, so that we can avoid inefficient checks in
388 : * the inner loops.
389 : */
390 : const char **stringchars;
391 : const char **setchars;
392 : int *stringmblen;
393 : int *setmblen;
394 : int stringnchars;
395 : int setnchars;
396 : int resultndx;
397 : int resultnchars;
398 : const char *p;
399 : int len;
400 : int mblen;
401 : const char *str_pos;
402 : int str_len;
403 :
404 34590 : stringchars = (const char **) palloc(stringlen * sizeof(char *));
405 34590 : stringmblen = (int *) palloc(stringlen * sizeof(int));
406 34590 : stringnchars = 0;
407 34590 : p = string;
408 34590 : len = stringlen;
409 301942 : while (len > 0)
410 : {
411 267352 : stringchars[stringnchars] = p;
412 267352 : stringmblen[stringnchars] = mblen = pg_mblen(p);
413 267352 : stringnchars++;
414 267352 : p += mblen;
415 267352 : len -= mblen;
416 : }
417 :
418 34590 : setchars = (const char **) palloc(setlen * sizeof(char *));
419 34590 : setmblen = (int *) palloc(setlen * sizeof(int));
420 34590 : setnchars = 0;
421 34590 : p = set;
422 34590 : len = setlen;
423 69210 : while (len > 0)
424 : {
425 34620 : setchars[setnchars] = p;
426 34620 : setmblen[setnchars] = mblen = pg_mblen(p);
427 34620 : setnchars++;
428 34620 : p += mblen;
429 34620 : len -= mblen;
430 : }
431 :
432 34590 : resultndx = 0; /* index in stringchars[] */
433 34590 : resultnchars = stringnchars;
434 :
435 34590 : if (doltrim)
436 : {
437 56588 : while (resultnchars > 0)
438 : {
439 56588 : str_pos = stringchars[resultndx];
440 56588 : str_len = stringmblen[resultndx];
441 85210 : for (i = 0; i < setnchars; i++)
442 : {
443 56708 : if (str_len == setmblen[i] &&
444 56708 : memcmp(str_pos, setchars[i], str_len) == 0)
445 28086 : break;
446 : }
447 56588 : if (i >= setnchars)
448 28502 : break; /* no match here */
449 28086 : string += str_len;
450 28086 : stringlen -= str_len;
451 28086 : resultndx++;
452 28086 : resultnchars--;
453 : }
454 : }
455 :
456 34590 : if (dortrim)
457 : {
458 74362 : while (resultnchars > 0)
459 : {
460 74362 : str_pos = stringchars[resultndx + resultnchars - 1];
461 74362 : str_len = stringmblen[resultndx + resultnchars - 1];
462 81050 : for (i = 0; i < setnchars; i++)
463 : {
464 74380 : if (str_len == setmblen[i] &&
465 74380 : memcmp(str_pos, setchars[i], str_len) == 0)
466 67692 : break;
467 : }
468 74362 : if (i >= setnchars)
469 6670 : break; /* no match here */
470 67692 : stringlen -= str_len;
471 67692 : resultnchars--;
472 : }
473 : }
474 :
475 34590 : pfree(stringchars);
476 34590 : pfree(stringmblen);
477 34590 : pfree(setchars);
478 34590 : pfree(setmblen);
479 : }
480 : else
481 : {
482 : /*
483 : * In the single-byte-encoding case, we don't need such overhead.
484 : */
485 16 : if (doltrim)
486 : {
487 0 : while (stringlen > 0)
488 : {
489 0 : char str_ch = *string;
490 :
491 0 : for (i = 0; i < setlen; i++)
492 : {
493 0 : if (str_ch == set[i])
494 0 : break;
495 : }
496 0 : if (i >= setlen)
497 0 : break; /* no match here */
498 0 : string++;
499 0 : stringlen--;
500 : }
501 : }
502 :
503 16 : if (dortrim)
504 : {
505 32 : while (stringlen > 0)
506 : {
507 32 : char str_ch = string[stringlen - 1];
508 :
509 48 : for (i = 0; i < setlen; i++)
510 : {
511 32 : if (str_ch == set[i])
512 16 : break;
513 : }
514 32 : if (i >= setlen)
515 16 : break; /* no match here */
516 16 : stringlen--;
517 : }
518 : }
519 : }
520 : }
521 :
522 : /* Return selected portion of string */
523 34606 : return cstring_to_text_with_len(string, stringlen);
524 : }
525 :
526 : /*
527 : * Common implementation for bytea versions of btrim, ltrim, rtrim
528 : */
529 : bytea *
530 36 : dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
531 : {
532 : bytea *ret;
533 : char *ptr,
534 : *end,
535 : *ptr2,
536 : *ptr2start,
537 : *end2;
538 : int m,
539 : stringlen,
540 : setlen;
541 :
542 36 : stringlen = VARSIZE_ANY_EXHDR(string);
543 36 : setlen = VARSIZE_ANY_EXHDR(set);
544 :
545 36 : if (stringlen <= 0 || setlen <= 0)
546 12 : return string;
547 :
548 24 : m = stringlen;
549 24 : ptr = VARDATA_ANY(string);
550 24 : end = ptr + stringlen - 1;
551 24 : ptr2start = VARDATA_ANY(set);
552 24 : end2 = ptr2start + setlen - 1;
553 :
554 24 : if (doltrim)
555 : {
556 36 : while (m > 0)
557 : {
558 36 : ptr2 = ptr2start;
559 54 : while (ptr2 <= end2)
560 : {
561 36 : if (*ptr == *ptr2)
562 18 : break;
563 18 : ++ptr2;
564 : }
565 36 : if (ptr2 > end2)
566 18 : break;
567 18 : ptr++;
568 18 : m--;
569 : }
570 : }
571 :
572 24 : if (dortrim)
573 : {
574 36 : while (m > 0)
575 : {
576 36 : ptr2 = ptr2start;
577 54 : while (ptr2 <= end2)
578 : {
579 36 : if (*end == *ptr2)
580 18 : break;
581 18 : ++ptr2;
582 : }
583 36 : if (ptr2 > end2)
584 18 : break;
585 18 : end--;
586 18 : m--;
587 : }
588 : }
589 :
590 24 : ret = (bytea *) palloc(VARHDRSZ + m);
591 24 : SET_VARSIZE(ret, VARHDRSZ + m);
592 24 : memcpy(VARDATA(ret), ptr, m);
593 24 : return ret;
594 : }
595 :
596 : /********************************************************************
597 : *
598 : * byteatrim
599 : *
600 : * Syntax:
601 : *
602 : * bytea byteatrim(bytea string, bytea set)
603 : *
604 : * Purpose:
605 : *
606 : * Returns string with characters removed from the front and back
607 : * up to the first character not in set.
608 : *
609 : * Cloned from btrim and modified as required.
610 : ********************************************************************/
611 :
612 : Datum
613 24 : byteatrim(PG_FUNCTION_ARGS)
614 : {
615 24 : bytea *string = PG_GETARG_BYTEA_PP(0);
616 24 : bytea *set = PG_GETARG_BYTEA_PP(1);
617 : bytea *ret;
618 :
619 24 : ret = dobyteatrim(string, set, true, true);
620 :
621 24 : PG_RETURN_BYTEA_P(ret);
622 : }
623 :
624 : /********************************************************************
625 : *
626 : * bytealtrim
627 : *
628 : * Syntax:
629 : *
630 : * bytea bytealtrim(bytea string, bytea set)
631 : *
632 : * Purpose:
633 : *
634 : * Returns string with initial characters removed up to the first
635 : * character not in set.
636 : *
637 : ********************************************************************/
638 :
639 : Datum
640 6 : bytealtrim(PG_FUNCTION_ARGS)
641 : {
642 6 : bytea *string = PG_GETARG_BYTEA_PP(0);
643 6 : bytea *set = PG_GETARG_BYTEA_PP(1);
644 : bytea *ret;
645 :
646 6 : ret = dobyteatrim(string, set, true, false);
647 :
648 6 : PG_RETURN_BYTEA_P(ret);
649 : }
650 :
651 : /********************************************************************
652 : *
653 : * byteartrim
654 : *
655 : * Syntax:
656 : *
657 : * bytea byteartrim(bytea string, bytea set)
658 : *
659 : * Purpose:
660 : *
661 : * Returns string with final characters removed after the last
662 : * character not in set.
663 : *
664 : ********************************************************************/
665 :
666 : Datum
667 6 : byteartrim(PG_FUNCTION_ARGS)
668 : {
669 6 : bytea *string = PG_GETARG_BYTEA_PP(0);
670 6 : bytea *set = PG_GETARG_BYTEA_PP(1);
671 : bytea *ret;
672 :
673 6 : ret = dobyteatrim(string, set, false, true);
674 :
675 6 : PG_RETURN_BYTEA_P(ret);
676 : }
677 :
678 : /********************************************************************
679 : *
680 : * ltrim
681 : *
682 : * Syntax:
683 : *
684 : * text ltrim(text string, text set)
685 : *
686 : * Purpose:
687 : *
688 : * Returns string with initial characters removed up to the first
689 : * character not in set.
690 : *
691 : ********************************************************************/
692 :
693 : Datum
694 27912 : ltrim(PG_FUNCTION_ARGS)
695 : {
696 27912 : text *string = PG_GETARG_TEXT_PP(0);
697 27912 : text *set = PG_GETARG_TEXT_PP(1);
698 : text *ret;
699 :
700 55824 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
701 55824 : VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
702 : true, false);
703 :
704 27912 : PG_RETURN_TEXT_P(ret);
705 : }
706 :
707 : /********************************************************************
708 : *
709 : * ltrim1 --- ltrim with set fixed as ' '
710 : *
711 : ********************************************************************/
712 :
713 : Datum
714 8 : ltrim1(PG_FUNCTION_ARGS)
715 : {
716 8 : text *string = PG_GETARG_TEXT_PP(0);
717 : text *ret;
718 :
719 8 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
720 : " ", 1,
721 : true, false);
722 :
723 8 : PG_RETURN_TEXT_P(ret);
724 : }
725 :
726 : /********************************************************************
727 : *
728 : * rtrim
729 : *
730 : * Syntax:
731 : *
732 : * text rtrim(text string, text set)
733 : *
734 : * Purpose:
735 : *
736 : * Returns string with final characters removed after the last
737 : * character not in set.
738 : *
739 : ********************************************************************/
740 :
741 : Datum
742 634 : rtrim(PG_FUNCTION_ARGS)
743 : {
744 634 : text *string = PG_GETARG_TEXT_PP(0);
745 634 : text *set = PG_GETARG_TEXT_PP(1);
746 : text *ret;
747 :
748 1268 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
749 1268 : VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
750 : false, true);
751 :
752 634 : PG_RETURN_TEXT_P(ret);
753 : }
754 :
755 : /********************************************************************
756 : *
757 : * rtrim1 --- rtrim with set fixed as ' '
758 : *
759 : ********************************************************************/
760 :
761 : Datum
762 5470 : rtrim1(PG_FUNCTION_ARGS)
763 : {
764 5470 : text *string = PG_GETARG_TEXT_PP(0);
765 : text *ret;
766 :
767 5470 : ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
768 : " ", 1,
769 : false, true);
770 :
771 5470 : PG_RETURN_TEXT_P(ret);
772 : }
773 :
774 :
775 : /********************************************************************
776 : *
777 : * translate
778 : *
779 : * Syntax:
780 : *
781 : * text translate(text string, text from, text to)
782 : *
783 : * Purpose:
784 : *
785 : * Returns string after replacing all occurrences of characters in from
786 : * with the corresponding character in to. If from is longer than to,
787 : * occurrences of the extra characters in from are deleted.
788 : * Improved by Edwin Ramirez <ramirez@doc.mssm.edu>.
789 : *
790 : ********************************************************************/
791 :
792 : Datum
793 28 : translate(PG_FUNCTION_ARGS)
794 : {
795 28 : text *string = PG_GETARG_TEXT_PP(0);
796 28 : text *from = PG_GETARG_TEXT_PP(1);
797 28 : text *to = PG_GETARG_TEXT_PP(2);
798 : text *result;
799 : char *from_ptr,
800 : *to_ptr;
801 : char *source,
802 : *target;
803 : int m,
804 : fromlen,
805 : tolen,
806 : retlen,
807 : i;
808 : int worst_len;
809 : int len;
810 : int source_len;
811 : int from_index;
812 :
813 28 : m = VARSIZE_ANY_EXHDR(string);
814 28 : if (m <= 0)
815 6 : PG_RETURN_TEXT_P(string);
816 22 : source = VARDATA_ANY(string);
817 :
818 22 : fromlen = VARSIZE_ANY_EXHDR(from);
819 22 : from_ptr = VARDATA_ANY(from);
820 22 : tolen = VARSIZE_ANY_EXHDR(to);
821 22 : to_ptr = VARDATA_ANY(to);
822 :
823 : /*
824 : * The worst-case expansion is to substitute a max-length character for a
825 : * single-byte character at each position of the string.
826 : */
827 22 : worst_len = pg_database_encoding_max_length() * m;
828 :
829 : /* check for integer overflow */
830 22 : if (worst_len / pg_database_encoding_max_length() != m)
831 0 : ereport(ERROR,
832 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
833 : errmsg("requested length too large")));
834 :
835 22 : result = (text *) palloc(worst_len + VARHDRSZ);
836 22 : target = VARDATA(result);
837 22 : retlen = 0;
838 :
839 244 : while (m > 0)
840 : {
841 222 : source_len = pg_mblen(source);
842 222 : from_index = 0;
843 :
844 600 : for (i = 0; i < fromlen; i += len)
845 : {
846 422 : len = pg_mblen(&from_ptr[i]);
847 422 : if (len == source_len &&
848 422 : memcmp(source, &from_ptr[i], len) == 0)
849 44 : break;
850 :
851 378 : from_index++;
852 : }
853 222 : if (i < fromlen)
854 : {
855 : /* substitute */
856 44 : char *p = to_ptr;
857 :
858 66 : for (i = 0; i < from_index; i++)
859 : {
860 22 : p += pg_mblen(p);
861 22 : if (p >= (to_ptr + tolen))
862 0 : break;
863 : }
864 44 : if (p < (to_ptr + tolen))
865 : {
866 44 : len = pg_mblen(p);
867 44 : memcpy(target, p, len);
868 44 : target += len;
869 44 : retlen += len;
870 : }
871 : }
872 : else
873 : {
874 : /* no match, so copy */
875 178 : memcpy(target, source, source_len);
876 178 : target += source_len;
877 178 : retlen += source_len;
878 : }
879 :
880 222 : source += source_len;
881 222 : m -= source_len;
882 : }
883 :
884 22 : SET_VARSIZE(result, retlen + VARHDRSZ);
885 :
886 : /*
887 : * The function result is probably much bigger than needed, if we're using
888 : * a multibyte encoding, but it's not worth reallocating it; the result
889 : * probably won't live long anyway.
890 : */
891 :
892 22 : PG_RETURN_TEXT_P(result);
893 : }
894 :
895 : /********************************************************************
896 : *
897 : * ascii
898 : *
899 : * Syntax:
900 : *
901 : * int ascii(text string)
902 : *
903 : * Purpose:
904 : *
905 : * Returns the decimal representation of the first character from
906 : * string.
907 : * If the string is empty we return 0.
908 : * If the database encoding is UTF8, we return the Unicode codepoint.
909 : * If the database encoding is any other multi-byte encoding, we
910 : * return the value of the first byte if it is an ASCII character
911 : * (range 1 .. 127), or raise an error.
912 : * For all other encodings we return the value of the first byte,
913 : * (range 1..255).
914 : *
915 : ********************************************************************/
916 :
917 : Datum
918 58 : ascii(PG_FUNCTION_ARGS)
919 : {
920 58 : text *string = PG_GETARG_TEXT_PP(0);
921 58 : int encoding = GetDatabaseEncoding();
922 : unsigned char *data;
923 :
924 58 : if (VARSIZE_ANY_EXHDR(string) <= 0)
925 6 : PG_RETURN_INT32(0);
926 :
927 52 : data = (unsigned char *) VARDATA_ANY(string);
928 :
929 52 : if (encoding == PG_UTF8 && *data > 127)
930 : {
931 : /* return the code point for Unicode */
932 :
933 0 : int result = 0,
934 0 : tbytes = 0,
935 : i;
936 :
937 0 : if (*data >= 0xF0)
938 : {
939 0 : result = *data & 0x07;
940 0 : tbytes = 3;
941 : }
942 0 : else if (*data >= 0xE0)
943 : {
944 0 : result = *data & 0x0F;
945 0 : tbytes = 2;
946 : }
947 : else
948 : {
949 : Assert(*data > 0xC0);
950 0 : result = *data & 0x1f;
951 0 : tbytes = 1;
952 : }
953 :
954 : Assert(tbytes > 0);
955 :
956 0 : for (i = 1; i <= tbytes; i++)
957 : {
958 : Assert((data[i] & 0xC0) == 0x80);
959 0 : result = (result << 6) + (data[i] & 0x3f);
960 : }
961 :
962 0 : PG_RETURN_INT32(result);
963 : }
964 : else
965 : {
966 52 : if (pg_encoding_max_length(encoding) > 1 && *data > 127)
967 0 : ereport(ERROR,
968 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
969 : errmsg("requested character too large")));
970 :
971 :
972 52 : PG_RETURN_INT32((int32) *data);
973 : }
974 : }
975 :
976 : /********************************************************************
977 : *
978 : * chr
979 : *
980 : * Syntax:
981 : *
982 : * text chr(int val)
983 : *
984 : * Purpose:
985 : *
986 : * Returns the character having the binary equivalent to val.
987 : *
988 : * For UTF8 we treat the argument as a Unicode code point.
989 : * For other multi-byte encodings we raise an error for arguments
990 : * outside the strict ASCII range (1..127).
991 : *
992 : * It's important that we don't ever return a value that is not valid
993 : * in the database encoding, so that this doesn't become a way for
994 : * invalid data to enter the database.
995 : *
996 : ********************************************************************/
997 :
998 : Datum
999 544964 : chr (PG_FUNCTION_ARGS)
1000 : {
1001 544964 : int32 arg = PG_GETARG_INT32(0);
1002 : uint32 cvalue;
1003 : text *result;
1004 544964 : int encoding = GetDatabaseEncoding();
1005 :
1006 : /*
1007 : * Error out on arguments that make no sense or that we can't validly
1008 : * represent in the encoding.
1009 : */
1010 544964 : if (arg < 0)
1011 0 : ereport(ERROR,
1012 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1013 : errmsg("character number must be positive")));
1014 544964 : else if (arg == 0)
1015 6 : ereport(ERROR,
1016 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1017 : errmsg("null character not permitted")));
1018 :
1019 544958 : cvalue = arg;
1020 :
1021 544958 : if (encoding == PG_UTF8 && cvalue > 127)
1022 0 : {
1023 : /* for Unicode we treat the argument as a code point */
1024 : int bytes;
1025 : unsigned char *wch;
1026 :
1027 : /*
1028 : * We only allow valid Unicode code points; per RFC3629 that stops at
1029 : * U+10FFFF, even though 4-byte UTF8 sequences can hold values up to
1030 : * U+1FFFFF.
1031 : */
1032 0 : if (cvalue > 0x0010ffff)
1033 0 : ereport(ERROR,
1034 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1035 : errmsg("requested character too large for encoding: %u",
1036 : cvalue)));
1037 :
1038 0 : if (cvalue > 0xffff)
1039 0 : bytes = 4;
1040 0 : else if (cvalue > 0x07ff)
1041 0 : bytes = 3;
1042 : else
1043 0 : bytes = 2;
1044 :
1045 0 : result = (text *) palloc(VARHDRSZ + bytes);
1046 0 : SET_VARSIZE(result, VARHDRSZ + bytes);
1047 0 : wch = (unsigned char *) VARDATA(result);
1048 :
1049 0 : if (bytes == 2)
1050 : {
1051 0 : wch[0] = 0xC0 | ((cvalue >> 6) & 0x1F);
1052 0 : wch[1] = 0x80 | (cvalue & 0x3F);
1053 : }
1054 0 : else if (bytes == 3)
1055 : {
1056 0 : wch[0] = 0xE0 | ((cvalue >> 12) & 0x0F);
1057 0 : wch[1] = 0x80 | ((cvalue >> 6) & 0x3F);
1058 0 : wch[2] = 0x80 | (cvalue & 0x3F);
1059 : }
1060 : else
1061 : {
1062 0 : wch[0] = 0xF0 | ((cvalue >> 18) & 0x07);
1063 0 : wch[1] = 0x80 | ((cvalue >> 12) & 0x3F);
1064 0 : wch[2] = 0x80 | ((cvalue >> 6) & 0x3F);
1065 0 : wch[3] = 0x80 | (cvalue & 0x3F);
1066 : }
1067 :
1068 : /*
1069 : * The preceding range check isn't sufficient, because UTF8 excludes
1070 : * Unicode "surrogate pair" codes. Make sure what we created is valid
1071 : * UTF8.
1072 : */
1073 0 : if (!pg_utf8_islegal(wch, bytes))
1074 0 : ereport(ERROR,
1075 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1076 : errmsg("requested character not valid for encoding: %u",
1077 : cvalue)));
1078 : }
1079 : else
1080 : {
1081 : bool is_mb;
1082 :
1083 544958 : is_mb = pg_encoding_max_length(encoding) > 1;
1084 :
1085 544958 : if ((is_mb && (cvalue > 127)) || (!is_mb && (cvalue > 255)))
1086 0 : ereport(ERROR,
1087 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1088 : errmsg("requested character too large for encoding: %u",
1089 : cvalue)));
1090 :
1091 544958 : result = (text *) palloc(VARHDRSZ + 1);
1092 544958 : SET_VARSIZE(result, VARHDRSZ + 1);
1093 544958 : *VARDATA(result) = (char) cvalue;
1094 : }
1095 :
1096 544958 : PG_RETURN_TEXT_P(result);
1097 : }
1098 :
1099 : /********************************************************************
1100 : *
1101 : * repeat
1102 : *
1103 : * Syntax:
1104 : *
1105 : * text repeat(text string, int val)
1106 : *
1107 : * Purpose:
1108 : *
1109 : * Repeat string by val.
1110 : *
1111 : ********************************************************************/
1112 :
1113 : Datum
1114 43156 : repeat(PG_FUNCTION_ARGS)
1115 : {
1116 43156 : text *string = PG_GETARG_TEXT_PP(0);
1117 43156 : int32 count = PG_GETARG_INT32(1);
1118 : text *result;
1119 : int slen,
1120 : tlen;
1121 : int i;
1122 : char *cp,
1123 : *sp;
1124 :
1125 43156 : if (count < 0)
1126 6 : count = 0;
1127 :
1128 43156 : slen = VARSIZE_ANY_EXHDR(string);
1129 :
1130 43156 : if (unlikely(pg_mul_s32_overflow(count, slen, &tlen)) ||
1131 43156 : unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)))
1132 0 : ereport(ERROR,
1133 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1134 : errmsg("requested length too large")));
1135 :
1136 43156 : result = (text *) palloc(tlen);
1137 :
1138 43156 : SET_VARSIZE(result, tlen);
1139 43156 : cp = VARDATA(result);
1140 43156 : sp = VARDATA_ANY(string);
1141 38473224 : for (i = 0; i < count; i++)
1142 : {
1143 38430068 : memcpy(cp, sp, slen);
1144 38430068 : cp += slen;
1145 38430068 : CHECK_FOR_INTERRUPTS();
1146 : }
1147 :
1148 43156 : PG_RETURN_TEXT_P(result);
1149 : }
|