]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/oracle_compat.c
5cfc215d71309f495bfc2b6de428ddc64af25e2f
[postgresql] / src / backend / utils / adt / oracle_compat.c
1 /*-------------------------------------------------------------------------
2  * oracle_compat.c
3  *      Oracle compatible functions.
4  *
5  * Copyright (c) 1996-2003, 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  *      $Header: /cvsroot/pgsql/src/backend/utils/adt/oracle_compat.c,v 1.45 2003/07/27 03:16:20 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include <ctype.h>
19
20 #include "utils/builtins.h"
21 #include "mb/pg_wchar.h"
22
23
24 static text *dotrim(const char *string, int stringlen,
25                                         const char *set, int setlen,
26                                         bool doltrim, bool dortrim);
27
28
29 /********************************************************************
30  *
31  * lower
32  *
33  * Syntax:
34  *
35  *       text lower(text string)
36  *
37  * Purpose:
38  *
39  *       Returns string, with all letters forced to lowercase.
40  *
41  ********************************************************************/
42
43 Datum
44 lower(PG_FUNCTION_ARGS)
45 {
46         text       *string = PG_GETARG_TEXT_P_COPY(0);
47         char       *ptr;
48         int                     m;
49
50         /* Since we copied the string, we can scribble directly on the value */
51         ptr = VARDATA(string);
52         m = VARSIZE(string) - VARHDRSZ;
53
54         while (m-- > 0)
55         {
56                 *ptr = tolower((unsigned char) *ptr);
57                 ptr++;
58         }
59
60         PG_RETURN_TEXT_P(string);
61 }
62
63
64 /********************************************************************
65  *
66  * upper
67  *
68  * Syntax:
69  *
70  *       text upper(text string)
71  *
72  * Purpose:
73  *
74  *       Returns string, with all letters forced to uppercase.
75  *
76  ********************************************************************/
77
78 Datum
79 upper(PG_FUNCTION_ARGS)
80 {
81         text       *string = PG_GETARG_TEXT_P_COPY(0);
82         char       *ptr;
83         int                     m;
84
85         /* Since we copied the string, we can scribble directly on the value */
86         ptr = VARDATA(string);
87         m = VARSIZE(string) - VARHDRSZ;
88
89         while (m-- > 0)
90         {
91                 *ptr = toupper((unsigned char) *ptr);
92                 ptr++;
93         }
94
95         PG_RETURN_TEXT_P(string);
96 }
97
98
99 /********************************************************************
100  *
101  * initcap
102  *
103  * Syntax:
104  *
105  *       text initcap(text string)
106  *
107  * Purpose:
108  *
109  *       Returns string, with first letter of each word in uppercase,
110  *       all other letters in lowercase. A word is delimited by white
111  *       space.
112  *
113  ********************************************************************/
114
115 Datum
116 initcap(PG_FUNCTION_ARGS)
117 {
118         text       *string = PG_GETARG_TEXT_P_COPY(0);
119         char       *ptr;
120         int                     m;
121
122         /* Since we copied the string, we can scribble directly on the value */
123         ptr = VARDATA(string);
124         m = VARSIZE(string) - VARHDRSZ;
125
126         if (m > 0)
127         {
128                 *ptr = toupper((unsigned char) *ptr);
129                 ptr++;
130                 m--;
131         }
132
133         while (m-- > 0)
134         {
135                 /* Oracle capitalizes after all non-alphanumeric */
136                 if (!isalnum((unsigned char) ptr[-1]))
137                         *ptr = toupper((unsigned char) *ptr);
138                 else
139                         *ptr = tolower((unsigned char) *ptr);
140                 ptr++;
141         }
142
143         PG_RETURN_TEXT_P(string);
144 }
145
146
147 /********************************************************************
148  *
149  * lpad
150  *
151  * Syntax:
152  *
153  *       text lpad(text string1, int4 len, text string2)
154  *
155  * Purpose:
156  *
157  *       Returns string1, left-padded to length len with the sequence of
158  *       characters in string2.  If len is less than the length of string1,
159  *       instead truncate (on the right) to len.
160  *
161  ********************************************************************/
162
163 Datum
164 lpad(PG_FUNCTION_ARGS)
165 {
166         text       *string1 = PG_GETARG_TEXT_P(0);
167         int32           len = PG_GETARG_INT32(1);
168         text       *string2 = PG_GETARG_TEXT_P(2);
169         text       *ret;
170         char       *ptr1,
171                            *ptr2,
172                            *ptr2end,
173                            *ptr_ret;
174         int                     m,
175                                 s1len,
176                                 s2len;
177
178         int                     bytelen;
179
180         /* Negative len is silently taken as zero */
181         if (len < 0)
182                 len = 0;
183
184         s1len = VARSIZE(string1) - VARHDRSZ;
185         if (s1len < 0)
186                 s1len = 0;                              /* shouldn't happen */
187
188         s2len = VARSIZE(string2) - VARHDRSZ;
189         if (s2len < 0)
190                 s2len = 0;                              /* shouldn't happen */
191
192         s1len = pg_mbstrlen_with_len(VARDATA(string1), s1len);
193
194         if (s1len > len)
195                 s1len = len;                    /* truncate string1 to len chars */
196
197         if (s2len <= 0)
198                 len = s1len;                    /* nothing to pad with, so don't pad */
199
200         bytelen = pg_database_encoding_max_length() * len;
201
202         /* check for integer overflow */
203         if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
204                 elog(ERROR, "Requested length too large");
205
206         ret = (text *) palloc(VARHDRSZ + bytelen);
207
208         m = len - s1len;
209
210         ptr2 = VARDATA(string2);
211         ptr2end = ptr2 + s2len;
212         ptr_ret = VARDATA(ret);
213
214         while (m--)
215         {
216                 int                     mlen = pg_mblen(ptr2);
217
218                 memcpy(ptr_ret, ptr2, mlen);
219                 ptr_ret += mlen;
220                 ptr2 += mlen;
221                 if (ptr2 == ptr2end)    /* wrap around at end of s2 */
222                         ptr2 = VARDATA(string2);
223         }
224
225         ptr1 = VARDATA(string1);
226
227         while (s1len--)
228         {
229                 int                     mlen = pg_mblen(ptr1);
230
231                 memcpy(ptr_ret, ptr1, mlen);
232                 ptr_ret += mlen;
233                 ptr1 += mlen;
234         }
235
236         VARATT_SIZEP(ret) = ptr_ret - (char *) ret;
237
238         PG_RETURN_TEXT_P(ret);
239 }
240
241
242 /********************************************************************
243  *
244  * rpad
245  *
246  * Syntax:
247  *
248  *       text rpad(text string1, int4 len, text string2)
249  *
250  * Purpose:
251  *
252  *       Returns string1, right-padded to length len with the sequence of
253  *       characters in string2.  If len is less than the length of string1,
254  *       instead truncate (on the right) to len.
255  *
256  ********************************************************************/
257
258 Datum
259 rpad(PG_FUNCTION_ARGS)
260 {
261         text       *string1 = PG_GETARG_TEXT_P(0);
262         int32           len = PG_GETARG_INT32(1);
263         text       *string2 = PG_GETARG_TEXT_P(2);
264         text       *ret;
265         char       *ptr1,
266                            *ptr2,
267                            *ptr2end,
268                            *ptr_ret;
269         int                     m,
270                                 s1len,
271                                 s2len;
272
273         int                     bytelen;
274
275         /* Negative len is silently taken as zero */
276         if (len < 0)
277                 len = 0;
278
279         s1len = VARSIZE(string1) - VARHDRSZ;
280         if (s1len < 0)
281                 s1len = 0;                              /* shouldn't happen */
282
283         s2len = VARSIZE(string2) - VARHDRSZ;
284         if (s2len < 0)
285                 s2len = 0;                              /* shouldn't happen */
286
287         s1len = pg_mbstrlen_with_len(VARDATA(string1), s1len);
288
289         if (s1len > len)
290                 s1len = len;                    /* truncate string1 to len chars */
291
292         if (s2len <= 0)
293                 len = s1len;                    /* nothing to pad with, so don't pad */
294
295         bytelen = pg_database_encoding_max_length() * len;
296
297         /* Check for integer overflow */
298         if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
299                 elog(ERROR, "Requested length too large");
300
301         ret = (text *) palloc(VARHDRSZ + bytelen);
302         m = len - s1len;
303
304         ptr1 = VARDATA(string1);
305         ptr_ret = VARDATA(ret);
306
307         while (s1len--)
308         {
309                 int                     mlen = pg_mblen(ptr1);
310
311                 memcpy(ptr_ret, ptr1, mlen);
312                 ptr_ret += mlen;
313                 ptr1 += mlen;
314         }
315
316         ptr2 = VARDATA(string2);
317         ptr2end = ptr2 + s2len;
318
319         while (m--)
320         {
321                 int                     mlen = pg_mblen(ptr2);
322
323                 memcpy(ptr_ret, ptr2, mlen);
324                 ptr_ret += mlen;
325                 ptr2 += mlen;
326                 if (ptr2 == ptr2end)    /* wrap around at end of s2 */
327                         ptr2 = VARDATA(string2);
328         }
329
330         VARATT_SIZEP(ret) = ptr_ret - (char *) ret;
331
332         PG_RETURN_TEXT_P(ret);
333 }
334
335
336 /********************************************************************
337  *
338  * btrim
339  *
340  * Syntax:
341  *
342  *       text btrim(text string, text set)
343  *
344  * Purpose:
345  *
346  *       Returns string with characters removed from the front and back
347  *       up to the first character not in set.
348  *
349  ********************************************************************/
350
351 Datum
352 btrim(PG_FUNCTION_ARGS)
353 {
354         text       *string = PG_GETARG_TEXT_P(0);
355         text       *set = PG_GETARG_TEXT_P(1);
356         text       *ret;
357
358         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
359                                  VARDATA(set), VARSIZE(set) - VARHDRSZ,
360                                  true, true);
361
362         PG_RETURN_TEXT_P(ret);
363 }
364
365 /********************************************************************
366  *
367  * btrim1 --- btrim with set fixed as ' '
368  *
369  ********************************************************************/
370
371 Datum
372 btrim1(PG_FUNCTION_ARGS)
373 {
374         text       *string = PG_GETARG_TEXT_P(0);
375         text       *ret;
376
377         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
378                                  " ", 1,
379                                  true, true);
380
381         PG_RETURN_TEXT_P(ret);
382 }
383
384 /*
385  * Common implementation for btrim, ltrim, rtrim
386  */
387 static text *
388 dotrim(const char *string, int stringlen,
389            const char *set, int setlen,
390            bool doltrim, bool dortrim)
391 {
392         text       *result;
393         int                     i;
394
395         /* Nothing to do if either string or set is empty */
396         if (stringlen > 0 && setlen > 0)
397         {
398                 if (pg_database_encoding_max_length() > 1)
399                 {
400                         /*
401                          * In the multibyte-encoding case, build arrays of pointers to
402                          * character starts, so that we can avoid inefficient checks in
403                          * the inner loops.
404                          */
405                         const char **stringchars;
406                         const char **setchars;
407                         int                *stringmblen;
408                         int                *setmblen;
409                         int                     stringnchars;
410                         int                     setnchars;
411                         int                     resultndx;
412                         int                     resultnchars;
413                         const char *p;
414                         int                     len;
415                         int                     mblen;
416                         const char *str_pos;
417                         int                     str_len;
418
419                         stringchars = (const char **) palloc(stringlen * sizeof(char *));
420                         stringmblen = (int *) palloc(stringlen * sizeof(int));
421                         stringnchars = 0;
422                         p = string;
423                         len = stringlen;
424                         while (len > 0)
425                         {
426                                 stringchars[stringnchars] = p;
427                                 stringmblen[stringnchars] = mblen = pg_mblen(p);
428                                 stringnchars++;
429                                 p += mblen;
430                                 len -= mblen;
431                         }
432
433                         setchars = (const char **) palloc(setlen * sizeof(char *));
434                         setmblen = (int *) palloc(setlen * sizeof(int));
435                         setnchars = 0;
436                         p = set;
437                         len = setlen;
438                         while (len > 0)
439                         {
440                                 setchars[setnchars] = p;
441                                 setmblen[setnchars] = mblen = pg_mblen(p);
442                                 setnchars++;
443                                 p += mblen;
444                                 len -= mblen;
445                         }
446
447                         resultndx = 0;          /* index in stringchars[] */
448                         resultnchars = stringnchars;
449
450                         if (doltrim)
451                         {
452                                 while (resultnchars > 0)
453                                 {
454                                         str_pos = stringchars[resultndx];
455                                         str_len = stringmblen[resultndx];
456                                         for (i = 0; i < setnchars; i++)
457                                         {
458                                                 if (str_len == setmblen[i] &&
459                                                         memcmp(str_pos, setchars[i], str_len) == 0)
460                                                         break;
461                                         }
462                                         if (i >= setnchars)
463                                                 break;  /* no match here */
464                                         string += str_len;
465                                         stringlen -= str_len;
466                                         resultndx++;
467                                         resultnchars--;
468                                 }
469                         }
470
471                         if (dortrim)
472                         {
473                                 while (resultnchars > 0)
474                                 {
475                                         str_pos = stringchars[resultndx + resultnchars - 1];
476                                         str_len = stringmblen[resultndx + resultnchars - 1];
477                                         for (i = 0; i < setnchars; i++)
478                                         {
479                                                 if (str_len == setmblen[i] &&
480                                                         memcmp(str_pos, setchars[i], str_len) == 0)
481                                                         break;
482                                         }
483                                         if (i >= setnchars)
484                                                 break;  /* no match here */
485                                         stringlen -= str_len;
486                                         resultnchars--;
487                                 }
488                         }
489
490                         pfree(stringchars);
491                         pfree(stringmblen);
492                         pfree(setchars);
493                         pfree(setmblen);
494                 }
495                 else
496                 {
497                         /*
498                          * In the single-byte-encoding case, we don't need such overhead.
499                          */
500                         if (doltrim)
501                         {
502                                 while (stringlen > 0)
503                                 {
504                                         char    str_ch = *string;
505
506                                         for (i = 0; i < setlen; i++)
507                                         {
508                                                 if (str_ch == set[i])
509                                                         break;
510                                         }
511                                         if (i >= setlen)
512                                                 break;  /* no match here */
513                                         string++;
514                                         stringlen--;
515                                 }
516                         }
517
518                         if (dortrim)
519                         {
520                                 while (stringlen > 0)
521                                 {
522                                         char    str_ch = string[stringlen - 1];
523
524                                         for (i = 0; i < setlen; i++)
525                                         {
526                                                 if (str_ch == set[i])
527                                                         break;
528                                         }
529                                         if (i >= setlen)
530                                                 break;  /* no match here */
531                                         stringlen--;
532                                 }
533                         }
534                 }
535         }
536
537         /* Return selected portion of string */
538         result = (text *) palloc(VARHDRSZ + stringlen);
539         VARATT_SIZEP(result) = VARHDRSZ + stringlen;
540         memcpy(VARDATA(result), string, stringlen);
541
542         return result;
543 }
544
545 /********************************************************************
546  *
547  * byteatrim
548  *
549  * Syntax:
550  *
551  *       bytea byteatrim(byta string, bytea set)
552  *
553  * Purpose:
554  *
555  *       Returns string with characters removed from the front and back
556  *       up to the first character not in set.
557  *
558  * Cloned from btrim and modified as required.
559  ********************************************************************/
560
561 Datum
562 byteatrim(PG_FUNCTION_ARGS)
563 {
564         bytea      *string = PG_GETARG_BYTEA_P(0);
565         bytea      *set = PG_GETARG_BYTEA_P(1);
566         bytea      *ret;
567         char       *ptr,
568                            *end,
569                            *ptr2,
570                            *end2;
571         int                     m;
572
573         if ((m = VARSIZE(string) - VARHDRSZ) <= 0 ||
574                 (VARSIZE(set) - VARHDRSZ) <= 0)
575                 PG_RETURN_BYTEA_P(string);
576
577         ptr = VARDATA(string);
578         end = VARDATA(string) + VARSIZE(string) - VARHDRSZ - 1;
579         end2 = VARDATA(set) + VARSIZE(set) - VARHDRSZ - 1;
580
581         while (m > 0)
582         {
583                 ptr2 = VARDATA(set);
584                 while (ptr2 <= end2)
585                 {
586                         if (*ptr == *ptr2)
587                                 break;
588                         ++ptr2;
589                 }
590                 if (ptr2 > end2)
591                         break;
592                 ptr++;
593                 m--;
594         }
595
596         while (m > 0)
597         {
598                 ptr2 = VARDATA(set);
599                 while (ptr2 <= end2)
600                 {
601                         if (*end == *ptr2)
602                                 break;
603                         ++ptr2;
604                 }
605                 if (ptr2 > end2)
606                         break;
607                 end--;
608                 m--;
609         }
610
611         ret = (bytea *) palloc(VARHDRSZ + m);
612         VARATT_SIZEP(ret) = VARHDRSZ + m;
613         memcpy(VARDATA(ret), ptr, m);
614
615         PG_RETURN_BYTEA_P(ret);
616 }
617
618 /********************************************************************
619  *
620  * ltrim
621  *
622  * Syntax:
623  *
624  *       text ltrim(text string, text set)
625  *
626  * Purpose:
627  *
628  *       Returns string with initial characters removed up to the first
629  *       character not in set.
630  *
631  ********************************************************************/
632
633 Datum
634 ltrim(PG_FUNCTION_ARGS)
635 {
636         text       *string = PG_GETARG_TEXT_P(0);
637         text       *set = PG_GETARG_TEXT_P(1);
638         text       *ret;
639
640         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
641                                  VARDATA(set), VARSIZE(set) - VARHDRSZ,
642                                  true, false);
643
644         PG_RETURN_TEXT_P(ret);
645 }
646
647 /********************************************************************
648  *
649  * ltrim1 --- ltrim with set fixed as ' '
650  *
651  ********************************************************************/
652
653 Datum
654 ltrim1(PG_FUNCTION_ARGS)
655 {
656         text       *string = PG_GETARG_TEXT_P(0);
657         text       *ret;
658
659         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
660                                  " ", 1,
661                                  true, false);
662
663         PG_RETURN_TEXT_P(ret);
664 }
665
666 /********************************************************************
667  *
668  * rtrim
669  *
670  * Syntax:
671  *
672  *       text rtrim(text string, text set)
673  *
674  * Purpose:
675  *
676  *       Returns string with final characters removed after the last
677  *       character not in set.
678  *
679  ********************************************************************/
680
681 Datum
682 rtrim(PG_FUNCTION_ARGS)
683 {
684         text       *string = PG_GETARG_TEXT_P(0);
685         text       *set = PG_GETARG_TEXT_P(1);
686         text       *ret;
687
688         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
689                                  VARDATA(set), VARSIZE(set) - VARHDRSZ,
690                                  false, true);
691
692         PG_RETURN_TEXT_P(ret);
693 }
694
695 /********************************************************************
696  *
697  * rtrim1 --- rtrim with set fixed as ' '
698  *
699  ********************************************************************/
700
701 Datum
702 rtrim1(PG_FUNCTION_ARGS)
703 {
704         text       *string = PG_GETARG_TEXT_P(0);
705         text       *ret;
706
707         ret = dotrim(VARDATA(string), VARSIZE(string) - VARHDRSZ,
708                                  " ", 1,
709                                  false, true);
710
711         PG_RETURN_TEXT_P(ret);
712 }
713
714
715 /********************************************************************
716  *
717  * translate
718  *
719  * Syntax:
720  *
721  *       text translate(text string, text from, text to)
722  *
723  * Purpose:
724  *
725  *       Returns string after replacing all occurrences of characters in from
726  *       with the corresponding character in to.  If from is longer than to,
727  *       occurrences of the extra characters in from are deleted.
728  *       Improved by Edwin Ramirez <ramirez@doc.mssm.edu>.
729  *
730  ********************************************************************/
731
732 Datum
733 translate(PG_FUNCTION_ARGS)
734 {
735         text       *string = PG_GETARG_TEXT_P(0);
736         text       *from = PG_GETARG_TEXT_P(1);
737         text       *to = PG_GETARG_TEXT_P(2);
738         text       *result;
739         char       *from_ptr,
740                            *to_ptr;
741         char       *source,
742                            *target;
743         int                     m,
744                                 fromlen,
745                                 tolen,
746                                 retlen,
747                                 i;
748
749         int                     str_len;
750         int                     estimate_len;
751         int                     len;
752         int                     source_len;
753         int                     from_index;
754
755         if ((m = VARSIZE(string) - VARHDRSZ) <= 0)
756                 PG_RETURN_TEXT_P(string);
757
758         fromlen = VARSIZE(from) - VARHDRSZ;
759         from_ptr = VARDATA(from);
760         tolen = VARSIZE(to) - VARHDRSZ;
761         to_ptr = VARDATA(to);
762
763         str_len = VARSIZE(string);
764         estimate_len = (tolen * 1.0 / fromlen + 0.5) * str_len;
765         estimate_len = estimate_len > str_len ? estimate_len : str_len;
766         result = (text *) palloc(estimate_len);
767
768         source = VARDATA(string);
769         target = VARDATA(result);
770         retlen = 0;
771
772         while (m > 0)
773         {
774                 source_len = pg_mblen(source);
775                 from_index = 0;
776
777                 for (i = 0; i < fromlen; i += len)
778                 {
779                         len = pg_mblen(&from_ptr[i]);
780                         if (len == source_len &&
781                                 memcmp(source, &from_ptr[i], len) == 0)
782                                 break;
783
784                         from_index++;
785                 }
786                 if (i < fromlen)
787                 {
788                         /* substitute */
789                         char       *p = to_ptr;
790
791                         for (i = 0; i < from_index; i++)
792                         {
793                                 p += pg_mblen(p);
794                                 if (p >= (to_ptr + tolen))
795                                         break;
796                         }
797                         if (p < (to_ptr + tolen))
798                         {
799                                 len = pg_mblen(p);
800                                 memcpy(target, p, len);
801                                 target += len;
802                                 retlen += len;
803                         }
804
805                 }
806                 else
807                 {
808                         /* no match, so copy */
809                         memcpy(target, source, source_len);
810                         target += source_len;
811                         retlen += source_len;
812                 }
813
814                 source += source_len;
815                 m -= source_len;
816         }
817
818         VARATT_SIZEP(result) = retlen + VARHDRSZ;
819
820         /*
821          * There may be some wasted space in the result if deletions occurred,
822          * but it's not worth reallocating it; the function result probably
823          * won't live long anyway.
824          */
825
826         PG_RETURN_TEXT_P(result);
827 }
828
829 /********************************************************************
830  *
831  * ascii
832  *
833  * Syntax:
834  *
835  *       int ascii(text string)
836  *
837  * Purpose:
838  *
839  *       Returns the decimal representation of the first character from
840  *       string.
841  *
842  ********************************************************************/
843
844 Datum
845 ascii(PG_FUNCTION_ARGS)
846 {
847         text       *string = PG_GETARG_TEXT_P(0);
848
849         if (VARSIZE(string) <= VARHDRSZ)
850                 PG_RETURN_INT32(0);
851
852         PG_RETURN_INT32((int32) *((unsigned char *) VARDATA(string)));
853 }
854
855 /********************************************************************
856  *
857  * chr
858  *
859  * Syntax:
860  *
861  *       text chr(int val)
862  *
863  * Purpose:
864  *
865  *      Returns the character having the binary equivalent to val
866  *
867  ********************************************************************/
868
869 Datum
870 chr(PG_FUNCTION_ARGS)
871 {
872         int32           cvalue = PG_GETARG_INT32(0);
873         text       *result;
874
875         result = (text *) palloc(VARHDRSZ + 1);
876         VARATT_SIZEP(result) = VARHDRSZ + 1;
877         *VARDATA(result) = (char) cvalue;
878
879         PG_RETURN_TEXT_P(result);
880 }
881
882 /********************************************************************
883  *
884  * repeat
885  *
886  * Syntax:
887  *
888  *       text repeat(text string, int val)
889  *
890  * Purpose:
891  *
892  *      Repeat string by val.
893  *
894  ********************************************************************/
895
896 Datum
897 repeat(PG_FUNCTION_ARGS)
898 {
899         text       *string = PG_GETARG_TEXT_P(0);
900         int32           count = PG_GETARG_INT32(1);
901         text       *result;
902         int                     slen,
903                                 tlen;
904         int                     i;
905         char       *cp;
906
907         if (count < 0)
908                 count = 0;
909
910         slen = (VARSIZE(string) - VARHDRSZ);
911         tlen = (VARHDRSZ + (count * slen));
912
913         /* Check for integer overflow */
914         if (slen != 0 && count != 0)
915         {
916                 int                     check = count * slen;
917                 int                     check2 = check + VARHDRSZ;
918
919                 if ((check / slen) != count || check2 <= check)
920                         elog(ERROR, "Requested buffer is too large.");
921         }
922
923         result = (text *) palloc(tlen);
924
925         VARATT_SIZEP(result) = tlen;
926         cp = VARDATA(result);
927         for (i = 0; i < count; i++)
928         {
929                 memcpy(cp, VARDATA(string), slen);
930                 cp += slen;
931         }
932
933         PG_RETURN_TEXT_P(result);
934 }